@reefclaw/openclaw-plugin 0.1.0

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 (371) hide show
  1. package/.gitignore +8 -0
  2. package/audit/mode-transition-audit.d.ts +11 -0
  3. package/audit/mode-transition-audit.js +29 -0
  4. package/balance-utils.d.ts +36 -0
  5. package/balance-utils.js +98 -0
  6. package/bridge/bridge.d.ts +109 -0
  7. package/bridge/bridge.js +1036 -0
  8. package/bridge/config.d.ts +43 -0
  9. package/bridge/config.js +176 -0
  10. package/bridge/connector.d.ts +52 -0
  11. package/bridge/connector.js +293 -0
  12. package/bridge/event-replay-buffer.d.ts +43 -0
  13. package/bridge/event-replay-buffer.js +109 -0
  14. package/bridge/gateway/event-parser.d.ts +209 -0
  15. package/bridge/gateway/event-parser.js +793 -0
  16. package/bridge/gateway/gateway-config.d.ts +39 -0
  17. package/bridge/gateway/gateway-config.js +86 -0
  18. package/bridge/gateway/gateway-http-client.d.ts +50 -0
  19. package/bridge/gateway/gateway-http-client.js +165 -0
  20. package/bridge/gateway/gateway-ws-client.d.ts +116 -0
  21. package/bridge/gateway/gateway-ws-client.js +417 -0
  22. package/bridge/gateway/poller.d.ts +146 -0
  23. package/bridge/gateway/poller.js +505 -0
  24. package/bridge/gateway/tool-discovery.d.ts +25 -0
  25. package/bridge/gateway/tool-discovery.js +198 -0
  26. package/bridge/index.d.ts +2 -0
  27. package/bridge/index.js +253 -0
  28. package/bridge/logger.d.ts +2 -0
  29. package/bridge/logger.js +2 -0
  30. package/bridge/provider.d.ts +156 -0
  31. package/bridge/provider.js +2 -0
  32. package/bridge/providers/emergency-commands.d.ts +54 -0
  33. package/bridge/providers/emergency-commands.js +235 -0
  34. package/bridge/providers/gateway.d.ts +322 -0
  35. package/bridge/providers/gateway.js +2302 -0
  36. package/bridge/providers/mock.d.ts +37 -0
  37. package/bridge/providers/mock.js +385 -0
  38. package/bridge/providers/onboarding-commands.d.ts +83 -0
  39. package/bridge/providers/onboarding-commands.js +213 -0
  40. package/bridge/providers/risk-calculator.d.ts +96 -0
  41. package/bridge/providers/risk-calculator.js +369 -0
  42. package/bridge/setup.d.ts +32 -0
  43. package/bridge/setup.js +226 -0
  44. package/bridge/types.d.ts +584 -0
  45. package/bridge/types.js +50 -0
  46. package/bridge/utils/reconnect.d.ts +6 -0
  47. package/bridge/utils/reconnect.js +6 -0
  48. package/bridge/utils/skill-signing.d.ts +51 -0
  49. package/bridge/utils/skill-signing.js +138 -0
  50. package/bridge/utils/skill-version.d.ts +17 -0
  51. package/bridge/utils/skill-version.js +74 -0
  52. package/ccxt/binance-ban-gate.d.ts +47 -0
  53. package/ccxt/binance-ban-gate.js +409 -0
  54. package/ccxt/binance-private.d.ts +325 -0
  55. package/ccxt/binance-private.js +1415 -0
  56. package/ccxt/binance-public.d.ts +18 -0
  57. package/ccxt/binance-public.js +147 -0
  58. package/config/agent-config-client.d.ts +55 -0
  59. package/config/agent-config-client.js +145 -0
  60. package/config/agent-config-poller.d.ts +25 -0
  61. package/config/agent-config-poller.js +100 -0
  62. package/config/brackets-config.d.ts +22 -0
  63. package/config/brackets-config.js +58 -0
  64. package/config/gate-store.d.ts +18 -0
  65. package/config/gate-store.js +61 -0
  66. package/config/plugin-config-io.d.ts +181 -0
  67. package/config/plugin-config-io.js +84 -0
  68. package/config/position-review-config.d.ts +35 -0
  69. package/config/position-review-config.js +105 -0
  70. package/config/tool-gate.d.ts +53 -0
  71. package/config/tool-gate.js +125 -0
  72. package/config/user-data-stream-config.d.ts +85 -0
  73. package/config/user-data-stream-config.js +224 -0
  74. package/connector-supervisor.d.ts +36 -0
  75. package/connector-supervisor.js +149 -0
  76. package/exchange-adapter.d.ts +49 -0
  77. package/exchange-adapter.js +4 -0
  78. package/index.d.ts +30 -0
  79. package/index.js +2030 -0
  80. package/ingest/pending-entry-metadata.d.ts +52 -0
  81. package/ingest/pending-entry-metadata.js +182 -0
  82. package/ingest/position-auto-capture.d.ts +98 -0
  83. package/ingest/position-auto-capture.js +394 -0
  84. package/ingest/position-decisions-client.d.ts +318 -0
  85. package/ingest/position-decisions-client.js +296 -0
  86. package/ingest/reconcile-db-vs-exchange.d.ts +13 -0
  87. package/ingest/reconcile-db-vs-exchange.js +114 -0
  88. package/ingest/reconciler-cleanup.d.ts +37 -0
  89. package/ingest/reconciler-cleanup.js +147 -0
  90. package/ingest/rest-gap-filler.d.ts +191 -0
  91. package/ingest/rest-gap-filler.js +565 -0
  92. package/ingest/touched-symbols-store.d.ts +25 -0
  93. package/ingest/touched-symbols-store.js +96 -0
  94. package/ingest/trade-store-client.d.ts +40 -0
  95. package/ingest/trade-store-client.js +125 -0
  96. package/ingest/ws-ingest.d.ts +43 -0
  97. package/ingest/ws-ingest.js +126 -0
  98. package/learning/setup-family.d.ts +21 -0
  99. package/learning/setup-family.js +103 -0
  100. package/lifecycle/install-signal-handlers.d.ts +33 -0
  101. package/lifecycle/install-signal-handlers.js +112 -0
  102. package/lifecycle/shutdown-coordinator.d.ts +43 -0
  103. package/lifecycle/shutdown-coordinator.js +131 -0
  104. package/live/bracket-id.d.ts +18 -0
  105. package/live/bracket-id.js +81 -0
  106. package/live/bracket-ledger.d.ts +54 -0
  107. package/live/bracket-ledger.js +267 -0
  108. package/live/bracket-manager.d.ts +82 -0
  109. package/live/bracket-manager.js +478 -0
  110. package/live/bracket-params.d.ts +22 -0
  111. package/live/bracket-params.js +124 -0
  112. package/live/bracket-reconciler.d.ts +95 -0
  113. package/live/bracket-reconciler.js +573 -0
  114. package/live/bracket-types.d.ts +102 -0
  115. package/live/bracket-types.js +8 -0
  116. package/live/deposit-tracker.d.ts +62 -0
  117. package/live/deposit-tracker.js +97 -0
  118. package/live/emergency-controls.d.ts +32 -0
  119. package/live/emergency-controls.js +226 -0
  120. package/live/exchange-errors.d.ts +12 -0
  121. package/live/exchange-errors.js +130 -0
  122. package/live/exchange-info-cache.d.ts +35 -0
  123. package/live/exchange-info-cache.js +119 -0
  124. package/live/fact-subscriber.d.ts +78 -0
  125. package/live/fact-subscriber.js +182 -0
  126. package/live/intent-journal.d.ts +42 -0
  127. package/live/intent-journal.js +122 -0
  128. package/live/listen-key-manager.d.ts +70 -0
  129. package/live/listen-key-manager.js +169 -0
  130. package/live/live-adapter.d.ts +264 -0
  131. package/live/live-adapter.js +1665 -0
  132. package/live/live-balance-enricher.d.ts +32 -0
  133. package/live/live-balance-enricher.js +104 -0
  134. package/live/live-bracket-api.d.ts +13 -0
  135. package/live/live-bracket-api.js +20 -0
  136. package/live/live-state-store.d.ts +194 -0
  137. package/live/live-state-store.js +450 -0
  138. package/live/local-signal-service.d.ts +57 -0
  139. package/live/local-signal-service.js +146 -0
  140. package/live/local-strategy-evaluator.d.ts +62 -0
  141. package/live/local-strategy-evaluator.js +127 -0
  142. package/live/microstructure-assembler.d.ts +54 -0
  143. package/live/microstructure-assembler.js +148 -0
  144. package/live/order-poller.d.ts +29 -0
  145. package/live/order-poller.js +125 -0
  146. package/live/position-state-store.d.ts +83 -0
  147. package/live/position-state-store.js +237 -0
  148. package/live/proposal-decision-listener.d.ts +64 -0
  149. package/live/proposal-decision-listener.js +288 -0
  150. package/live/proposal-manager.d.ts +76 -0
  151. package/live/proposal-manager.js +140 -0
  152. package/live/rate-limiter.d.ts +47 -0
  153. package/live/rate-limiter.js +159 -0
  154. package/live/reconciler.d.ts +39 -0
  155. package/live/reconciler.js +175 -0
  156. package/live/setup-buckets.d.ts +7 -0
  157. package/live/setup-buckets.js +33 -0
  158. package/live/slippage-tracker.d.ts +45 -0
  159. package/live/slippage-tracker.js +78 -0
  160. package/live/stop-watcher.d.ts +34 -0
  161. package/live/stop-watcher.js +158 -0
  162. package/live/user-data-active-probe.d.ts +54 -0
  163. package/live/user-data-active-probe.js +180 -0
  164. package/live/user-data-stream-controller.d.ts +200 -0
  165. package/live/user-data-stream-controller.js +579 -0
  166. package/live/user-data-stream-ws.d.ts +22 -0
  167. package/live/user-data-stream-ws.js +63 -0
  168. package/live/user-data-stream.d.ts +243 -0
  169. package/live/user-data-stream.js +704 -0
  170. package/logger.d.ts +2 -0
  171. package/logger.js +2 -0
  172. package/mfe.d.ts +21 -0
  173. package/mfe.js +68 -0
  174. package/onboarding/mode-ladder.d.ts +1 -0
  175. package/onboarding/mode-ladder.js +3 -0
  176. package/onboarding/runtime.d.ts +71 -0
  177. package/onboarding/runtime.js +153 -0
  178. package/openclaw.plugin.json +94 -0
  179. package/package.json +27 -0
  180. package/paper-adapter.d.ts +24 -0
  181. package/paper-adapter.js +91 -0
  182. package/persistence/state-manager.d.ts +42 -0
  183. package/persistence/state-manager.js +164 -0
  184. package/pinned-plan.d.ts +9 -0
  185. package/pinned-plan.js +23 -0
  186. package/risk/pre-trade-check.d.ts +38 -0
  187. package/risk/pre-trade-check.js +345 -0
  188. package/risk/pre-trade-types.d.ts +60 -0
  189. package/risk/pre-trade-types.js +3 -0
  190. package/scripts/assemble.mjs +114 -0
  191. package/shadow/shadow-tracker.d.ts +36 -0
  192. package/shadow/shadow-tracker.js +151 -0
  193. package/shadow/types.d.ts +42 -0
  194. package/shadow/types.js +20 -0
  195. package/shared/indicators-extended.d.ts +52 -0
  196. package/shared/indicators-extended.js +291 -0
  197. package/shared/indicators.d.ts +15 -0
  198. package/shared/indicators.js +114 -0
  199. package/signals/conditions/registry.d.ts +16 -0
  200. package/signals/conditions/registry.js +1274 -0
  201. package/signals/conditions/types.d.ts +1 -0
  202. package/signals/conditions/types.js +4 -0
  203. package/signals/direction-rules.d.ts +3 -0
  204. package/signals/direction-rules.js +24 -0
  205. package/signals/entry-rules.d.ts +6 -0
  206. package/signals/entry-rules.js +33 -0
  207. package/signals/serialize-context.d.ts +4 -0
  208. package/signals/serialize-context.js +39 -0
  209. package/signals/stop-rules.d.ts +3 -0
  210. package/signals/stop-rules.js +48 -0
  211. package/signals/strategy-adapter.d.ts +14 -0
  212. package/signals/strategy-adapter.js +122 -0
  213. package/signals/types.d.ts +1 -0
  214. package/signals/types.js +8 -0
  215. package/simulator/exchange-simulator.d.ts +93 -0
  216. package/simulator/exchange-simulator.js +684 -0
  217. package/simulator/fill-engine.d.ts +53 -0
  218. package/simulator/fill-engine.js +276 -0
  219. package/simulator/paper-market-feed.d.ts +26 -0
  220. package/simulator/paper-market-feed.js +104 -0
  221. package/simulator/realistic-fills.d.ts +59 -0
  222. package/simulator/realistic-fills.js +175 -0
  223. package/simulator/types.d.ts +219 -0
  224. package/simulator/types.js +43 -0
  225. package/skills/reefclaw/SKILL.md +97 -0
  226. package/strategy/builtin-strategies.d.ts +2 -0
  227. package/strategy/builtin-strategies.js +109 -0
  228. package/strategy/condition-registry.d.ts +3 -0
  229. package/strategy/condition-registry.js +153 -0
  230. package/strategy/evaluator.d.ts +67 -0
  231. package/strategy/evaluator.js +93 -0
  232. package/tools/assessment-validation.d.ts +118 -0
  233. package/tools/assessment-validation.js +415 -0
  234. package/tools/attach-brackets.d.ts +34 -0
  235. package/tools/attach-brackets.js +363 -0
  236. package/tools/audit-bracket-protection.d.ts +49 -0
  237. package/tools/audit-bracket-protection.js +527 -0
  238. package/tools/cancel-all-orders.d.ts +7 -0
  239. package/tools/cancel-all-orders.js +5 -0
  240. package/tools/cancel-order.d.ts +10 -0
  241. package/tools/cancel-order.js +14 -0
  242. package/tools/check-position-health.d.ts +46 -0
  243. package/tools/check-position-health.js +194 -0
  244. package/tools/clear-exchange-credentials.d.ts +24 -0
  245. package/tools/clear-exchange-credentials.js +70 -0
  246. package/tools/close-position.d.ts +22 -0
  247. package/tools/close-position.js +449 -0
  248. package/tools/create-order.d.ts +54 -0
  249. package/tools/create-order.js +338 -0
  250. package/tools/exit-gate.d.ts +58 -0
  251. package/tools/exit-gate.js +162 -0
  252. package/tools/fetch-balance.d.ts +5 -0
  253. package/tools/fetch-balance.js +4 -0
  254. package/tools/fetch-ohlcv.d.ts +11 -0
  255. package/tools/fetch-ohlcv.js +8 -0
  256. package/tools/fetch-open-orders.d.ts +7 -0
  257. package/tools/fetch-open-orders.js +4 -0
  258. package/tools/fetch-positions.d.ts +7 -0
  259. package/tools/fetch-positions.js +4 -0
  260. package/tools/fetch-ticker.d.ts +11 -0
  261. package/tools/fetch-ticker.js +5 -0
  262. package/tools/get-agent-profile.d.ts +4 -0
  263. package/tools/get-agent-profile.js +6 -0
  264. package/tools/get-analytics.d.ts +6 -0
  265. package/tools/get-analytics.js +7 -0
  266. package/tools/get-backtest.d.ts +12 -0
  267. package/tools/get-backtest.js +91 -0
  268. package/tools/get-basis.d.ts +7 -0
  269. package/tools/get-basis.js +7 -0
  270. package/tools/get-bracket-config.d.ts +11 -0
  271. package/tools/get-bracket-config.js +24 -0
  272. package/tools/get-cascade-risk.d.ts +7 -0
  273. package/tools/get-cascade-risk.js +8 -0
  274. package/tools/get-crypto-metrics.d.ts +18 -0
  275. package/tools/get-crypto-metrics.js +45 -0
  276. package/tools/get-cvd.d.ts +6 -0
  277. package/tools/get-cvd.js +6 -0
  278. package/tools/get-divergences.d.ts +6 -0
  279. package/tools/get-divergences.js +6 -0
  280. package/tools/get-funding-context.d.ts +6 -0
  281. package/tools/get-funding-context.js +16 -0
  282. package/tools/get-liquidation-levels.d.ts +7 -0
  283. package/tools/get-liquidation-levels.js +7 -0
  284. package/tools/get-liquidation-pulse.d.ts +9 -0
  285. package/tools/get-liquidation-pulse.js +22 -0
  286. package/tools/get-market-breadth.d.ts +6 -0
  287. package/tools/get-market-breadth.js +8 -0
  288. package/tools/get-market-intel.d.ts +19 -0
  289. package/tools/get-market-intel.js +116 -0
  290. package/tools/get-market-structure.d.ts +47 -0
  291. package/tools/get-market-structure.js +198 -0
  292. package/tools/get-my-mined-patterns.d.ts +20 -0
  293. package/tools/get-my-mined-patterns.js +61 -0
  294. package/tools/get-my-proposed-learnings.d.ts +20 -0
  295. package/tools/get-my-proposed-learnings.js +55 -0
  296. package/tools/get-my-recent-reviews.d.ts +22 -0
  297. package/tools/get-my-recent-reviews.js +66 -0
  298. package/tools/get-orderbook.d.ts +21 -0
  299. package/tools/get-orderbook.js +32 -0
  300. package/tools/get-pattern-scan.d.ts +7 -0
  301. package/tools/get-pattern-scan.js +8 -0
  302. package/tools/get-regime.d.ts +6 -0
  303. package/tools/get-regime.js +7 -0
  304. package/tools/get-relevant-learnings.d.ts +21 -0
  305. package/tools/get-relevant-learnings.js +65 -0
  306. package/tools/get-resting-liquidity.d.ts +6 -0
  307. package/tools/get-resting-liquidity.js +11 -0
  308. package/tools/get-risk-scenario.d.ts +29 -0
  309. package/tools/get-risk-scenario.js +47 -0
  310. package/tools/get-risk-summary.d.ts +51 -0
  311. package/tools/get-risk-summary.js +118 -0
  312. package/tools/get-sentiment.d.ts +4 -0
  313. package/tools/get-sentiment.js +6 -0
  314. package/tools/get-session-review.d.ts +7 -0
  315. package/tools/get-session-review.js +8 -0
  316. package/tools/get-setup-detail.d.ts +7 -0
  317. package/tools/get-setup-detail.js +303 -0
  318. package/tools/get-signals.d.ts +15 -0
  319. package/tools/get-signals.js +54 -0
  320. package/tools/get-sizing.d.ts +6 -0
  321. package/tools/get-sizing.js +6 -0
  322. package/tools/get-trade-feedback.d.ts +7 -0
  323. package/tools/get-trade-feedback.js +8 -0
  324. package/tools/get-trade-flow.d.ts +7 -0
  325. package/tools/get-trade-flow.js +7 -0
  326. package/tools/get-volume-analysis.d.ts +21 -0
  327. package/tools/get-volume-analysis.js +74 -0
  328. package/tools/get-volume-profile.d.ts +7 -0
  329. package/tools/get-volume-profile.js +7 -0
  330. package/tools/helpers.d.ts +25 -0
  331. package/tools/helpers.js +38 -0
  332. package/tools/intel-api.d.ts +14 -0
  333. package/tools/intel-api.js +52 -0
  334. package/tools/intel-cache.d.ts +25 -0
  335. package/tools/intel-cache.js +133 -0
  336. package/tools/list-strategies.d.ts +7 -0
  337. package/tools/list-strategies.js +6 -0
  338. package/tools/modify-stop.d.ts +17 -0
  339. package/tools/modify-stop.js +81 -0
  340. package/tools/modify-target.d.ts +17 -0
  341. package/tools/modify-target.js +71 -0
  342. package/tools/propose-learning.d.ts +22 -0
  343. package/tools/propose-learning.js +65 -0
  344. package/tools/query-review-outcomes.d.ts +30 -0
  345. package/tools/query-review-outcomes.js +64 -0
  346. package/tools/query-trades.d.ts +21 -0
  347. package/tools/query-trades.js +37 -0
  348. package/tools/record-position-reviews.d.ts +38 -0
  349. package/tools/record-position-reviews.js +147 -0
  350. package/tools/save-strategy.d.ts +16 -0
  351. package/tools/save-strategy.js +46 -0
  352. package/tools/scan-pairs.d.ts +18 -0
  353. package/tools/scan-pairs.js +220 -0
  354. package/tools/score-setup.d.ts +31 -0
  355. package/tools/score-setup.js +268 -0
  356. package/tools/set-bracket-requirement.d.ts +18 -0
  357. package/tools/set-bracket-requirement.js +81 -0
  358. package/tools/set-exchange-credentials.d.ts +25 -0
  359. package/tools/set-exchange-credentials.js +80 -0
  360. package/tools/set-trading-mode.d.ts +26 -0
  361. package/tools/set-trading-mode.js +135 -0
  362. package/tools/test-exchange-credentials.d.ts +16 -0
  363. package/tools/test-exchange-credentials.js +100 -0
  364. package/tools/toggle-strategy.d.ts +8 -0
  365. package/tools/toggle-strategy.js +8 -0
  366. package/trading-params-cache.d.ts +26 -0
  367. package/trading-params-cache.js +52 -0
  368. package/types.d.ts +110 -0
  369. package/types.js +7 -0
  370. package/util/plugin-paths.d.ts +3 -0
  371. package/util/plugin-paths.js +15 -0
