@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,80 @@
1
+ // set_exchange_credentials — operator-only plugin tool.
2
+ //
3
+ // Writes Binance API key + secret to ~/.reefclaw/plugin-config.json and
4
+ // triggers an adapter reconnect if the current mode is live. The agent
5
+ // should NOT call this tool; it's registered for the skill's operator RPC
6
+ // path only. PR2 will enforce that via a skill-side scope gate (operator.write).
7
+ //
8
+ // Security:
9
+ // - The secret is NEVER logged. Only a redacted fingerprint (first 4 + last 2
10
+ // chars of the apiKey) goes to logs / audit.
11
+ // - If the current mode is PAPER, credentials are written but NOT activated —
12
+ // the operator must explicitly call set_trading_mode to move up the ladder.
13
+ import { updatePluginConfig, redactCredential } from '../config/plugin-config-io.js';
14
+ import { logger } from '../logger.js';
15
+ const TAG = 'set-exchange-credentials';
16
+ function validate(args) {
17
+ if (typeof args.apiKey !== 'string' || args.apiKey.trim().length < 8) {
18
+ return 'apiKey must be a string of at least 8 characters';
19
+ }
20
+ if (typeof args.secret !== 'string' || args.secret.trim().length < 8) {
21
+ return 'secret must be a string of at least 8 characters';
22
+ }
23
+ if (args.testnet !== undefined && typeof args.testnet !== 'boolean') {
24
+ return 'testnet must be a boolean when provided';
25
+ }
26
+ return null;
27
+ }
28
+ export async function setExchangeCredentialsTool(args, deps) {
29
+ const validationError = validate(args);
30
+ if (validationError) {
31
+ return {
32
+ ok: false,
33
+ message: validationError,
34
+ fingerprint: '(not-set)',
35
+ reconnected: false,
36
+ mode: deps.runtime.mode,
37
+ readiness: deps.runtime.adapter.readiness,
38
+ };
39
+ }
40
+ const exchange = {
41
+ apiKey: args.apiKey.trim(),
42
+ secret: args.secret.trim(),
43
+ testnet: args.testnet ?? false,
44
+ };
45
+ const fingerprint = redactCredential(exchange.apiKey);
46
+ try {
47
+ updatePluginConfig({ exchange }, deps.configPath);
48
+ }
49
+ catch (err) {
50
+ const msg = err instanceof Error ? err.message : String(err);
51
+ logger.error(TAG, `Failed to write credentials: ${msg}`);
52
+ return {
53
+ ok: false,
54
+ message: `Failed to persist credentials: ${msg}`,
55
+ fingerprint,
56
+ reconnected: false,
57
+ mode: deps.runtime.mode,
58
+ readiness: deps.runtime.adapter.readiness,
59
+ };
60
+ }
61
+ logger.info(TAG, `Stored credentials (fingerprint: ${fingerprint}, testnet: ${exchange.testnet})`);
62
+ // Only reconnect if we're already in a live mode — if the operator is still
63
+ // in PAPER, activating the keys is a separate deliberate step via
64
+ // set_trading_mode. This prevents accidentally going live by saving keys.
65
+ let reconnected = false;
66
+ if (deps.runtime.mode !== 'PAPER') {
67
+ await deps.runtime.reconnect({ mode: deps.runtime.mode, exchange }, { adapterDeps: deps.adapterDeps });
68
+ reconnected = true;
69
+ }
70
+ return {
71
+ ok: true,
72
+ message: reconnected
73
+ ? `Credentials updated and ${deps.runtime.mode} adapter reconnected`
74
+ : `Credentials stored (mode is PAPER — run set_trading_mode SHADOW to activate)`,
75
+ fingerprint,
76
+ reconnected,
77
+ mode: deps.runtime.mode,
78
+ readiness: deps.runtime.adapter.readiness,
79
+ };
80
+ }
@@ -0,0 +1,26 @@
1
+ import type { PluginRuntime } from '../onboarding/runtime.js';
2
+ import type { IExchangeAdapter } from '../exchange-adapter.js';
3
+ import type { TradingMode } from '../types.js';
4
+ export interface SetTradingModeArgs {
5
+ mode: TradingMode;
6
+ /** Optional acknowledgment flag for going live. The skill layer will set
7
+ * this explicitly when the operator confirms the elevated confirmation. */
8
+ acknowledged?: boolean;
9
+ }
10
+ export interface SetTradingModeResult {
11
+ ok: boolean;
12
+ message: string;
13
+ previousMode: TradingMode;
14
+ mode: TradingMode;
15
+ readiness: string;
16
+ reason?: string;
17
+ }
18
+ export interface SetTradingModeDeps {
19
+ runtime: PluginRuntime;
20
+ adapterDeps: {
21
+ adapter: IExchangeAdapter;
22
+ };
23
+ /** Override the config file path — tests use this. */
24
+ configPath?: string;
25
+ }
26
+ export declare function setTradingModeTool(args: SetTradingModeArgs, deps: SetTradingModeDeps): Promise<SetTradingModeResult>;
@@ -0,0 +1,135 @@
1
+ // set_trading_mode — operator-only plugin tool.
2
+ //
3
+ // Moves the plugin between PAPER / MICRO_LIVE / LIVE, enforcing
4
+ // the one-rung-at-a-time ladder. Requires valid exchange credentials for
5
+ // any non-PAPER target. Rebuilds the adapter via runtime.reconnect().
6
+ //
7
+ // The agent should NOT call this tool; PR2 will enforce operator-only scope
8
+ // in the skill. PR1 leaves the plugin-side handler functional but unguarded
9
+ // because the caller chain is currently controlled end-to-end by the skill.
10
+ import { readPluginConfig } from '../config/plugin-config-io.js';
11
+ import { validateModeTransition, modeRequiresCredentials } from '../onboarding/mode-ladder.js';
12
+ import { logger } from '../logger.js';
13
+ import { recordModeTransition } from '../audit/mode-transition-audit.js';
14
+ const TAG = 'set-trading-mode';
15
+ const VALID_MODES = ['PAPER', 'MICRO_LIVE', 'LIVE'];
16
+ export async function setTradingModeTool(args, deps) {
17
+ const previousMode = deps.runtime.mode;
18
+ if (!args || typeof args.mode !== 'string' || !VALID_MODES.includes(args.mode)) {
19
+ return {
20
+ ok: false,
21
+ message: `Invalid mode. Must be one of: ${VALID_MODES.join(', ')}`,
22
+ previousMode,
23
+ mode: previousMode,
24
+ readiness: deps.runtime.adapter.readiness,
25
+ };
26
+ }
27
+ const target = args.mode;
28
+ const acknowledged = args.acknowledged === true;
29
+ // 1. Ladder check.
30
+ const ladder = validateModeTransition(previousMode, target);
31
+ if (!ladder.allowed) {
32
+ recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'ladder' });
33
+ return {
34
+ ok: false,
35
+ message: ladder.reason ?? 'Mode transition not allowed',
36
+ previousMode,
37
+ mode: previousMode,
38
+ readiness: deps.runtime.adapter.readiness,
39
+ reason: 'ladder',
40
+ };
41
+ }
42
+ // 2. If already in the target mode, no-op (no audit entry — nothing happened).
43
+ if (previousMode === target) {
44
+ return {
45
+ ok: true,
46
+ message: `Already in ${target} mode`,
47
+ previousMode,
48
+ mode: previousMode,
49
+ readiness: deps.runtime.adapter.readiness,
50
+ };
51
+ }
52
+ // 3. Credential requirement check.
53
+ let exchange = null;
54
+ if (modeRequiresCredentials(target)) {
55
+ try {
56
+ const cfg = readPluginConfig(deps.configPath);
57
+ if (cfg.exchange?.apiKey && cfg.exchange?.secret) {
58
+ exchange = {
59
+ apiKey: cfg.exchange.apiKey,
60
+ secret: cfg.exchange.secret,
61
+ testnet: cfg.exchange.testnet ?? false,
62
+ };
63
+ }
64
+ }
65
+ catch (err) {
66
+ const msg = err instanceof Error ? err.message : String(err);
67
+ recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'config_read_error' });
68
+ return {
69
+ ok: false,
70
+ message: `Cannot read config: ${msg}`,
71
+ previousMode,
72
+ mode: previousMode,
73
+ readiness: deps.runtime.adapter.readiness,
74
+ reason: 'config_read_error',
75
+ };
76
+ }
77
+ if (!exchange) {
78
+ recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'missing_credentials' });
79
+ return {
80
+ ok: false,
81
+ message: `${target} mode requires exchange credentials. Run set_exchange_credentials first.`,
82
+ previousMode,
83
+ mode: previousMode,
84
+ readiness: deps.runtime.adapter.readiness,
85
+ reason: 'missing_credentials',
86
+ };
87
+ }
88
+ }
89
+ // 4. Elevated confirmation for full LIVE.
90
+ if (target === 'LIVE' && !acknowledged) {
91
+ recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'acknowledgment_required' });
92
+ return {
93
+ ok: false,
94
+ message: `Transition to LIVE requires explicit acknowledgment (acknowledged: true)`,
95
+ previousMode,
96
+ mode: previousMode,
97
+ readiness: deps.runtime.adapter.readiness,
98
+ reason: 'acknowledgment_required',
99
+ };
100
+ }
101
+ // 5. Persist the new mode.
102
+ try {
103
+ const { updatePluginConfig } = await import('../config/plugin-config-io.js');
104
+ updatePluginConfig({ tradingMode: target }, deps.configPath);
105
+ }
106
+ catch (err) {
107
+ const msg = err instanceof Error ? err.message : String(err);
108
+ recordModeTransition({ previousMode, targetMode: target, acknowledged, ok: false, reason: 'config_write_error' });
109
+ return {
110
+ ok: false,
111
+ message: `Failed to persist mode: ${msg}`,
112
+ previousMode,
113
+ mode: previousMode,
114
+ readiness: deps.runtime.adapter.readiness,
115
+ reason: 'config_write_error',
116
+ };
117
+ }
118
+ // 6. Swap the adapter.
119
+ await deps.runtime.reconnect({ mode: target, exchange }, { adapterDeps: deps.adapterDeps });
120
+ logger.info(TAG, `Transitioned ${previousMode} → ${target}`);
121
+ recordModeTransition({
122
+ previousMode,
123
+ targetMode: target,
124
+ acknowledged,
125
+ ok: true,
126
+ testnet: exchange?.testnet,
127
+ });
128
+ return {
129
+ ok: true,
130
+ message: `Trading mode changed from ${previousMode} to ${target}`,
131
+ previousMode,
132
+ mode: deps.runtime.mode,
133
+ readiness: deps.runtime.adapter.readiness,
134
+ };
135
+ }
@@ -0,0 +1,16 @@
1
+ export interface TestExchangeCredentialsArgs {
2
+ apiKey: string;
3
+ secret: string;
4
+ testnet?: boolean;
5
+ }
6
+ export interface TestExchangeCredentialsResult {
7
+ ok: boolean;
8
+ message: string;
9
+ fingerprint: string;
10
+ canReadBalance: boolean;
11
+ canReadPositions: boolean;
12
+ balanceUSDT: number | null;
13
+ testnet: boolean;
14
+ errors: string[];
15
+ }
16
+ export declare function testExchangeCredentialsTool(args: TestExchangeCredentialsArgs): Promise<TestExchangeCredentialsResult>;
@@ -0,0 +1,100 @@
1
+ // test_exchange_credentials — operator-only plugin tool.
2
+ //
3
+ // Takes a candidate Binance API key + secret and verifies them with a
4
+ // transient, read-only call to the exchange (fetchBalance via
5
+ // validatePermissions). Nothing is written to disk. Nothing is retained
6
+ // after the call returns. This is the "pre-flight" check that lets the
7
+ // dashboard say "yes these keys work, you can now click Store" before
8
+ // the operator commits the credentials via set_exchange_credentials.
9
+ //
10
+ // Security:
11
+ // - Builds a throwaway BinancePrivateApi instance scoped to one call.
12
+ // - Never logs the secret — only a redacted fingerprint.
13
+ // - Does not touch plugin-config.json at all.
14
+ import { BinancePrivateApi } from '../ccxt/binance-private.js';
15
+ import { redactCredential } from '../config/plugin-config-io.js';
16
+ import { logger, formatError } from '../logger.js';
17
+ const TAG = 'test-exchange-credentials';
18
+ function validate(args) {
19
+ if (typeof args.apiKey !== 'string' || args.apiKey.trim().length < 8) {
20
+ return 'apiKey must be a string of at least 8 characters';
21
+ }
22
+ if (typeof args.secret !== 'string' || args.secret.trim().length < 8) {
23
+ return 'secret must be a string of at least 8 characters';
24
+ }
25
+ return null;
26
+ }
27
+ export async function testExchangeCredentialsTool(args) {
28
+ const fingerprint = redactCredential(args.apiKey);
29
+ const err = validate(args);
30
+ if (err) {
31
+ return {
32
+ ok: false,
33
+ message: err,
34
+ fingerprint,
35
+ canReadBalance: false,
36
+ canReadPositions: false,
37
+ balanceUSDT: null,
38
+ testnet: args.testnet ?? false,
39
+ errors: [err],
40
+ };
41
+ }
42
+ const config = {
43
+ apiKey: args.apiKey.trim(),
44
+ secret: args.secret.trim(),
45
+ testnet: args.testnet ?? false,
46
+ };
47
+ logger.info(TAG, `Testing credentials (fingerprint: ${fingerprint}, testnet: ${config.testnet})`);
48
+ let api;
49
+ try {
50
+ api = new BinancePrivateApi(config);
51
+ }
52
+ catch (e) {
53
+ const msg = formatError(e);
54
+ return {
55
+ ok: false,
56
+ message: `Could not initialize exchange client: ${msg}`,
57
+ fingerprint,
58
+ canReadBalance: false,
59
+ canReadPositions: false,
60
+ balanceUSDT: null,
61
+ testnet: config.testnet ?? false,
62
+ errors: [msg],
63
+ };
64
+ }
65
+ const perms = await api.validatePermissions();
66
+ // Opportunistically read the balance for the result payload — if
67
+ // validatePermissions already proved it works, this is a no-op second
68
+ // call that gives the user confidence ("connected, $123.45").
69
+ // Supports both USDT and USDC (Binance USDⓈ-M settles in either).
70
+ let balanceUSDT = null;
71
+ if (perms.canReadBalance) {
72
+ try {
73
+ const bal = await api.fetchBalance();
74
+ if (bal && typeof bal.total === 'object') {
75
+ const usdt = bal.total.USDT ?? 0;
76
+ const usdc = bal.total.USDC ?? 0;
77
+ const sum = usdt + usdc;
78
+ if (Number.isFinite(sum) && sum > 0)
79
+ balanceUSDT = sum;
80
+ }
81
+ }
82
+ catch {
83
+ // Ignore — we already know canReadBalance is true, so this is cosmetic.
84
+ }
85
+ }
86
+ const ok = perms.canReadBalance && perms.canReadPositions;
87
+ const message = ok
88
+ ? `Connection verified — USDT balance: ${balanceUSDT != null ? balanceUSDT.toFixed(2) : 'unknown'}`
89
+ : `API permission check failed: ${perms.errors.join('; ')}`;
90
+ return {
91
+ ok,
92
+ message,
93
+ fingerprint,
94
+ canReadBalance: perms.canReadBalance,
95
+ canReadPositions: perms.canReadPositions,
96
+ balanceUSDT,
97
+ testnet: config.testnet ?? false,
98
+ errors: perms.errors,
99
+ };
100
+ }
@@ -0,0 +1,8 @@
1
+ import type { IntelApiDeps } from './intel-api.js';
2
+ export interface ToggleStrategyArgs {
3
+ name: string;
4
+ active: boolean;
5
+ }
6
+ export declare function toggleStrategyTool(args: ToggleStrategyArgs, deps: IntelApiDeps): Promise<Record<string, unknown> | {
7
+ error: string;
8
+ }>;
@@ -0,0 +1,8 @@
1
+ // Tool: toggle_strategy — activate or deactivate a strategy.
2
+ import { fetchIntelApi } from './intel-api.js';
3
+ export async function toggleStrategyTool(args, deps) {
4
+ return fetchIntelApi(`/api/strategies/${encodeURIComponent(args.name)}/toggle`, deps, {
5
+ method: 'PUT',
6
+ body: { active: args.active },
7
+ });
8
+ }
@@ -0,0 +1,26 @@
1
+ import { type IntelApiDeps } from './tools/intel-api.js';
2
+ export interface TradingParameters {
3
+ regimeConfidenceFloor: number;
4
+ signalConfluenceThreshold: number;
5
+ scorecardStrongGoThreshold: number;
6
+ scorecardGoThreshold: number;
7
+ scorecardMarginalThreshold: number;
8
+ confidencePenaltyThreshold: number;
9
+ unprovenSetupScore: number;
10
+ rrWarningThreshold: number;
11
+ lossStreakQuarterSize: number;
12
+ lossStreakHalfSize: number;
13
+ maxPositionSize: number;
14
+ maxOpenPositions: number;
15
+ maxGrossExposure: number;
16
+ maxPerTradeLoss: number;
17
+ drawdownYellow: number;
18
+ drawdownOrange: number;
19
+ drawdownRed: number;
20
+ }
21
+ /** Get trading parameters (from cache or fetch). Returns defaults on failure. */
22
+ export declare function getTradingParams(deps: IntelApiDeps): Promise<TradingParameters>;
23
+ /** Get cached params synchronously (for pure functions like preTradeRiskCheck). */
24
+ export declare function getCachedTradingParams(): TradingParameters;
25
+ /** Force-refresh the cache (call on plugin init). */
26
+ export declare function refreshTradingParams(deps: IntelApiDeps): Promise<void>;
@@ -0,0 +1,52 @@
1
+ // Trading parameters cache — fetches from intelligence API and caches locally.
2
+ // Plugin tools (score-setup, pre-trade-check) read from this cache.
3
+ import { fetchIntelApi } from './tools/intel-api.js';
4
+ // Hardcoded defaults (same as intelligence/src/trading-params/types.ts)
5
+ const DEFAULTS = {
6
+ regimeConfidenceFloor: 0.35,
7
+ signalConfluenceThreshold: 0.8,
8
+ scorecardStrongGoThreshold: 7.5,
9
+ scorecardGoThreshold: 5.5,
10
+ scorecardMarginalThreshold: 4.0,
11
+ confidencePenaltyThreshold: 0.35,
12
+ unprovenSetupScore: 5,
13
+ rrWarningThreshold: 1.5,
14
+ lossStreakQuarterSize: 5,
15
+ lossStreakHalfSize: 3,
16
+ maxPositionSize: 10_000,
17
+ maxOpenPositions: 5,
18
+ maxGrossExposure: 1.5,
19
+ maxPerTradeLoss: 500,
20
+ drawdownYellow: -0.01,
21
+ drawdownOrange: -0.02,
22
+ drawdownRed: -0.025,
23
+ };
24
+ let cached = { ...DEFAULTS };
25
+ let cacheExpiry = 0;
26
+ const CACHE_TTL = 60_000; // 60s
27
+ /** Get trading parameters (from cache or fetch). Returns defaults on failure. */
28
+ export async function getTradingParams(deps) {
29
+ const now = Date.now();
30
+ if (now < cacheExpiry)
31
+ return cached;
32
+ try {
33
+ const res = await fetchIntelApi('/api/trading-params', deps);
34
+ if (!('error' in res) && res.params) {
35
+ cached = { ...DEFAULTS, ...res.params };
36
+ cacheExpiry = now + CACHE_TTL;
37
+ }
38
+ }
39
+ catch {
40
+ // Silently use cached/defaults on failure
41
+ }
42
+ return cached;
43
+ }
44
+ /** Get cached params synchronously (for pure functions like preTradeRiskCheck). */
45
+ export function getCachedTradingParams() {
46
+ return cached;
47
+ }
48
+ /** Force-refresh the cache (call on plugin init). */
49
+ export async function refreshTradingParams(deps) {
50
+ cacheExpiry = 0; // force refresh
51
+ await getTradingParams(deps);
52
+ }
package/types.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ import type { RealizationRule } from './simulator/types.js';
2
+ /** CCXT order — matches skill/src/gateway/event-parser.ts CcxtOrder */
3
+ export interface CcxtOrder {
4
+ id: string;
5
+ symbol: string;
6
+ side: 'buy' | 'sell';
7
+ type: 'market' | 'limit';
8
+ status: 'open' | 'closed' | 'canceled';
9
+ amount: number;
10
+ filled: number;
11
+ remaining: number;
12
+ average: number | null;
13
+ price: number | null;
14
+ cost: number;
15
+ fee: {
16
+ cost: number;
17
+ currency: string;
18
+ };
19
+ timestamp: number;
20
+ datetime: string;
21
+ timeInForce: string;
22
+ /** Client-supplied order id (Binance: `newClientOrderId`). Present on exchange
23
+ * responses; used by bracket reconciliation to identify our orders on restart. */
24
+ clientOrderId?: string;
25
+ /** Raw upstream exchange response preserved verbatim. Binance algo endpoint
26
+ * carries `orderType` (STOP_MARKET / TAKE_PROFIT_MARKET / TRAILING_STOP_MARKET)
27
+ * and `algoType` (CONDITIONAL) fields that are authoritative for role
28
+ * classification — more reliable than our CID-shape inference since they
29
+ * also cover externally-placed stops (manual operator SL via Binance UI).
30
+ * Top-level `type` stays narrow ('market'|'limit') for TS type safety;
31
+ * consumers that need order subtype read `info.orderType`. */
32
+ info?: Record<string, unknown>;
33
+ }
34
+ /** CCXT ticker — matches skill/src/gateway/event-parser.ts CcxtTicker */
35
+ export interface CcxtTicker {
36
+ symbol: string;
37
+ last: number;
38
+ bid: number;
39
+ ask: number;
40
+ baseVolume: number;
41
+ quoteVolume: number;
42
+ change: number;
43
+ percentage: number;
44
+ timestamp: number;
45
+ datetime: string;
46
+ }
47
+ /** CCXT balance — compatible with skill/src/gateway/event-parser.ts CcxtBalance (stricter: named fields are required here, optional there) */
48
+ export interface CcxtBalance {
49
+ [currency: string]: {
50
+ free: number;
51
+ used: number;
52
+ total: number;
53
+ } | unknown;
54
+ free: Record<string, number>;
55
+ used: Record<string, number>;
56
+ total: Record<string, number>;
57
+ }
58
+ /** CCXT position — returned by fetch_positions */
59
+ export interface CcxtPosition {
60
+ symbol: string;
61
+ side: 'long' | 'short';
62
+ contracts: number;
63
+ contractSize: number;
64
+ entryPrice: number;
65
+ markPrice: number;
66
+ notional: number;
67
+ unrealizedPnl: number;
68
+ percentage: number;
69
+ timestamp: number;
70
+ datetime: string;
71
+ setupType?: string;
72
+ thesis?: string;
73
+ stopPrice?: number;
74
+ targetPrice?: number;
75
+ regime?: string;
76
+ regimeConfidence?: number;
77
+ scorecardVerdict?: string;
78
+ confluenceScore?: number;
79
+ originalStopPrice?: number;
80
+ mfePeakPrice?: number;
81
+ mfeR?: number;
82
+ giveBackRatio?: number;
83
+ invalidationPrice?: number;
84
+ realizationRule?: RealizationRule;
85
+ /** Derived: has the pinned invalidation price been reached at the current
86
+ * mark? When false, the agent's own plan says the trade is still valid (a
87
+ * salient anti-re-litigation cue). Undefined when no invalidation was pinned. */
88
+ invalidationHit?: boolean;
89
+ bracket?: {
90
+ slPrice?: number;
91
+ tpPrice?: number;
92
+ state: string;
93
+ };
94
+ }
95
+ /** CCXT OHLCV tuple: [timestamp, open, high, low, close, volume] */
96
+ export type CcxtOHLCV = [number, number, number, number, number, number];
97
+ /** Trading mode for the plugin. */
98
+ export type TradingMode = 'PAPER' | 'SHADOW' | 'MICRO_LIVE' | 'LIVE';
99
+ export interface PluginConfig {
100
+ startingBalance: number;
101
+ symbol: string;
102
+ quoteCurrency: string;
103
+ }
104
+ export declare const DEFAULT_CONFIG: PluginConfig;
105
+ /** Exchange credentials for shadow/live modes. Read from openclaw.json. */
106
+ export interface ExchangeConfig {
107
+ apiKey: string;
108
+ secret: string;
109
+ testnet?: boolean;
110
+ }
package/types.js ADDED
@@ -0,0 +1,7 @@
1
+ // Types for the ReefClaw Paper Trading plugin.
2
+ // CCXT-compatible types that match what the GatewayProvider's event-parser expects.
3
+ export const DEFAULT_CONFIG = {
4
+ startingBalance: 10000,
5
+ symbol: 'BTC/USDT',
6
+ quoteCurrency: 'USDT',
7
+ };
@@ -0,0 +1,3 @@
1
+ /** Resolve the base directory where plugin state lives. The trailing
2
+ * `plugins` segment is included so callers can append `<pluginId>`. */
3
+ export declare function resolvePluginsBaseDir(explicitBase?: string): string;
@@ -0,0 +1,15 @@
1
+ // Centralises the "where do plugin state files live" decision so test setup
2
+ // can redirect every state writer with a single OPENCLAW_HOME env var.
3
+ // Honors precedence: explicit override > OPENCLAW_HOME env > $HOME/.openclaw.
4
+ import { homedir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ /** Resolve the base directory where plugin state lives. The trailing
7
+ * `plugins` segment is included so callers can append `<pluginId>`. */
8
+ export function resolvePluginsBaseDir(explicitBase) {
9
+ if (explicitBase)
10
+ return explicitBase;
11
+ const envBase = process.env.OPENCLAW_HOME;
12
+ if (envBase)
13
+ return join(envBase, 'plugins');
14
+ return join(homedir(), '.openclaw', 'plugins');
15
+ }