@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,759 +0,0 @@
1
- // In-memory exchange simulator.
2
- // Maintains wallet, positions, open orders, and trade history.
3
- // Emits 'stateChanged' event for persistence.
4
- //
5
- // Phase 9a: Supports realistic fills (book-aware VWAP, latency, maker/taker fees)
6
- // via SimulationConfig and OrderBookDepth.
7
- import { EventEmitter } from 'node:events';
8
- import { randomUUID } from 'node:crypto';
9
- import { logger } from '../logger.js';
10
- import { MAX_TRADE_HISTORY, DEFAULT_SIMULATION_CONFIG } from './types.js';
11
- import { fillMarketOrder, fillLimitOrder, parseSymbol } from './fill-engine.js';
12
- import { updateMfe } from '../mfe.js';
13
- import { computeInvalidationHit } from '../pinned-plan.js';
14
- const TAG = 'simulator';
15
- export class ExchangeSimulator extends EventEmitter {
16
- state;
17
- lastTicker = new Map();
18
- lastOrderBook = new Map();
19
- simulationConfig;
20
- /** Metadata for pending limit orders, keyed by order ID. Cleaned up on fill/cancel. */
21
- pendingOrderMetadata = new Map();
22
- // ---- Volatility caching (set by plugin after market structure fetch) ----
23
- cachedVolFactor = 1.0;
24
- baselineAtr = 0;
25
- atrSampleCount = 0;
26
- // ---- Startup trade lockout ----
27
- // Prevents stale agent sessions from executing trades during gateway restart.
28
- // Without this, the agent resumes mid-action and sells positions it doesn't remember.
29
- startupTime = Date.now();
30
- hadPositionsAtStartup;
31
- static STARTUP_LOCKOUT_MS = 15_000; // 15 seconds
32
- constructor(initialState, config) {
33
- super();
34
- this.state = initialState;
35
- this.simulationConfig = config ?? DEFAULT_SIMULATION_CONFIG;
36
- this.hadPositionsAtStartup = initialState.positions.length > 0;
37
- if (this.hadPositionsAtStartup) {
38
- logger.info(TAG, `Startup lockout ACTIVE for 15s — ${initialState.positions.length} existing position(s) protected`);
39
- }
40
- logger.info(TAG, `Simulator initialized: ${this.state.positions.length} positions, ${this.state.openOrders.length} open orders (realistic fills enabled)`);
41
- }
42
- // ---- Simulation config ----
43
- getSimulationConfig() {
44
- return this.simulationConfig;
45
- }
46
- // ---- Volatility factor accessors ----
47
- setVolFactor(factor) {
48
- this.cachedVolFactor = factor;
49
- }
50
- getVolFactor() {
51
- return this.cachedVolFactor;
52
- }
53
- /** Update baseline ATR using exponential moving average of samples. */
54
- updateBaselineAtr(atr14) {
55
- if (atr14 <= 0)
56
- return;
57
- this.atrSampleCount++;
58
- if (this.baselineAtr === 0) {
59
- this.baselineAtr = atr14;
60
- }
61
- else {
62
- // EMA with alpha = 2/(n+1), capped at 20 samples for stability
63
- const n = Math.min(this.atrSampleCount, 20);
64
- const alpha = 2 / (n + 1);
65
- this.baselineAtr = alpha * atr14 + (1 - alpha) * this.baselineAtr;
66
- }
67
- }
68
- getBaselineAtr() {
69
- return this.baselineAtr;
70
- }
71
- // ---- Session start NAV ----
72
- /**
73
- * Returns today's session-start NAV, lazily seeding it from current equity
74
- * if unset or stale (different UTC day). This is the plugin-side anchor used
75
- * by the pre-trade risk gate; a stale/missing anchor silently disables the
76
- * drawdown zones, so callers must never see a zero.
77
- */
78
- getSessionStartNav() {
79
- this.ensureSessionAnchor();
80
- return this.state.sessionStartNav ?? 0;
81
- }
82
- setSessionStartNav(nav) {
83
- const today = new Date().toISOString().slice(0, 10);
84
- if (this.state.sessionDate !== today || this.state.sessionStartNav === undefined) {
85
- this.state.sessionStartNav = nav;
86
- this.state.sessionDate = today;
87
- this.emitStateChanged();
88
- }
89
- }
90
- /** Seed sessionStartNav from current equity on the first check of each UTC day.
91
- * Safe to call repeatedly — no-op once the anchor matches today's date. */
92
- ensureSessionAnchor() {
93
- const today = new Date().toISOString().slice(0, 10);
94
- if (this.state.sessionDate === today && this.state.sessionStartNav !== undefined) {
95
- return;
96
- }
97
- const equity = this.computeEquity();
98
- this.state.sessionStartNav = equity;
99
- this.state.sessionDate = today;
100
- logger.info(TAG, `Session NAV anchored: $${equity.toFixed(2)} for ${today} (was: date=${this.state.sessionDate ?? 'none'}, nav=${this.state.sessionStartNav ?? 'none'})`);
101
- this.emitStateChanged();
102
- }
103
- /** Mark-to-market equity = walletTotal + for each open position, the
104
- * collateral locked at entry + current unrealized P&L. Paper simulator is
105
- * SPOT-collateral style: the full entry notional was deducted from wallet
106
- * on open and is released on close, so we must add it back here. */
107
- computeEquity() {
108
- const quote = this.getQuoteCurrency();
109
- const walletTotal = this.state.wallet[quote]?.total ?? 0;
110
- let positionEquity = 0;
111
- for (const pos of this.state.positions) {
112
- const ticker = this.lastTicker.get(pos.symbol);
113
- const mark = ticker?.last ?? pos.entryPrice;
114
- const entryNotional = pos.entryPrice * pos.quantity;
115
- const unrealized = pos.side === 'long'
116
- ? (mark - pos.entryPrice) * pos.quantity
117
- : (pos.entryPrice - mark) * pos.quantity;
118
- positionEquity += entryNotional + unrealized;
119
- }
120
- return walletTotal + positionEquity;
121
- }
122
- getQuoteCurrency() {
123
- return this.state.config?.quoteCurrency ?? 'USDT';
124
- }
125
- // ---- Read operations (for tools) ----
126
- getBalance() {
127
- const free = {};
128
- const used = {};
129
- const total = {};
130
- const result = { free, used, total };
131
- // Derivatives model: only quote currency (USDT) in wallet
132
- for (const [currency, bal] of Object.entries(this.state.wallet)) {
133
- free[currency] = bal.available;
134
- used[currency] = bal.locked;
135
- total[currency] = bal.total;
136
- result[currency] = {
137
- free: bal.available,
138
- used: bal.locked,
139
- total: bal.total,
140
- };
141
- }
142
- // All-time realized P&L from trade history (restart-proof).
143
- // Includes BOTH open-side and close-side fees — prior versions omitted the
144
- // open-side fee and under-reported fee drag by ~50%.
145
- let realizedPnlAllTime = 0;
146
- let totalRoundtripFees = 0;
147
- const today = new Date().toISOString().slice(0, 10);
148
- let realizedPnlToday = 0;
149
- for (const t of this.state.tradeHistory) {
150
- const roundtripFee = t.fee + (t.openFee ?? 0);
151
- const net = t.realizedPnl - roundtripFee;
152
- realizedPnlAllTime += net;
153
- totalRoundtripFees += roundtripFee;
154
- if (typeof t.closedAt === 'string' && t.closedAt.slice(0, 10) === today) {
155
- realizedPnlToday += net;
156
- }
157
- }
158
- const round4 = (n) => +n.toFixed(4);
159
- result.realizedPnlAllTime = round4(realizedPnlAllTime);
160
- result.realizedPnlToday = round4(realizedPnlToday);
161
- result.totalRoundtripFees = round4(totalRoundtripFees);
162
- // Session execution-quality costs (since boot) for the dashboard cost cells
163
- // (M1). These are the SAME fees/slippage already baked into equity/NAV —
164
- // surfaced so the "Fees" / "Avg Slip" breakdown cells don't read $0 while
165
- // the headline PnL clearly shows the drag.
166
- const execStats = this.state.executionStats;
167
- result.sessionFeesPaid = round4(execStats?.totalFeesPaid ?? 0);
168
- result.sessionAvgSlippageBps = round4(execStats?.avgSlippageBps ?? 0);
169
- // Equity = mark-to-market total account value. Use this (NOT walletTotal)
170
- // for heat / leverage / exposure math — walletTotal drops as notional is
171
- // locked into open positions, but that collateral is released on close.
172
- const equity = this.computeEquity();
173
- result.equity = round4(equity);
174
- // Session anchor (for drawdown zone calculation). Side-effect: seeds on
175
- // first read each UTC day if stale/missing.
176
- const sessionNav = this.getSessionStartNav();
177
- result.sessionStartNav = round4(sessionNav);
178
- return result;
179
- }
180
- getPositions(symbol) {
181
- const positions = symbol
182
- ? this.state.positions.filter(p => p.symbol === symbol)
183
- : this.state.positions;
184
- return positions.map(p => {
185
- const ticker = this.lastTicker.get(p.symbol);
186
- const markPrice = ticker?.last ?? p.entryPrice;
187
- const notional = p.quantity * markPrice;
188
- const pnlMultiplier = p.side === 'long' ? 1 : -1;
189
- const unrealizedPnl = (markPrice - p.entryPrice) * p.quantity * pnlMultiplier;
190
- const percentage = p.entryPrice > 0
191
- ? ((markPrice - p.entryPrice) / p.entryPrice) * 100 * pnlMultiplier
192
- : 0;
193
- return {
194
- symbol: p.symbol,
195
- side: p.side,
196
- contracts: p.quantity,
197
- contractSize: 1,
198
- entryPrice: p.entryPrice,
199
- markPrice,
200
- notional,
201
- unrealizedPnl,
202
- percentage,
203
- timestamp: Date.now(),
204
- datetime: new Date().toISOString(),
205
- // Surface entry metadata (if available)
206
- ...(p.metadata && {
207
- setupType: p.metadata.setupType,
208
- missionId: p.metadata.missionId,
209
- candidateId: p.metadata.candidateId,
210
- strategy: p.metadata.strategy,
211
- accruedFundingUsd: p.metadata.accruedFundingUsd,
212
- fundingThroughTime: p.metadata.fundingThroughTime,
213
- thesis: p.metadata.thesis,
214
- stopPrice: p.metadata.stopPrice,
215
- targetPrice: p.metadata.targetPrice,
216
- regime: p.metadata.regime,
217
- regimeConfidence: p.metadata.regimeConfidence,
218
- scorecardVerdict: p.metadata.scorecardVerdict,
219
- confluenceScore: p.metadata.confluenceScore,
220
- originalStopPrice: p.metadata.originalStopPrice,
221
- mfePeakPrice: p.metadata.mfePeakPrice,
222
- mfeR: p.metadata.mfeR,
223
- giveBackRatio: p.metadata.giveBackRatio,
224
- invalidationPrice: p.metadata.invalidationPrice,
225
- realizationRule: p.metadata.realizationRule,
226
- invalidationHit: computeInvalidationHit(p.side, markPrice, p.metadata.invalidationPrice),
227
- }),
228
- };
229
- });
230
- }
231
- getOpenOrders(symbol) {
232
- const orders = symbol
233
- ? this.state.openOrders.filter(o => o.symbol === symbol)
234
- : this.state.openOrders;
235
- return orders.map(o => this.toCcxtOrder(o));
236
- }
237
- /** Get cumulative execution quality stats. */
238
- getExecutionStats() {
239
- return this.state.executionStats;
240
- }
241
- // ---- Order book ----
242
- /** Cache the latest order book snapshot for a symbol. */
243
- updateOrderBook(symbol, orderbook) {
244
- this.lastOrderBook.set(symbol, orderbook);
245
- }
246
- getLastOrderBook(symbol) {
247
- return this.lastOrderBook.get(symbol);
248
- }
249
- // ---- Write operations (for tools) ----
250
- createOrder(symbol, side, type, amount, price, metadata) {
251
- // ---- Startup trade lockout ----
252
- // Block trades during the first 15s after gateway restart IF there were
253
- // existing positions at startup. This prevents stale agent sessions from
254
- // selling positions before the session is cleared and the agent re-reads SKILL.md.
255
- // Only activates when positions exist (nothing to protect if starting empty).
256
- const elapsed = Date.now() - this.startupTime;
257
- if (this.hadPositionsAtStartup && elapsed < ExchangeSimulator.STARTUP_LOCKOUT_MS) {
258
- const remaining = Math.ceil((ExchangeSimulator.STARTUP_LOCKOUT_MS - elapsed) / 1000);
259
- logger.warn(TAG, `STARTUP LOCKOUT: Blocked ${side} ${amount} ${symbol} — ${remaining}s remaining. This prevents stale session trades during restart.`);
260
- throw new Error(`Trade blocked: startup lockout (${remaining}s remaining). The gateway just restarted — wait for the agent to re-read its instructions and check positions before trading.`);
261
- }
262
- if (amount <= 0) {
263
- throw new Error('Order amount must be positive');
264
- }
265
- if (type === 'limit' && (price === undefined || price <= 0)) {
266
- throw new Error('Limit orders require a positive price');
267
- }
268
- const now = new Date().toISOString();
269
- const order = {
270
- id: randomUUID(),
271
- symbol,
272
- side,
273
- type,
274
- status: 'open',
275
- amount,
276
- price: type === 'limit' ? price : null,
277
- filled: 0,
278
- average: null,
279
- cost: 0,
280
- fee: { cost: 0, currency: parseSymbol(symbol).quote },
281
- createdAt: now,
282
- };
283
- if (type === 'market') {
284
- // Market orders fill immediately at current price
285
- const ticker = this.lastTicker.get(symbol);
286
- if (!ticker) {
287
- throw new Error(`No ticker data for ${symbol}. Call updateTicker() first.`);
288
- }
289
- // Stale-quote guard (issue #202): a market fill priced off an aged quote
290
- // books phantom P&L the moment a fresh price arrives (measured up to
291
- // ~0.6% off on the HL paper book — a real −$22.99 in 4 seconds). Reject
292
- // instead; the agent refreshes via fetch_ticker and retries.
293
- this.assertQuoteFresh(symbol, ticker);
294
- return this.executeMarketFill(order, ticker.last, metadata);
295
- }
296
- // Limit order — check if it crosses the current price. A stale quote must
297
- // not price an immediate cross-fill (same hazard as market fills); the
298
- // order RESTS instead and fills on the next fresh tick via updateTicker.
299
- const ticker = this.lastTicker.get(symbol);
300
- if (ticker && this.quoteAgeMs(ticker) <= this.maxQuoteAgeMs() && this.shouldFillLimit(order, ticker.last)) {
301
- return this.executeLimitFill(order, ticker.last, metadata);
302
- }
303
- // Limit order doesn't cross — add to open orders. Pin the entry metadata
304
- // ONTO the order itself (durable home, persisted in state.json) AND in the
305
- // in-memory cache (fast path). order.metadata is the one that survives a
306
- // restart / two-process reload, so a resting limit never fills naked.
307
- if (metadata) {
308
- order.metadata = metadata;
309
- this.pendingOrderMetadata.set(order.id, metadata);
310
- }
311
- this.state.openOrders.push(order);
312
- this.emitStateChanged();
313
- logger.info(TAG, `Limit order created: ${side} ${amount} ${symbol} @ ${price}`);
314
- return this.toCcxtOrder(order);
315
- }
316
- cancelOrder(orderId) {
317
- const idx = this.state.openOrders.findIndex(o => o.id === orderId);
318
- if (idx === -1) {
319
- throw new Error(`Order not found: ${orderId}`);
320
- }
321
- const order = this.state.openOrders.splice(idx, 1)[0];
322
- order.status = 'canceled';
323
- this.pendingOrderMetadata.delete(orderId);
324
- this.emitStateChanged();
325
- logger.info(TAG, `Order cancelled: ${orderId}`);
326
- return this.toCcxtOrder(order);
327
- }
328
- cancelAllOrders(symbol) {
329
- const cancelled = [];
330
- const remaining = [];
331
- for (const order of this.state.openOrders) {
332
- if (!symbol || order.symbol === symbol) {
333
- order.status = 'canceled';
334
- cancelled.push(order);
335
- this.pendingOrderMetadata.delete(order.id);
336
- }
337
- else {
338
- remaining.push(order);
339
- }
340
- }
341
- this.state.openOrders = remaining;
342
- if (cancelled.length > 0) {
343
- this.emitStateChanged();
344
- logger.info(TAG, `Cancelled ${cancelled.length} orders${symbol ? ` for ${symbol}` : ''}`);
345
- }
346
- return cancelled.map(o => this.toCcxtOrder(o));
347
- }
348
- closePosition(symbol, closeReason) {
349
- const position = this.state.positions.find(p => p.symbol === symbol);
350
- if (!position) {
351
- throw new Error(`No open position for ${symbol}`);
352
- }
353
- // Tag the position's metadata with the close reason BEFORE firing the close
354
- // order. The closing Trade record copies `existingPosition.metadata` in
355
- // fill-engine.executeOrderFill, so this is what makes the reason reach
356
- // the trade history (and therefore the agent on its next heartbeat).
357
- if (closeReason) {
358
- position.metadata = { ...(position.metadata ?? {}), closeReason };
359
- }
360
- // Create opposing market order to close the position
361
- const closeSide = position.side === 'long' ? 'sell' : 'buy';
362
- return this.createOrder(symbol, closeSide, 'market', position.quantity);
363
- }
364
- /** Paper-only: move an open position's MUTABLE protective levels (stopPrice /
365
- * targetPrice) in place and persist, WITHOUT the close+reopen round-trip
366
- * (which pays an extra taker fee and resets the R/MFE denominators). The
367
- * frozen originalStopPrice / originalEntryPrice are intentionally left
368
- * untouched so R-multiples stay anchored at entry. The paper stop-watcher
369
- * and getPositions both read metadata.stopPrice, so a moved stop takes
370
- * effect on the next watcher tick. Throws if there is no open position. (M9) */
371
- updatePositionMetadata(symbol, patch) {
372
- const pos = this.state.positions.find(p => p.symbol === symbol);
373
- if (!pos) {
374
- throw new Error(`No open position for ${symbol}`);
375
- }
376
- const meta = { ...(pos.metadata ?? {}) };
377
- if (patch.stopPrice !== undefined)
378
- meta.stopPrice = patch.stopPrice;
379
- if (patch.targetPrice !== undefined)
380
- meta.targetPrice = patch.targetPrice;
381
- pos.metadata = meta;
382
- this.emitStateChanged();
383
- }
384
- // ---- Ticker updates (checks pending limit fills) ----
385
- updateTicker(ticker) {
386
- this.lastTicker.set(ticker.symbol, ticker);
387
- this.refreshMfeForSymbol(ticker.symbol, ticker.last);
388
- // Check if any pending limit orders should fill
389
- const toFill = [];
390
- const remaining = [];
391
- for (const order of this.state.openOrders) {
392
- if (order.symbol === ticker.symbol && this.shouldFillLimit(order, ticker.last)) {
393
- toFill.push(order);
394
- }
395
- else {
396
- remaining.push(order);
397
- }
398
- }
399
- if (toFill.length > 0) {
400
- this.state.openOrders = remaining;
401
- for (const order of toFill) {
402
- // Prefer the durable metadata pinned on the order (survives restart);
403
- // fall back to the in-memory cache for any legacy in-flight order.
404
- const meta = order.metadata ?? this.pendingOrderMetadata.get(order.id);
405
- try {
406
- this.executeLimitFill(order, ticker.last, meta);
407
- // Clear the cache only AFTER a successful fill so a throw-then-retry
408
- // (e.g. transient insufficient balance) keeps the metadata for the
409
- // next tick instead of filling naked.
410
- this.pendingOrderMetadata.delete(order.id);
411
- }
412
- catch (err) {
413
- // Fill failed (e.g. insufficient balance) — restore order to open
414
- // list. order.metadata stays intact, so the retry is not naked.
415
- order.status = 'open';
416
- this.state.openOrders.push(order);
417
- const message = err instanceof Error ? err.message : String(err);
418
- logger.warn(TAG, `Limit fill failed for ${order.id}, order restored: ${message}`);
419
- this.emit('fillError', {
420
- orderId: order.id,
421
- symbol: order.symbol,
422
- error: message,
423
- timestamp: new Date().toISOString(),
424
- });
425
- }
426
- }
427
- }
428
- }
429
- getLastTicker(symbol) {
430
- return this.lastTicker.get(symbol);
431
- }
432
- /** Walk every position for `symbol` and refresh MFE / give-back from the
433
- * latest mark. Idempotent — pure update of `metadata.mfePeakPrice` (only
434
- * ratchets favourably) plus derived `mfeR` and `giveBackRatio`. Safe to
435
- * call before any positions exist (no-op). */
436
- refreshMfeForSymbol(symbol, mark) {
437
- if (!Number.isFinite(mark) || mark <= 0)
438
- return;
439
- let peakAdvanced = false;
440
- for (const p of this.state.positions) {
441
- if (p.symbol !== symbol)
442
- continue;
443
- const meta = { ...(p.metadata ?? {}) };
444
- const priorPeak = meta.mfePeakPrice;
445
- const out = updateMfe({
446
- side: p.side,
447
- // R/MFE are denominated against the FROZEN original entry (and stop),
448
- // never the running averaged entryPrice — otherwise a scale-in
449
- // retroactively shrinks an already-achieved excursion (M4).
450
- entryPrice: meta.originalEntryPrice ?? p.entryPrice,
451
- originalStopPrice: meta.originalStopPrice,
452
- markPrice: mark,
453
- priorPeakPrice: meta.mfePeakPrice,
454
- });
455
- if (out.mfePeakPrice !== priorPeak)
456
- peakAdvanced = true;
457
- meta.mfePeakPrice = out.mfePeakPrice;
458
- meta.mfeR = out.mfeR;
459
- meta.giveBackRatio = out.giveBackRatio;
460
- p.metadata = meta;
461
- }
462
- // Persist ONLY when a peak strictly advances (M3). The monotone peak moves
463
- // less and less over time and debouncedSave coalesces to <=1/s, so this
464
- // does not flood — and it is what makes the MFE ratchet survive a restart
465
- // and a reloadState() (which previously discarded the unpersisted peak,
466
- // feeding a stale/zeroed mfeR to the agent and the exit gate). mfeR /
467
- // giveBackRatio are recomputed from the persisted peak on read, so they
468
- // don't need a per-tick write.
469
- if (peakAdvanced) {
470
- this.emitStateChanged();
471
- }
472
- }
473
- // ---- State management ----
474
- /** Replace internal state with a fresh copy from disk.
475
- * Used by the gateway process to pick up state saved by the agent process.
476
- *
477
- * Non-destructive for the MFE ratchet (M3): the disk copy can lag the live
478
- * peak (refreshMfeForSymbol ratchets every tick; persistence coalesces), so
479
- * a wholesale swap would discard it. For each position present in BOTH
480
- * snapshots (same symbol+side+openedAt) we carry over the MORE-favourable
481
- * monotone peak and recompute mfeR/giveBackRatio from the current mark, so a
482
- * reload never regresses the agent's give-back signal. */
483
- replaceState(newState) {
484
- // M8: refuse a disk snapshot OLDER than what we already hold in memory.
485
- // reloadState() reads disk that lags memory by the debounce window; without
486
- // this guard a reload fired within ~1s of a local mutation would revert it
487
- // (and a second mutation could then re-persist the reverted state, dropping
488
- // a position + its realized PnL). A genuinely newer cross-process write
489
- // (savedAt >= ours) still installs. Equal stamps install (idempotent).
490
- const incomingAt = newState.savedAt ?? 0;
491
- const currentAt = this.state.savedAt ?? 0;
492
- if (incomingAt < currentAt) {
493
- logger.debug(TAG, `Ignoring stale state reload (disk savedAt=${incomingAt} < memory savedAt=${currentAt})`);
494
- return;
495
- }
496
- const priorPeakByKey = new Map();
497
- for (const p of this.state.positions) {
498
- if (p.metadata?.mfePeakPrice !== undefined) {
499
- priorPeakByKey.set(`${p.symbol}|${p.side}|${p.openedAt}`, p.metadata.mfePeakPrice);
500
- }
501
- }
502
- this.state = newState;
503
- for (const p of this.state.positions) {
504
- const priorPeak = priorPeakByKey.get(`${p.symbol}|${p.side}|${p.openedAt}`);
505
- if (priorPeak === undefined)
506
- continue;
507
- const meta = { ...(p.metadata ?? {}) };
508
- const diskPeak = meta.mfePeakPrice;
509
- // Monotone: keep the better of disk vs in-memory (max for long, min short).
510
- const keptPeak = diskPeak === undefined
511
- ? priorPeak
512
- : (p.side === 'long' ? Math.max(priorPeak, diskPeak) : Math.min(priorPeak, diskPeak));
513
- const mark = this.lastTicker.get(p.symbol)?.last ?? meta.originalEntryPrice ?? p.entryPrice;
514
- const out = updateMfe({
515
- side: p.side,
516
- entryPrice: meta.originalEntryPrice ?? p.entryPrice,
517
- originalStopPrice: meta.originalStopPrice,
518
- markPrice: mark,
519
- priorPeakPrice: keptPeak,
520
- });
521
- meta.mfePeakPrice = out.mfePeakPrice;
522
- meta.mfeR = out.mfeR;
523
- meta.giveBackRatio = out.giveBackRatio;
524
- p.metadata = meta;
525
- }
526
- logger.debug(TAG, `State replaced: ${newState.positions.length} positions, ${newState.openOrders.length} orders`);
527
- }
528
- getState() {
529
- // Spread each element + deep-copy ONLY the nested objects that need
530
- // isolation (metadata — which holds the nested realizationRule.scale array
531
- // — plus executionQuality and fee). This keeps a consumer mutating the
532
- // snapshot, or the JSON-persisted copy, from corrupting live state, while
533
- // staying cheap on the hot persistence path: elements without metadata
534
- // (the common case) pay only a shallow spread, never a full structuredClone.
535
- return {
536
- ...this.state,
537
- wallet: Object.fromEntries(Object.entries(this.state.wallet).map(([k, v]) => [k, { ...v }])),
538
- // Positions + open orders ARE mutated in place (updatePositionMetadata,
539
- // refreshMfeForSymbol, scale-in, limit metadata), so deep-copy their
540
- // nested metadata to keep a snapshot consumer from corrupting live state.
541
- positions: this.state.positions.map(p => ({
542
- ...p,
543
- metadata: p.metadata ? structuredClone(p.metadata) : undefined,
544
- })),
545
- openOrders: this.state.openOrders.map(o => ({
546
- ...o,
547
- fee: { ...o.fee },
548
- metadata: o.metadata ? structuredClone(o.metadata) : undefined,
549
- })),
550
- // Trade records are append-only + immutable after close, so a shallow copy
551
- // is safe and keeps getState() O(n)-cheap on the hot persistence path even
552
- // with a full 1000-entry history (JSON persistence still deep-serializes).
553
- tradeHistory: this.state.tradeHistory.map(t => ({ ...t })),
554
- executionStats: this.state.executionStats ? { ...this.state.executionStats } : undefined,
555
- };
556
- }
557
- // ---- Private helpers ----
558
- /** Default max quote age a NEW-exposure fill may price against (issue #202).
559
- * Generous vs the 5s paper feed cadence; env RC_PAPER_MAX_QUOTE_AGE_MS
560
- * overrides. */
561
- static DEFAULT_MAX_QUOTE_AGE_MS = 45_000;
562
- maxQuoteAgeMs() {
563
- const raw = Number(process.env.RC_PAPER_MAX_QUOTE_AGE_MS);
564
- return Number.isFinite(raw) && raw > 0 ? raw : ExchangeSimulator.DEFAULT_MAX_QUOTE_AGE_MS;
565
- }
566
- /** Quote age from the ticker's own timestamp. A missing/invalid timestamp
567
- * reads as age 0 (fail-open — the guard cannot fire on it). */
568
- quoteAgeMs(ticker) {
569
- const ts = ticker.timestamp;
570
- if (!Number.isFinite(ts) || ts <= 0)
571
- return 0;
572
- return Math.max(0, Date.now() - ts);
573
- }
574
- /** Reject fills that would OPEN or GROW exposure on a stale quote. Risk-
575
- * reducing fills (closes/partials against an existing position) are always
576
- * allowed — blocking a close on a broken feed compounds the risk. */
577
- assertQuoteFresh(symbol, ticker) {
578
- const age = this.quoteAgeMs(ticker);
579
- const max = this.maxQuoteAgeMs();
580
- if (age <= max)
581
- return;
582
- const pos = this.state.positions.find(p => p.symbol === symbol);
583
- // The order side reaching here is the one being filled — derive reduce vs
584
- // grow from the position side at the call site instead? The market path
585
- // calls this before fill with the order side unavailable; use position
586
- // presence: any existing position keeps closes flowing, and a stale-quote
587
- // scale-in on an open position is bounded by the feed refreshing open
588
- // symbols every 5s (only NEW symbols go minutes without a tick).
589
- if (pos) {
590
- logger.warn(TAG, `Stale quote for ${symbol} (${Math.round(age / 1000)}s old) — allowing fill because an open ` +
591
- `position exists (risk-reducing paths are never blocked)`);
592
- return;
593
- }
594
- throw new Error(`Order rejected: market data for ${symbol} is stale (${Math.round(age / 1000)}s old, ` +
595
- `max ${Math.round(max / 1000)}s). Refresh the price (fetch_ticker) and retry.`);
596
- }
597
- shouldFillLimit(order, currentPrice) {
598
- if (order.price === null)
599
- return false;
600
- // BUY limit fills when price <= limit price
601
- // SELL limit fills when price >= limit price
602
- return order.side === 'buy'
603
- ? currentPrice <= order.price
604
- : currentPrice >= order.price;
605
- }
606
- executeMarketFill(order, currentPrice, metadata) {
607
- const position = this.state.positions.find(p => p.symbol === order.symbol) ?? null;
608
- const orderbook = this.lastOrderBook.get(order.symbol) ?? null;
609
- const realistic = {
610
- orderbook,
611
- config: this.simulationConfig,
612
- volFactor: this.cachedVolFactor,
613
- metadata,
614
- };
615
- const result = fillMarketOrder(order, currentPrice, this.state.wallet, position, realistic);
616
- // Observability for issue #202: stamp the quote's age onto the fill's
617
- // execution-quality record so staleness is visible in trade history.
618
- const tickerAtFill = this.lastTicker.get(order.symbol);
619
- if (result.executionQuality && tickerAtFill) {
620
- result.executionQuality.quoteAgeMs = this.quoteAgeMs(tickerAtFill);
621
- }
622
- const ccxtOrder = this.applyFillResult(result);
623
- const eq = result.executionQuality;
624
- if (eq) {
625
- logger.info(TAG, `Market order filled: ${order.side} ${order.amount} ${order.symbol} @ ${result.order.average}` +
626
- ` (decision: ${eq.decisionPrice.toFixed(2)}, slippage: ${eq.slippageBps.toFixed(2)}bps` +
627
- `, latency: ${eq.latencyMs.toFixed(0)}ms, fee: ${eq.feeRate * 100}%` +
628
- `, book: ${eq.bookDepthAvailable ? `${eq.bookLevelsConsumed} levels` : 'unavailable'})`);
629
- }
630
- else {
631
- logger.info(TAG, `Market order filled: ${order.side} ${order.amount} ${order.symbol} @ ${result.order.average}`);
632
- }
633
- return ccxtOrder;
634
- }
635
- executeLimitFill(order, decisionPrice, metadata) {
636
- const position = this.state.positions.find(p => p.symbol === order.symbol) ?? null;
637
- const result = fillLimitOrder(order, this.state.wallet, position, this.simulationConfig, decisionPrice, metadata);
638
- const ccxtOrder = this.applyFillResult(result);
639
- const eq = result.executionQuality;
640
- if (eq) {
641
- logger.info(TAG, `Limit order filled: ${order.side} ${order.amount} ${order.symbol} @ ${result.order.average}` +
642
- ` (maker fee: ${eq.feeRate * 100}%)`);
643
- }
644
- else {
645
- logger.info(TAG, `Limit order filled: ${order.side} ${order.amount} ${order.symbol} @ ${result.order.average}`);
646
- }
647
- return ccxtOrder;
648
- }
649
- /** Apply a fill result: update positions, record trade, track stats, emit state change. */
650
- applyFillResult(result) {
651
- this.updatePosition(result.order.symbol, result.position);
652
- if (result.trade)
653
- this.addTrade(result.trade);
654
- if (result.executionQuality)
655
- this.updateExecutionStats(result.executionQuality);
656
- this.emitStateChanged();
657
- // Emit fill event for shadow tracker (Phase 9b)
658
- this.emit('fill', {
659
- trade: result.trade,
660
- orderId: result.order.id,
661
- symbol: result.order.symbol,
662
- side: result.order.side,
663
- type: result.order.type,
664
- amount: result.order.amount,
665
- fillPrice: result.order.average ?? 0,
666
- fee: result.order.fee.cost,
667
- });
668
- const ccxtOrder = this.toCcxtOrder(result.order);
669
- // Reducing fills carry a Trade record — attach its engine-exact economics
670
- // (net-of-fee P&L + both fee legs) so the journal close capture records
671
- // NET, matching the live book's convention (issue #201). The wallet/NAV
672
- // was already net; only the journal was blind to fees.
673
- if (result.trade) {
674
- const t = result.trade;
675
- ccxtOrder.info = {
676
- ...(ccxtOrder.info ?? {}),
677
- paperTrade: {
678
- grossRealizedPnl: t.realizedPnl,
679
- netRealizedPnl: t.realizedPnl - (t.fee + (t.openFee ?? 0)),
680
- openFee: t.openFee ?? 0,
681
- closeFee: t.fee,
682
- ...(typeof t.metadata?.setupType === 'string' ? { setupType: t.metadata.setupType } : {}),
683
- },
684
- };
685
- }
686
- return ccxtOrder;
687
- }
688
- updatePosition(symbol, newPosition) {
689
- const idx = this.state.positions.findIndex(p => p.symbol === symbol);
690
- if (newPosition) {
691
- if (idx >= 0) {
692
- this.state.positions[idx] = newPosition;
693
- }
694
- else {
695
- this.state.positions.push(newPosition);
696
- }
697
- }
698
- else if (idx >= 0) {
699
- this.state.positions.splice(idx, 1);
700
- }
701
- }
702
- addTrade(trade) {
703
- this.state.tradeHistory.push(trade);
704
- // Keep only the last MAX_TRADE_HISTORY trades
705
- if (this.state.tradeHistory.length > MAX_TRADE_HISTORY) {
706
- this.state.tradeHistory = this.state.tradeHistory.slice(-MAX_TRADE_HISTORY);
707
- }
708
- }
709
- /** Update cumulative execution stats from a fill's execution quality. */
710
- updateExecutionStats(eq) {
711
- if (!this.state.executionStats) {
712
- this.state.executionStats = {
713
- totalTrades: 0,
714
- totalSlippageBps: 0,
715
- totalFeesPaid: 0,
716
- worstSlippageBps: 0,
717
- avgSlippageBps: 0,
718
- avgLatencyMs: 0,
719
- };
720
- }
721
- const stats = this.state.executionStats;
722
- stats.totalTrades++;
723
- const absSlippage = Math.abs(eq.slippageBps);
724
- stats.totalSlippageBps += absSlippage;
725
- stats.totalFeesPaid += eq.feePaid;
726
- if (absSlippage > stats.worstSlippageBps) {
727
- stats.worstSlippageBps = absSlippage;
728
- }
729
- stats.avgSlippageBps = stats.totalSlippageBps / stats.totalTrades;
730
- stats.avgLatencyMs =
731
- (stats.avgLatencyMs * (stats.totalTrades - 1) + eq.latencyMs) / stats.totalTrades;
732
- }
733
- emitStateChanged() {
734
- // Stamp the mutation time so a stale-disk reloadState() can be detected and
735
- // dropped (M8). Must be set BEFORE getState() snapshots the state.
736
- this.state.savedAt = Date.now();
737
- this.emit('stateChanged', this.getState());
738
- }
739
- toCcxtOrder(order) {
740
- const ts = new Date(order.createdAt).getTime();
741
- return {
742
- id: order.id,
743
- symbol: order.symbol,
744
- side: order.side,
745
- type: order.type,
746
- status: order.status,
747
- amount: order.amount,
748
- filled: order.filled,
749
- remaining: order.amount - order.filled,
750
- average: order.average,
751
- price: order.price,
752
- cost: order.cost,
753
- fee: order.fee,
754
- timestamp: ts,
755
- datetime: order.createdAt,
756
- timeInForce: 'GTC',
757
- };
758
- }
759
- }