@@ -0,0 +1,52 @@
1
+ import type { PositionMetadata } from '../simulator/types.js';
2
+ export interface PendingEntry {
3
+ /** Exchange-issued orderId — the lookup key when the WS fill arrives. */
4
+ orderId: string;
5
+ /** Canonical (un-suffixed) symbol. */
6
+ symbol: string;
7
+ /** Side the agent intended at create_order time. */
8
+ side: 'buy' | 'sell';
9
+ metadata: PositionMetadata;
10
+ createdAt: number;
11
+ /** Epoch ms after which this entry is dropped. */
12
+ expiresAt: number;
13
+ }
14
+ export type NowFn = () => number;
15
+ export interface PendingEntryStoreOptions {
16
+ pluginId?: string;
17
+ basePath?: string;
18
+ now?: NowFn;
19
+ ttlMs?: number;
20
+ }
21
+ export declare class PendingEntryStore {
22
+ private readonly filePath;
23
+ private readonly dir;
24
+ private readonly now;
25
+ private readonly ttlMs;
26
+ private entries;
27
+ constructor(opts?: PendingEntryStoreOptions);
28
+ /** Stash metadata until the WS fill for this orderId arrives. */
29
+ put(input: {
30
+ orderId: string;
31
+ symbol: string;
32
+ side: 'buy' | 'sell';
33
+ metadata: PositionMetadata;
34
+ }): void;
35
+ /** Retrieve metadata stashed for an orderId. Does NOT remove the entry —
36
+ * callers should call `consume(orderId)` to remove on first match, since
37
+ * multiple partial fills on the same limit order all share one orderId
38
+ * and the FIRST fill is the entry; subsequent are scale-ins of an already-
39
+ * registered position (state-store branch handles those). */
40
+ get(orderId: string): PendingEntry | undefined;
41
+ /** Atomic get + remove. Use this when you've already consumed the metadata
42
+ * to post an entry row and don't want it picked up again. */
43
+ consume(orderId: string): PendingEntry | undefined;
44
+ /** Remove an entry without retrieval (e.g. on cancel). */
45
+ drop(orderId: string): boolean;
46
+ size(): number;
47
+ getFilePath(): string;
48
+ private pruneExpired;
49
+ private loadOrInit;
50
+ private persist;
51
+ private validateFile;
52
+ }
@@ -0,0 +1,182 @@
1
+ // Pending-entry metadata cache.
2
+ //
3
+ // When the agent calls create_order with v2.10.0 metadata (thesis, setup_type,
4
+ // regime, scorecard_verdict, confluence_score, etc.) for a LIMIT order that
5
+ // doesn't fill immediately, the metadata is stashed here keyed by the
6
+ // exchange's orderId. When the WS-driven fill arrives later (could be seconds
7
+ // or hours), `onWsFillObserved` looks up the metadata and posts the entry /
8
+ // scale-in decision row with the original rationale intact.
9
+ //
10
+ // Without this cache, limit-order entries land in `position_entries` with
11
+ // thesis='(no thesis recorded)' / setupType='unknown' — useless for Phase 2
12
+ // outcome attribution and Phase 3 mining.
13
+ //
14
+ // Persistence: same atomic-rename pattern as bracket-ledger / position-state-
15
+ // store. File at ~/.openclaw/plugins/<pluginId>/pending-entry-metadata.json.
16
+ //
17
+ // TTL: entries auto-expire 24 hours after creation (limit orders rarely live
18
+ // longer; expired entries are pruned on every load + every write to keep the
19
+ // file bounded).
20
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
21
+ import { join } from 'node:path';
22
+ import { resolvePluginsBaseDir } from '../util/plugin-paths.js';
23
+ import { logger, formatError } from '../logger.js';
24
+ const TAG = 'pending-entry-metadata';
25
+ const DEFAULT_PLUGIN_ID = 'reefclaw-paper-trading';
26
+ const STATE_FILENAME = 'pending-entry-metadata.json';
27
+ const SCHEMA_VERSION = 1;
28
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
29
+ const defaultNow = () => Date.now();
30
+ export class PendingEntryStore {
31
+ filePath;
32
+ dir;
33
+ now;
34
+ ttlMs;
35
+ entries = new Map();
36
+ constructor(opts) {
37
+ const base = resolvePluginsBaseDir(opts?.basePath);
38
+ this.dir = join(base, opts?.pluginId ?? DEFAULT_PLUGIN_ID);
39
+ this.filePath = join(this.dir, STATE_FILENAME);
40
+ this.now = opts?.now ?? defaultNow;
41
+ this.ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
42
+ if (!existsSync(this.dir))
43
+ mkdirSync(this.dir, { recursive: true });
44
+ this.loadOrInit();
45
+ }
46
+ /** Stash metadata until the WS fill for this orderId arrives. */
47
+ put(input) {
48
+ const ts = this.now();
49
+ this.entries.set(input.orderId, {
50
+ orderId: input.orderId,
51
+ symbol: input.symbol,
52
+ side: input.side,
53
+ metadata: input.metadata,
54
+ createdAt: ts,
55
+ expiresAt: ts + this.ttlMs,
56
+ });
57
+ this.persist();
58
+ }
59
+ /** Retrieve metadata stashed for an orderId. Does NOT remove the entry —
60
+ * callers should call `consume(orderId)` to remove on first match, since
61
+ * multiple partial fills on the same limit order all share one orderId
62
+ * and the FIRST fill is the entry; subsequent are scale-ins of an already-
63
+ * registered position (state-store branch handles those). */
64
+ get(orderId) {
65
+ this.pruneExpired();
66
+ return this.entries.get(orderId);
67
+ }
68
+ /** Atomic get + remove. Use this when you've already consumed the metadata
69
+ * to post an entry row and don't want it picked up again. */
70
+ consume(orderId) {
71
+ this.pruneExpired();
72
+ const entry = this.entries.get(orderId);
73
+ if (!entry)
74
+ return undefined;
75
+ this.entries.delete(orderId);
76
+ this.persist();
77
+ return entry;
78
+ }
79
+ /** Remove an entry without retrieval (e.g. on cancel). */
80
+ drop(orderId) {
81
+ const existed = this.entries.delete(orderId);
82
+ if (existed)
83
+ this.persist();
84
+ return existed;
85
+ }
86
+ size() {
87
+ return this.entries.size;
88
+ }
89
+ getFilePath() {
90
+ return this.filePath;
91
+ }
92
+ // ---- Internals ----
93
+ pruneExpired() {
94
+ const ts = this.now();
95
+ let pruned = false;
96
+ for (const [orderId, entry] of this.entries) {
97
+ if (entry.expiresAt <= ts) {
98
+ this.entries.delete(orderId);
99
+ pruned = true;
100
+ }
101
+ }
102
+ if (pruned)
103
+ this.persist();
104
+ }
105
+ loadOrInit() {
106
+ if (!existsSync(this.filePath)) {
107
+ logger.info(TAG, `No state file at ${this.filePath}; starting empty`);
108
+ return;
109
+ }
110
+ let raw;
111
+ try {
112
+ raw = readFileSync(this.filePath, 'utf8');
113
+ }
114
+ catch (err) {
115
+ throw new Error(`pending-entry-metadata: failed to read ${this.filePath}: ${formatError(err)}`);
116
+ }
117
+ let parsed;
118
+ try {
119
+ parsed = JSON.parse(raw);
120
+ }
121
+ catch (err) {
122
+ throw new Error(`pending-entry-metadata: ${this.filePath} is not valid JSON: ${formatError(err)}`);
123
+ }
124
+ const file = this.validateFile(parsed);
125
+ const ts = this.now();
126
+ let kept = 0;
127
+ let dropped = 0;
128
+ for (const entry of file.entries) {
129
+ if (entry.expiresAt > ts) {
130
+ this.entries.set(entry.orderId, entry);
131
+ kept++;
132
+ }
133
+ else {
134
+ dropped++;
135
+ }
136
+ }
137
+ logger.info(TAG, `Loaded ${kept} pending entries from ${this.filePath}` +
138
+ (dropped > 0 ? ` (pruned ${dropped} expired)` : ''));
139
+ if (dropped > 0)
140
+ this.persist();
141
+ }
142
+ persist() {
143
+ const file = {
144
+ schemaVersion: SCHEMA_VERSION,
145
+ entries: Array.from(this.entries.values()),
146
+ };
147
+ const tempPath = `${this.filePath}.tmp`;
148
+ writeFileSync(tempPath, JSON.stringify(file, null, 2));
149
+ renameSync(tempPath, this.filePath);
150
+ }
151
+ validateFile(raw) {
152
+ if (!isRecord(raw))
153
+ throw new Error('pending-entry-metadata: file root is not an object');
154
+ if (raw.schemaVersion !== SCHEMA_VERSION) {
155
+ throw new Error(`pending-entry-metadata: unsupported schemaVersion ${String(raw.schemaVersion)} (expected ${SCHEMA_VERSION})`);
156
+ }
157
+ if (!Array.isArray(raw.entries))
158
+ throw new Error('pending-entry-metadata: entries is not an array');
159
+ const entries = [];
160
+ for (const e of raw.entries) {
161
+ if (!isRecord(e))
162
+ throw new Error('pending-entry-metadata: entry is not an object');
163
+ if (typeof e.orderId !== 'string' || e.orderId.length === 0) {
164
+ throw new Error('pending-entry-metadata: entry missing orderId');
165
+ }
166
+ if (typeof e.symbol !== 'string' || e.symbol.length === 0) {
167
+ throw new Error('pending-entry-metadata: entry missing symbol');
168
+ }
169
+ if (e.side !== 'buy' && e.side !== 'sell') {
170
+ throw new Error(`pending-entry-metadata: bad side ${String(e.side)}`);
171
+ }
172
+ if (typeof e.expiresAt !== 'number' || !Number.isFinite(e.expiresAt)) {
173
+ throw new Error('pending-entry-metadata: missing/invalid expiresAt');
174
+ }
175
+ entries.push(e);
176
+ }
177
+ return { schemaVersion: SCHEMA_VERSION, entries };
178
+ }
179
+ }
180
+ function isRecord(v) {
181
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
182
+ }
@@ -0,0 +1,98 @@
1
+ import type { PositionDecisionsClient } from './position-decisions-client.js';
2
+ import type { PositionStateStore } from '../live/position-state-store.js';
3
+ import type { CcxtOrder } from '../types.js';
4
+ import type { PositionMetadata } from '../simulator/types.js';
5
+ import type { PendingEntryStore } from './pending-entry-metadata.js';
6
+ export interface AutoCaptureContext {
7
+ decisionsClient?: PositionDecisionsClient;
8
+ stateStore?: PositionStateStore;
9
+ /** Stash for create_order metadata that arrives BEFORE the fill (limit
10
+ * orders that don't fill immediately). Looked up by exchangeOrderId when
11
+ * the WS fill arrives via `onWsFillObserved`. */
12
+ pendingEntries?: PendingEntryStore;
13
+ userId?: string;
14
+ /** When true, a reduce-only fill that takes a journaled position flat is
15
+ * auto-captured as an exact close (bracket SL/TP fill or other close that
16
+ * bypassed close_position). Default behaviour when unset is the legacy
17
+ * no-op (reduce-only fills ignored). Env kill-switch: RC_JOURNAL_CLOSE_ON_FILL
18
+ * (set 'off' to disable). See docs/CLAUDE/position-journal.md (close-bypass). */
19
+ closeOnReduceOnlyFill?: boolean;
20
+ /** Resolve the active trading book ('paper' | 'live') at capture time, read
21
+ * from the current adapter (so it follows a runtime reconnect). Tags the
22
+ * journal position row so paper and live entries can be segregated. */
23
+ resolveMode?: () => 'paper' | 'live';
24
+ }
25
+ export interface CreateOrderInputs {
26
+ symbol: string;
27
+ side: 'buy' | 'sell';
28
+ metadata?: PositionMetadata;
29
+ }
30
+ /** Called after a successful adapter.createOrder().
31
+ *
32
+ * For PR 1, we treat any non-zero `filled` quantity as an entry event and
33
+ * upsert the parent position + post the entry decision row. Limit orders that
34
+ * haven't filled yet are skipped (caller can call us again from the WS-ingest
35
+ * pipeline once they fill — that wiring is the follow-up).
36
+ *
37
+ * Always-fail-open: webapp errors are logged but never thrown.
38
+ */
39
+ export declare function onCreateOrderFilled(ctx: AutoCaptureContext, inputs: CreateOrderInputs, order: CcxtOrder): Promise<void>;
40
+ export interface ClosePositionInputs {
41
+ symbol: string;
42
+ closeReason: string;
43
+ closeAssessment: Record<string, unknown>;
44
+ /** Optional v2.10.0 inputs the agent may have passed alongside reason+assessment. */
45
+ scorecardVerdict?: string;
46
+ confluenceScore?: number;
47
+ regime?: string;
48
+ regimeConfidence?: number;
49
+ }
50
+ /** Called after a successful adapter.closePosition() with the v2.10.0 reason +
51
+ * assessment from close_position.ts. Looks up the cached position UUID, posts
52
+ * the close decision row, and drops the local state entry. */
53
+ export declare function onClosePositionFilled(ctx: AutoCaptureContext, inputs: ClosePositionInputs, order: CcxtOrder): Promise<void>;
54
+ /** WS-driven fill observed. Resolves the PR1 deferral that limit-order fills
55
+ * + scale-ins were silently dropped. Branches on three cases:
56
+ *
57
+ * - reduceOnly fill → ignored here (close path runs from close_position
58
+ * tool with v2.10.0 reason+assessment; bracket SL/TP
59
+ * fills also reduce-only and the legacy bracket
60
+ * handler covers those).
61
+ * - new-symbol entry → no state-store row → register the position +
62
+ * post entry decision row (uses pending metadata
63
+ * from the cache, or '(no thesis recorded)' fallback).
64
+ * - same-side fill on → state-store row exists with same side → SCALE-IN.
65
+ * existing position Posts a position_entries row with is_scale_in=true.
66
+ * Does NOT create a new positions row (single parent
67
+ * per open exposure).
68
+ * - opposite-side fill → state-store has same symbol but opposite side.
69
+ * on existing position Treated as a manual flatten that bypassed the
70
+ * close_position tool — skip (cleaner to require
71
+ * close_position for proper rationale capture).
72
+ *
73
+ * Dedup: when create_order's market-order path already captured an entry
74
+ * synchronously, the state-store entry's `openedFromExchangeTradeId` matches
75
+ * this fill's orderId. We detect that and skip to avoid double-posting. */
76
+ export interface WsFillInputs {
77
+ symbol: string;
78
+ side: 'buy' | 'sell';
79
+ exchangeOrderId: string;
80
+ exchangeTradeId: string;
81
+ fillPrice: number;
82
+ fillSize: number;
83
+ reduceOnly: boolean;
84
+ /** Binance-exact realised PnL on this specific fill (`o.rp`). Used to sum the
85
+ * position's realised PnL when a reduce-only fill takes it flat. */
86
+ realizedPnl?: number;
87
+ /** Client order id of the fill. Lets the reduce-only close handler tag a
88
+ * bracket SL/TP fill (cid is a bracket cid) vs an external reduce-only close. */
89
+ clientOrderId?: string;
90
+ /** Timestamp from the exchange. Falls back to Date.now() if not provided. */
91
+ exchangeTimeMs?: number;
92
+ }
93
+ export declare function onWsFillObserved(ctx: AutoCaptureContext, fill: WsFillInputs): Promise<void>;
94
+ /** Pinned-plan subset persisted to position_entries.metadata (free-form JSONB).
95
+ * Keys are snake_case per the canonical-JSONB-key rule (CLAUDE.md learning-loop:
96
+ * a camelCase mismatch previously broke learning matching). Returns undefined
97
+ * when neither field is present so we never write an empty object. */
98
+ export declare function buildEntryPlanMetadata(md: PositionMetadata | undefined): Record<string, unknown> | undefined;