@raintree-technology/perps 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 (316) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/dist/adapters/aevo.d.ts +64 -0
  5. package/dist/adapters/aevo.js +899 -0
  6. package/dist/adapters/certification.d.ts +33 -0
  7. package/dist/adapters/certification.js +99 -0
  8. package/dist/adapters/decibel/order-manager.d.ts +45 -0
  9. package/dist/adapters/decibel/order-manager.js +140 -0
  10. package/dist/adapters/decibel/rest-client.d.ts +176 -0
  11. package/dist/adapters/decibel/rest-client.js +155 -0
  12. package/dist/adapters/decibel/ws-feed.d.ts +28 -0
  13. package/dist/adapters/decibel/ws-feed.js +166 -0
  14. package/dist/adapters/decibel.d.ts +108 -0
  15. package/dist/adapters/decibel.js +1377 -0
  16. package/dist/adapters/hyperliquid.d.ts +63 -0
  17. package/dist/adapters/hyperliquid.js +797 -0
  18. package/dist/adapters/index.d.ts +11 -0
  19. package/dist/adapters/index.js +12 -0
  20. package/dist/adapters/interface.d.ts +310 -0
  21. package/dist/adapters/interface.js +15 -0
  22. package/dist/adapters/orderly.d.ts +70 -0
  23. package/dist/adapters/orderly.js +936 -0
  24. package/dist/adapters/paradex.d.ts +69 -0
  25. package/dist/adapters/paradex.js +862 -0
  26. package/dist/adapters/utils.d.ts +17 -0
  27. package/dist/adapters/utils.js +122 -0
  28. package/dist/cli/command-metadata.d.ts +2 -0
  29. package/dist/cli/command-metadata.js +44 -0
  30. package/dist/cli/context.d.ts +14 -0
  31. package/dist/cli/context.js +59 -0
  32. package/dist/cli/experience.d.ts +48 -0
  33. package/dist/cli/experience.js +243 -0
  34. package/dist/cli/ink/app/AppShell.d.ts +12 -0
  35. package/dist/cli/ink/app/AppShell.js +32 -0
  36. package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
  37. package/dist/cli/ink/app/MetricStrip.js +14 -0
  38. package/dist/cli/ink/app/Panel.d.ts +9 -0
  39. package/dist/cli/ink/app/Panel.js +7 -0
  40. package/dist/cli/ink/app/ascii.d.ts +2 -0
  41. package/dist/cli/ink/app/ascii.js +46 -0
  42. package/dist/cli/ink/app/index.d.ts +5 -0
  43. package/dist/cli/ink/app/index.js +4 -0
  44. package/dist/cli/ink/app/types.d.ts +15 -0
  45. package/dist/cli/ink/app/types.js +1 -0
  46. package/dist/cli/ink/components/PnL.d.ts +12 -0
  47. package/dist/cli/ink/components/PnL.js +23 -0
  48. package/dist/cli/ink/components/Spinner.d.ts +13 -0
  49. package/dist/cli/ink/components/Spinner.js +13 -0
  50. package/dist/cli/ink/components/Table.d.ts +14 -0
  51. package/dist/cli/ink/components/Table.js +42 -0
  52. package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
  53. package/dist/cli/ink/components/WatchHeader.js +18 -0
  54. package/dist/cli/ink/components/index.d.ts +4 -0
  55. package/dist/cli/ink/components/index.js +4 -0
  56. package/dist/cli/ink/index.d.ts +4 -0
  57. package/dist/cli/ink/index.js +4 -0
  58. package/dist/cli/ink/render.d.ts +12 -0
  59. package/dist/cli/ink/render.js +21 -0
  60. package/dist/cli/ink/theme.d.ts +29 -0
  61. package/dist/cli/ink/theme.js +40 -0
  62. package/dist/cli/network-defaults.d.ts +10 -0
  63. package/dist/cli/network-defaults.js +35 -0
  64. package/dist/cli/output.d.ts +11 -0
  65. package/dist/cli/output.js +115 -0
  66. package/dist/cli/program.d.ts +18 -0
  67. package/dist/cli/program.js +164 -0
  68. package/dist/cli/watch.d.ts +19 -0
  69. package/dist/cli/watch.js +35 -0
  70. package/dist/client/index.d.ts +55 -0
  71. package/dist/client/index.js +157 -0
  72. package/dist/commands/account/add.d.ts +2 -0
  73. package/dist/commands/account/add.js +510 -0
  74. package/dist/commands/account/balances-simple.d.ts +5 -0
  75. package/dist/commands/account/balances-simple.js +63 -0
  76. package/dist/commands/account/index.d.ts +2 -0
  77. package/dist/commands/account/index.js +17 -0
  78. package/dist/commands/account/ls.d.ts +2 -0
  79. package/dist/commands/account/ls.js +95 -0
  80. package/dist/commands/account/positions-simple.d.ts +5 -0
  81. package/dist/commands/account/positions-simple.js +77 -0
  82. package/dist/commands/account/remove.d.ts +2 -0
  83. package/dist/commands/account/remove.js +47 -0
  84. package/dist/commands/account/set-default.d.ts +2 -0
  85. package/dist/commands/account/set-default.js +47 -0
  86. package/dist/commands/agent/index.d.ts +2 -0
  87. package/dist/commands/agent/index.js +126 -0
  88. package/dist/commands/arb/alert.d.ts +6 -0
  89. package/dist/commands/arb/alert.js +88 -0
  90. package/dist/commands/arb/basis-execute.d.ts +6 -0
  91. package/dist/commands/arb/basis-execute.js +332 -0
  92. package/dist/commands/arb/basis.d.ts +6 -0
  93. package/dist/commands/arb/basis.js +181 -0
  94. package/dist/commands/arb/compare.d.ts +6 -0
  95. package/dist/commands/arb/compare.js +216 -0
  96. package/dist/commands/arb/execute.d.ts +6 -0
  97. package/dist/commands/arb/execute.js +467 -0
  98. package/dist/commands/arb/funding.d.ts +6 -0
  99. package/dist/commands/arb/funding.js +201 -0
  100. package/dist/commands/arb/history.d.ts +6 -0
  101. package/dist/commands/arb/history.js +153 -0
  102. package/dist/commands/arb/index.d.ts +6 -0
  103. package/dist/commands/arb/index.js +29 -0
  104. package/dist/commands/arb/positions.d.ts +6 -0
  105. package/dist/commands/arb/positions.js +158 -0
  106. package/dist/commands/arb/spread.d.ts +6 -0
  107. package/dist/commands/arb/spread.js +253 -0
  108. package/dist/commands/arb/track.d.ts +6 -0
  109. package/dist/commands/arb/track.js +259 -0
  110. package/dist/commands/asset/book-simple.d.ts +5 -0
  111. package/dist/commands/asset/book-simple.js +77 -0
  112. package/dist/commands/asset/index.d.ts +2 -0
  113. package/dist/commands/asset/index.js +5 -0
  114. package/dist/commands/completion.d.ts +2 -0
  115. package/dist/commands/completion.js +161 -0
  116. package/dist/commands/config/index.d.ts +5 -0
  117. package/dist/commands/config/index.js +109 -0
  118. package/dist/commands/data/index.d.ts +31 -0
  119. package/dist/commands/data/index.js +1466 -0
  120. package/dist/commands/doctor.d.ts +2 -0
  121. package/dist/commands/doctor.js +201 -0
  122. package/dist/commands/exchange/index.d.ts +2 -0
  123. package/dist/commands/exchange/index.js +107 -0
  124. package/dist/commands/index.d.ts +2 -0
  125. package/dist/commands/index.js +48 -0
  126. package/dist/commands/markets/index.d.ts +2 -0
  127. package/dist/commands/markets/index.js +5 -0
  128. package/dist/commands/markets/ls-simple.d.ts +7 -0
  129. package/dist/commands/markets/ls-simple.js +277 -0
  130. package/dist/commands/operator/index.d.ts +2 -0
  131. package/dist/commands/operator/index.js +146 -0
  132. package/dist/commands/order/cancel-simple.d.ts +5 -0
  133. package/dist/commands/order/cancel-simple.js +104 -0
  134. package/dist/commands/order/index.d.ts +2 -0
  135. package/dist/commands/order/index.js +13 -0
  136. package/dist/commands/order/limit-simple.d.ts +5 -0
  137. package/dist/commands/order/limit-simple.js +195 -0
  138. package/dist/commands/order/market-simple.d.ts +5 -0
  139. package/dist/commands/order/market-simple.js +190 -0
  140. package/dist/commands/order/shared.d.ts +17 -0
  141. package/dist/commands/order/shared.js +51 -0
  142. package/dist/commands/order/trigger-simple.d.ts +5 -0
  143. package/dist/commands/order/trigger-simple.js +246 -0
  144. package/dist/commands/referral/index.d.ts +2 -0
  145. package/dist/commands/referral/index.js +7 -0
  146. package/dist/commands/referral/set.d.ts +2 -0
  147. package/dist/commands/referral/set.js +26 -0
  148. package/dist/commands/referral/status.d.ts +2 -0
  149. package/dist/commands/referral/status.js +31 -0
  150. package/dist/commands/replay/index.d.ts +2 -0
  151. package/dist/commands/replay/index.js +152 -0
  152. package/dist/commands/risk/analytics.d.ts +2 -0
  153. package/dist/commands/risk/analytics.js +64 -0
  154. package/dist/commands/risk/audit.d.ts +2 -0
  155. package/dist/commands/risk/audit.js +52 -0
  156. package/dist/commands/risk/index.d.ts +2 -0
  157. package/dist/commands/risk/index.js +9 -0
  158. package/dist/commands/risk/rules.d.ts +2 -0
  159. package/dist/commands/risk/rules.js +102 -0
  160. package/dist/commands/server.d.ts +2 -0
  161. package/dist/commands/server.js +208 -0
  162. package/dist/commands/setup/index.d.ts +2 -0
  163. package/dist/commands/setup/index.js +478 -0
  164. package/dist/commands/signal/index.d.ts +2 -0
  165. package/dist/commands/signal/index.js +129 -0
  166. package/dist/commands/state/index.d.ts +2 -0
  167. package/dist/commands/state/index.js +5 -0
  168. package/dist/commands/state/show.d.ts +2 -0
  169. package/dist/commands/state/show.js +105 -0
  170. package/dist/commands/strategy/index.d.ts +4 -0
  171. package/dist/commands/strategy/index.js +73 -0
  172. package/dist/commands/traces/index.d.ts +2 -0
  173. package/dist/commands/traces/index.js +76 -0
  174. package/dist/commands/ui/demo.d.ts +9 -0
  175. package/dist/commands/ui/demo.js +195 -0
  176. package/dist/commands/ui/index.d.ts +2 -0
  177. package/dist/commands/ui/index.js +7 -0
  178. package/dist/commands/ui/terminal.d.ts +2 -0
  179. package/dist/commands/ui/terminal.js +255 -0
  180. package/dist/commands/upgrade.d.ts +2 -0
  181. package/dist/commands/upgrade.js +98 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +4 -0
  184. package/dist/lib/agent/audit.d.ts +12 -0
  185. package/dist/lib/agent/audit.js +13 -0
  186. package/dist/lib/agent/gateway.d.ts +13 -0
  187. package/dist/lib/agent/gateway.js +598 -0
  188. package/dist/lib/agent/metrics.d.ts +33 -0
  189. package/dist/lib/agent/metrics.js +175 -0
  190. package/dist/lib/agent/signature.d.ts +8 -0
  191. package/dist/lib/agent/signature.js +28 -0
  192. package/dist/lib/agent/tools.d.ts +28 -0
  193. package/dist/lib/agent/tools.js +453 -0
  194. package/dist/lib/agent/x402.d.ts +23 -0
  195. package/dist/lib/agent/x402.js +62 -0
  196. package/dist/lib/api-wallet.d.ts +69 -0
  197. package/dist/lib/api-wallet.js +101 -0
  198. package/dist/lib/balance-watcher.d.ts +25 -0
  199. package/dist/lib/balance-watcher.js +83 -0
  200. package/dist/lib/book-watcher.d.ts +25 -0
  201. package/dist/lib/book-watcher.js +48 -0
  202. package/dist/lib/config.d.ts +88 -0
  203. package/dist/lib/config.js +427 -0
  204. package/dist/lib/constants.d.ts +50 -0
  205. package/dist/lib/constants.js +84 -0
  206. package/dist/lib/contracts.d.ts +7 -0
  207. package/dist/lib/contracts.js +8 -0
  208. package/dist/lib/credential-vault.d.ts +22 -0
  209. package/dist/lib/credential-vault.js +109 -0
  210. package/dist/lib/db/accounts.d.ts +83 -0
  211. package/dist/lib/db/accounts.js +203 -0
  212. package/dist/lib/db/funding-history.d.ts +69 -0
  213. package/dist/lib/db/funding-history.js +183 -0
  214. package/dist/lib/db/index.d.ts +11 -0
  215. package/dist/lib/db/index.js +272 -0
  216. package/dist/lib/events/bus.d.ts +10 -0
  217. package/dist/lib/events/bus.js +17 -0
  218. package/dist/lib/events/types.d.ts +51 -0
  219. package/dist/lib/events/types.js +1 -0
  220. package/dist/lib/exchange.d.ts +30 -0
  221. package/dist/lib/exchange.js +84 -0
  222. package/dist/lib/execution/journal.d.ts +25 -0
  223. package/dist/lib/execution/journal.js +158 -0
  224. package/dist/lib/execution/safety.d.ts +34 -0
  225. package/dist/lib/execution/safety.js +197 -0
  226. package/dist/lib/exit-codes.d.ts +18 -0
  227. package/dist/lib/exit-codes.js +60 -0
  228. package/dist/lib/fetch.d.ts +18 -0
  229. package/dist/lib/fetch.js +66 -0
  230. package/dist/lib/fs-security.d.ts +10 -0
  231. package/dist/lib/fs-security.js +26 -0
  232. package/dist/lib/funding-tracker.d.ts +40 -0
  233. package/dist/lib/funding-tracker.js +118 -0
  234. package/dist/lib/logger.d.ts +27 -0
  235. package/dist/lib/logger.js +82 -0
  236. package/dist/lib/network-model.d.ts +13 -0
  237. package/dist/lib/network-model.js +30 -0
  238. package/dist/lib/onboarding.d.ts +133 -0
  239. package/dist/lib/onboarding.js +1459 -0
  240. package/dist/lib/operator-state.d.ts +25 -0
  241. package/dist/lib/operator-state.js +82 -0
  242. package/dist/lib/orders-watcher.d.ts +24 -0
  243. package/dist/lib/orders-watcher.js +74 -0
  244. package/dist/lib/paths.d.ts +20 -0
  245. package/dist/lib/paths.js +23 -0
  246. package/dist/lib/portfolio-watcher.d.ts +33 -0
  247. package/dist/lib/portfolio-watcher.js +95 -0
  248. package/dist/lib/position-watcher.d.ts +16 -0
  249. package/dist/lib/position-watcher.js +44 -0
  250. package/dist/lib/price-watcher.d.ts +15 -0
  251. package/dist/lib/price-watcher.js +84 -0
  252. package/dist/lib/prompts.d.ts +32 -0
  253. package/dist/lib/prompts.js +105 -0
  254. package/dist/lib/rate-limit.d.ts +32 -0
  255. package/dist/lib/rate-limit.js +88 -0
  256. package/dist/lib/risk/analytics.d.ts +39 -0
  257. package/dist/lib/risk/analytics.js +98 -0
  258. package/dist/lib/risk/drawdown.d.ts +18 -0
  259. package/dist/lib/risk/drawdown.js +49 -0
  260. package/dist/lib/risk/evaluation-log.d.ts +29 -0
  261. package/dist/lib/risk/evaluation-log.js +61 -0
  262. package/dist/lib/risk/index.d.ts +4 -0
  263. package/dist/lib/risk/index.js +4 -0
  264. package/dist/lib/risk/limits.d.ts +23 -0
  265. package/dist/lib/risk/limits.js +27 -0
  266. package/dist/lib/risk/manager.d.ts +32 -0
  267. package/dist/lib/risk/manager.js +85 -0
  268. package/dist/lib/risk/policy-middleware.d.ts +33 -0
  269. package/dist/lib/risk/policy-middleware.js +267 -0
  270. package/dist/lib/risk/position-sizer.d.ts +9 -0
  271. package/dist/lib/risk/position-sizer.js +14 -0
  272. package/dist/lib/risk/rules-store.d.ts +16 -0
  273. package/dist/lib/risk/rules-store.js +47 -0
  274. package/dist/lib/schema.d.ts +254 -0
  275. package/dist/lib/schema.js +199 -0
  276. package/dist/lib/secrets.d.ts +3 -0
  277. package/dist/lib/secrets.js +62 -0
  278. package/dist/lib/settings.d.ts +24 -0
  279. package/dist/lib/settings.js +86 -0
  280. package/dist/lib/signals.d.ts +73 -0
  281. package/dist/lib/signals.js +136 -0
  282. package/dist/lib/stable-stringify.d.ts +6 -0
  283. package/dist/lib/stable-stringify.js +17 -0
  284. package/dist/lib/state-context.d.ts +44 -0
  285. package/dist/lib/state-context.js +133 -0
  286. package/dist/lib/strategy/basis-trade.d.ts +2 -0
  287. package/dist/lib/strategy/basis-trade.js +24 -0
  288. package/dist/lib/strategy/funding-arb.d.ts +2 -0
  289. package/dist/lib/strategy/funding-arb.js +23 -0
  290. package/dist/lib/strategy/interface.d.ts +23 -0
  291. package/dist/lib/strategy/interface.js +1 -0
  292. package/dist/lib/strategy/registry.d.ts +4 -0
  293. package/dist/lib/strategy/registry.js +10 -0
  294. package/dist/lib/telemetry.d.ts +25 -0
  295. package/dist/lib/telemetry.js +101 -0
  296. package/dist/lib/trace-queries.d.ts +20 -0
  297. package/dist/lib/trace-queries.js +133 -0
  298. package/dist/lib/trace.d.ts +1 -0
  299. package/dist/lib/trace.js +4 -0
  300. package/dist/lib/trade-reputation.d.ts +6 -0
  301. package/dist/lib/trade-reputation.js +99 -0
  302. package/dist/lib/ui-tokens.d.ts +21 -0
  303. package/dist/lib/ui-tokens.js +26 -0
  304. package/dist/lib/validate.d.ts +39 -0
  305. package/dist/lib/validate.js +108 -0
  306. package/dist/lib/validation.d.ts +9 -0
  307. package/dist/lib/validation.js +64 -0
  308. package/dist/server/cache.d.ts +38 -0
  309. package/dist/server/cache.js +56 -0
  310. package/dist/server/index.d.ts +2 -0
  311. package/dist/server/index.js +89 -0
  312. package/dist/server/ipc.d.ts +18 -0
  313. package/dist/server/ipc.js +159 -0
  314. package/dist/server/subscriptions.d.ts +18 -0
  315. package/dist/server/subscriptions.js +114 -0
  316. package/package.json +124 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Rate Limiting Utilities
3
+ * Prevents API abuse and handles concurrent requests
4
+ */
5
+ /**
6
+ * Simple semaphore for limiting concurrent operations
7
+ */
8
+ export declare class Semaphore {
9
+ private permits;
10
+ private queue;
11
+ constructor(permits: number);
12
+ acquire(): Promise<void>;
13
+ release(): void;
14
+ }
15
+ /**
16
+ * Run promises with concurrency limit
17
+ */
18
+ export declare function pLimit<T>(tasks: Array<() => Promise<T>>, concurrency?: number): Promise<Array<PromiseSettledResult<T>>>;
19
+ /**
20
+ * Run promises sequentially with delay between each
21
+ */
22
+ export declare function pSequential<T>(tasks: Array<() => Promise<T>>, delayMs?: number): Promise<T[]>;
23
+ /**
24
+ * Rate limiter with sliding window
25
+ */
26
+ export declare class RateLimiter {
27
+ private timestamps;
28
+ private readonly maxRequests;
29
+ private readonly windowMs;
30
+ constructor(maxRequests: number, windowMs: number);
31
+ acquire(): Promise<void>;
32
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Rate Limiting Utilities
3
+ * Prevents API abuse and handles concurrent requests
4
+ */
5
+ /**
6
+ * Simple semaphore for limiting concurrent operations
7
+ */
8
+ export class Semaphore {
9
+ permits;
10
+ queue = [];
11
+ constructor(permits) {
12
+ this.permits = permits;
13
+ }
14
+ async acquire() {
15
+ if (this.permits > 0) {
16
+ this.permits--;
17
+ return;
18
+ }
19
+ return new Promise(resolve => {
20
+ this.queue.push(resolve);
21
+ });
22
+ }
23
+ release() {
24
+ const next = this.queue.shift();
25
+ if (next) {
26
+ next();
27
+ }
28
+ else {
29
+ this.permits++;
30
+ }
31
+ }
32
+ }
33
+ /**
34
+ * Run promises with concurrency limit
35
+ */
36
+ export async function pLimit(tasks, concurrency = 3) {
37
+ const semaphore = new Semaphore(concurrency);
38
+ const wrappedTasks = tasks.map(async (task) => {
39
+ await semaphore.acquire();
40
+ try {
41
+ return await task();
42
+ }
43
+ finally {
44
+ semaphore.release();
45
+ }
46
+ });
47
+ return Promise.allSettled(wrappedTasks);
48
+ }
49
+ /**
50
+ * Run promises sequentially with delay between each
51
+ */
52
+ export async function pSequential(tasks, delayMs = 100) {
53
+ const results = [];
54
+ for (const task of tasks) {
55
+ results.push(await task());
56
+ if (delayMs > 0) {
57
+ await sleep(delayMs);
58
+ }
59
+ }
60
+ return results;
61
+ }
62
+ /**
63
+ * Rate limiter with sliding window
64
+ */
65
+ export class RateLimiter {
66
+ timestamps = [];
67
+ maxRequests;
68
+ windowMs;
69
+ constructor(maxRequests, windowMs) {
70
+ this.maxRequests = maxRequests;
71
+ this.windowMs = windowMs;
72
+ }
73
+ async acquire() {
74
+ while (true) {
75
+ const now = Date.now();
76
+ this.timestamps = this.timestamps.filter(t => now - t < this.windowMs);
77
+ if (this.timestamps.length < this.maxRequests) {
78
+ this.timestamps.push(now);
79
+ return;
80
+ }
81
+ const waitTime = this.timestamps[0] + this.windowMs - now;
82
+ await sleep(Math.max(0, waitTime));
83
+ }
84
+ }
85
+ }
86
+ function sleep(ms) {
87
+ return new Promise(resolve => setTimeout(resolve, ms));
88
+ }
@@ -0,0 +1,39 @@
1
+ export interface DenialSummary {
2
+ reason: string;
3
+ count: number;
4
+ lastSeen: number;
5
+ markets: string[];
6
+ }
7
+ export interface EvalWindow {
8
+ total: number;
9
+ allowed: number;
10
+ denied: number;
11
+ denialRate: number;
12
+ windowStartMs: number;
13
+ windowEndMs: number;
14
+ }
15
+ export interface AnomalyAlert {
16
+ type: "high_denial_rate" | "repeated_denial" | "exposure_spike";
17
+ severity: "warning" | "critical";
18
+ message: string;
19
+ data: Record<string, unknown>;
20
+ timestamp: number;
21
+ }
22
+ /**
23
+ * Get denials grouped by reason in the last N minutes.
24
+ */
25
+ export declare function getDenialsByReason(minutes?: number): DenialSummary[];
26
+ /**
27
+ * Get evaluation stats in sliding windows.
28
+ *
29
+ * Breaks the last `spanMinutes` into `windowMinutes`-sized buckets. For each
30
+ * bucket, counts total/allowed/denied and computes denial rate.
31
+ */
32
+ export declare function getEvalWindows(windowMinutes?: number, spanMinutes?: number): EvalWindow[];
33
+ /**
34
+ * Detect anomalies based on recent evaluation patterns.
35
+ */
36
+ export declare function detectAnomalies(options?: {
37
+ denialRateThreshold?: number;
38
+ windowMinutes?: number;
39
+ }): AnomalyAlert[];
@@ -0,0 +1,98 @@
1
+ import { getDb } from "../db/index.js";
2
+ /**
3
+ * Get denials grouped by reason in the last N minutes.
4
+ */
5
+ export function getDenialsByReason(minutes = 60) {
6
+ const db = getDb();
7
+ const cutoff = Date.now() - minutes * 60_000;
8
+ const rows = db
9
+ .prepare(`SELECT reason, COUNT(*) as count, MAX(created_at) as last_seen,
10
+ GROUP_CONCAT(DISTINCT market) as markets
11
+ FROM risk_evaluation_log
12
+ WHERE allowed = 0 AND created_at > ?
13
+ GROUP BY reason
14
+ ORDER BY count DESC`)
15
+ .all(cutoff);
16
+ return rows.map((r) => ({
17
+ reason: r.reason ?? "unknown",
18
+ count: r.count,
19
+ lastSeen: r.last_seen,
20
+ markets: r.markets ? r.markets.split(",").filter(Boolean) : [],
21
+ }));
22
+ }
23
+ /**
24
+ * Get evaluation stats in sliding windows.
25
+ *
26
+ * Breaks the last `spanMinutes` into `windowMinutes`-sized buckets. For each
27
+ * bucket, counts total/allowed/denied and computes denial rate.
28
+ */
29
+ export function getEvalWindows(windowMinutes = 5, spanMinutes = 60) {
30
+ const db = getDb();
31
+ const bucketMs = windowMinutes * 60_000;
32
+ const cutoff = Date.now() - spanMinutes * 60_000;
33
+ const rows = db
34
+ .prepare(`SELECT
35
+ (created_at / ?) * ? as bucket,
36
+ COUNT(*) as total,
37
+ SUM(CASE WHEN allowed = 1 THEN 1 ELSE 0 END) as allowed,
38
+ SUM(CASE WHEN allowed = 0 THEN 1 ELSE 0 END) as denied
39
+ FROM risk_evaluation_log
40
+ WHERE created_at > ?
41
+ GROUP BY bucket
42
+ ORDER BY bucket`)
43
+ .all(bucketMs, bucketMs, cutoff);
44
+ return rows.map((r) => ({
45
+ total: r.total,
46
+ allowed: r.allowed,
47
+ denied: r.denied,
48
+ denialRate: r.total > 0 ? r.denied / r.total : 0,
49
+ windowStartMs: r.bucket,
50
+ windowEndMs: r.bucket + bucketMs,
51
+ }));
52
+ }
53
+ /**
54
+ * Detect anomalies based on recent evaluation patterns.
55
+ */
56
+ export function detectAnomalies(options) {
57
+ const threshold = options?.denialRateThreshold ?? 0.5;
58
+ const windowMinutes = options?.windowMinutes ?? 5;
59
+ const alerts = [];
60
+ const now = Date.now();
61
+ // Check the most recent window for high denial rate
62
+ const windows = getEvalWindows(windowMinutes, windowMinutes);
63
+ if (windows.length > 0) {
64
+ const latest = windows[windows.length - 1];
65
+ if (latest.denialRate > threshold && latest.total > 3) {
66
+ alerts.push({
67
+ type: "high_denial_rate",
68
+ severity: latest.denialRate > 0.8 ? "critical" : "warning",
69
+ message: `Denial rate ${(latest.denialRate * 100).toFixed(0)}% (${latest.denied}/${latest.total}) in last ${windowMinutes} min`,
70
+ data: {
71
+ denialRate: latest.denialRate,
72
+ denied: latest.denied,
73
+ total: latest.total,
74
+ windowMinutes,
75
+ },
76
+ timestamp: now,
77
+ });
78
+ }
79
+ }
80
+ // Check for repeated denials by reason
81
+ const denials = getDenialsByReason(windowMinutes);
82
+ for (const d of denials) {
83
+ if (d.count > 5) {
84
+ alerts.push({
85
+ type: "repeated_denial",
86
+ severity: d.count > 10 ? "critical" : "warning",
87
+ message: `"${d.reason}" denied ${d.count} times in last ${windowMinutes} min`,
88
+ data: {
89
+ reason: d.reason,
90
+ count: d.count,
91
+ markets: d.markets,
92
+ },
93
+ timestamp: now,
94
+ });
95
+ }
96
+ }
97
+ return alerts;
98
+ }
@@ -0,0 +1,18 @@
1
+ export interface DrawdownState {
2
+ peakEquity: number;
3
+ halted: boolean;
4
+ }
5
+ export declare class DrawdownMonitor {
6
+ private peakEquity;
7
+ private readonly maxDrawdownPct;
8
+ private haltedState;
9
+ constructor(maxDrawdownPct: number, initialState?: Partial<DrawdownState>);
10
+ get halted(): boolean;
11
+ get peak(): number;
12
+ update(equity: number): {
13
+ drawdownPct: number;
14
+ halted: boolean;
15
+ peakEquity: number;
16
+ };
17
+ reset(): void;
18
+ }
@@ -0,0 +1,49 @@
1
+ import { eventBus } from "../events/bus.js";
2
+ import { createLogger } from "../logger.js";
3
+ const log = createLogger("risk:drawdown");
4
+ export class DrawdownMonitor {
5
+ peakEquity = 0;
6
+ maxDrawdownPct;
7
+ haltedState = false;
8
+ constructor(maxDrawdownPct, initialState) {
9
+ this.maxDrawdownPct = maxDrawdownPct;
10
+ this.peakEquity = initialState?.peakEquity ?? 0;
11
+ this.haltedState = initialState?.halted ?? false;
12
+ }
13
+ get halted() {
14
+ return this.haltedState;
15
+ }
16
+ get peak() {
17
+ return this.peakEquity;
18
+ }
19
+ update(equity) {
20
+ if (equity > this.peakEquity) {
21
+ this.peakEquity = equity;
22
+ }
23
+ const drawdownPct = this.peakEquity > 0
24
+ ? ((this.peakEquity - equity) / this.peakEquity) * 100
25
+ : 0;
26
+ if (drawdownPct >= this.maxDrawdownPct && !this.haltedState) {
27
+ this.haltedState = true;
28
+ log.error("CIRCUIT BREAKER: max drawdown exceeded", {
29
+ drawdownPct: drawdownPct.toFixed(2),
30
+ maxAllowed: this.maxDrawdownPct,
31
+ equity,
32
+ peak: this.peakEquity,
33
+ });
34
+ eventBus.emit("risk.circuit_breaker.tripped", {
35
+ exchange: "",
36
+ drawdownPct,
37
+ peakEquity: this.peakEquity,
38
+ currentEquity: equity,
39
+ timestamp: Date.now(),
40
+ });
41
+ }
42
+ return { drawdownPct, halted: this.haltedState, peakEquity: this.peakEquity };
43
+ }
44
+ reset() {
45
+ this.haltedState = false;
46
+ this.peakEquity = 0;
47
+ log.info("Drawdown monitor reset");
48
+ }
49
+ }
@@ -0,0 +1,29 @@
1
+ export interface RiskEvaluationInput {
2
+ traceId?: string;
3
+ exchange: string;
4
+ market?: string;
5
+ side?: string;
6
+ requestedSizeUsd: number;
7
+ equity: number;
8
+ exposureUsd: number;
9
+ confidence?: number;
10
+ allowed: boolean;
11
+ adjustedSizeUsd?: number;
12
+ drawdownPct?: number;
13
+ rulesEvaluated: string[];
14
+ rulesFired: string[];
15
+ reason: string | null;
16
+ }
17
+ export interface RiskEvaluationRecord extends RiskEvaluationInput {
18
+ id: number;
19
+ createdAt: number;
20
+ }
21
+ export interface RiskEvaluationQuery {
22
+ traceId?: string;
23
+ market?: string;
24
+ exchange?: string;
25
+ allowed?: boolean;
26
+ limit?: number;
27
+ }
28
+ export declare function logRiskEvaluation(input: RiskEvaluationInput): number;
29
+ export declare function queryRiskEvaluations(query?: RiskEvaluationQuery): RiskEvaluationRecord[];
@@ -0,0 +1,61 @@
1
+ import { getDb } from "../db/index.js";
2
+ function rowToRecord(row) {
3
+ return {
4
+ id: row.id,
5
+ traceId: row.trace_id ?? undefined,
6
+ exchange: row.exchange,
7
+ market: row.market ?? undefined,
8
+ side: row.side ?? undefined,
9
+ requestedSizeUsd: row.requested_size_usd,
10
+ equity: row.equity,
11
+ exposureUsd: row.exposure_usd,
12
+ confidence: row.confidence ?? undefined,
13
+ allowed: row.allowed === 1,
14
+ adjustedSizeUsd: row.adjusted_size_usd ?? undefined,
15
+ drawdownPct: row.drawdown_pct ?? undefined,
16
+ rulesEvaluated: row.rules_evaluated
17
+ ? JSON.parse(row.rules_evaluated)
18
+ : [],
19
+ rulesFired: row.rules_fired
20
+ ? JSON.parse(row.rules_fired)
21
+ : [],
22
+ reason: row.reason,
23
+ createdAt: row.created_at,
24
+ };
25
+ }
26
+ export function logRiskEvaluation(input) {
27
+ const db = getDb();
28
+ const stmt = db.prepare(`INSERT INTO risk_evaluation_log
29
+ (trace_id, exchange, market, side, requested_size_usd, equity, exposure_usd,
30
+ confidence, allowed, adjusted_size_usd, drawdown_pct, rules_evaluated, rules_fired, reason)
31
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
32
+ const result = stmt.run(input.traceId ?? null, input.exchange, input.market ?? null, input.side ?? null, input.requestedSizeUsd, input.equity, input.exposureUsd, input.confidence ?? null, input.allowed ? 1 : 0, input.adjustedSizeUsd ?? null, input.drawdownPct ?? null, JSON.stringify(input.rulesEvaluated), JSON.stringify(input.rulesFired), input.reason);
33
+ return Number(result.lastInsertRowid);
34
+ }
35
+ export function queryRiskEvaluations(query = {}) {
36
+ const db = getDb();
37
+ const conditions = [];
38
+ const params = [];
39
+ if (query.traceId !== undefined) {
40
+ conditions.push("trace_id = ?");
41
+ params.push(query.traceId);
42
+ }
43
+ if (query.market !== undefined) {
44
+ conditions.push("market = ?");
45
+ params.push(query.market);
46
+ }
47
+ if (query.exchange !== undefined) {
48
+ conditions.push("exchange = ?");
49
+ params.push(query.exchange);
50
+ }
51
+ if (query.allowed !== undefined) {
52
+ conditions.push("allowed = ?");
53
+ params.push(query.allowed ? 1 : 0);
54
+ }
55
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
56
+ const limit = query.limit ?? 100;
57
+ const rows = db
58
+ .prepare(`SELECT * FROM risk_evaluation_log ${where} ORDER BY created_at DESC LIMIT ?`)
59
+ .all(...params, limit);
60
+ return rows.map(rowToRecord);
61
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./drawdown.js";
2
+ export * from "./limits.js";
3
+ export * from "./manager.js";
4
+ export * from "./position-sizer.js";
@@ -0,0 +1,4 @@
1
+ export * from "./drawdown.js";
2
+ export * from "./limits.js";
3
+ export * from "./manager.js";
4
+ export * from "./position-sizer.js";
@@ -0,0 +1,23 @@
1
+ export interface RiskLimitsConfig {
2
+ maxPositionSizeUsd: number;
3
+ maxTotalExposureUsd: number;
4
+ maxLeverage: number;
5
+ minSignalConfidence: number;
6
+ }
7
+ export interface RiskSignal {
8
+ market: string;
9
+ side: "long" | "short" | "close";
10
+ confidence: number;
11
+ reason?: string;
12
+ timestamp?: number;
13
+ }
14
+ export interface RiskCheck {
15
+ allowed: boolean;
16
+ reason?: string;
17
+ adjustedSizeUsd?: number;
18
+ }
19
+ export declare class RiskLimits {
20
+ private readonly config;
21
+ constructor(config: RiskLimitsConfig);
22
+ check(signal: RiskSignal, proposedSizeUsd: number, currentExposureUsd: number, equity: number): RiskCheck;
23
+ }
@@ -0,0 +1,27 @@
1
+ export class RiskLimits {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ check(signal, proposedSizeUsd, currentExposureUsd, equity) {
7
+ if (signal.confidence < this.config.minSignalConfidence) {
8
+ return {
9
+ allowed: false,
10
+ reason: `Confidence ${signal.confidence.toFixed(3)} below min ${this.config.minSignalConfidence}`,
11
+ };
12
+ }
13
+ if (proposedSizeUsd > this.config.maxPositionSizeUsd) {
14
+ proposedSizeUsd = this.config.maxPositionSizeUsd;
15
+ }
16
+ const newTotalExposure = currentExposureUsd + proposedSizeUsd;
17
+ const maxExposure = Math.min(this.config.maxTotalExposureUsd, equity * this.config.maxLeverage);
18
+ if (newTotalExposure > maxExposure) {
19
+ const available = maxExposure - currentExposureUsd;
20
+ if (available <= 0) {
21
+ return { allowed: false, reason: "Max exposure reached" };
22
+ }
23
+ proposedSizeUsd = available;
24
+ }
25
+ return { allowed: true, adjustedSizeUsd: proposedSizeUsd };
26
+ }
27
+ }
@@ -0,0 +1,32 @@
1
+ import { type DrawdownState } from "./drawdown.js";
2
+ import { type RiskLimitsConfig, type RiskSignal } from "./limits.js";
3
+ import { type PositionSizerConfig } from "./position-sizer.js";
4
+ export interface RiskManagerConfig {
5
+ positionSizer: PositionSizerConfig;
6
+ limits: RiskLimitsConfig;
7
+ maxDrawdownPct: number;
8
+ initialDrawdownState?: Partial<DrawdownState>;
9
+ }
10
+ export interface RiskEvaluation {
11
+ allowed: boolean;
12
+ sizeUsd: number;
13
+ reason?: string;
14
+ drawdownPct: number;
15
+ peakEquity: number;
16
+ halted: boolean;
17
+ }
18
+ export declare class RiskManager {
19
+ private readonly sizer;
20
+ private readonly drawdown;
21
+ private readonly limits;
22
+ constructor(config: RiskManagerConfig);
23
+ evaluate(signal: RiskSignal, equity: number, currentExposureUsd: number, proposedSizeUsd?: number): RiskEvaluation;
24
+ updateEquity(equity: number): {
25
+ drawdownPct: number;
26
+ halted: boolean;
27
+ peakEquity: number;
28
+ };
29
+ get isHalted(): boolean;
30
+ get drawdownState(): DrawdownState;
31
+ resetCircuitBreaker(): void;
32
+ }
@@ -0,0 +1,85 @@
1
+ import { createLogger } from "../logger.js";
2
+ import { DrawdownMonitor } from "./drawdown.js";
3
+ import { RiskLimits } from "./limits.js";
4
+ import { PositionSizer } from "./position-sizer.js";
5
+ const log = createLogger("risk:manager");
6
+ export class RiskManager {
7
+ sizer;
8
+ drawdown;
9
+ limits;
10
+ constructor(config) {
11
+ this.sizer = new PositionSizer(config.positionSizer);
12
+ this.drawdown = new DrawdownMonitor(config.maxDrawdownPct, config.initialDrawdownState);
13
+ this.limits = new RiskLimits(config.limits);
14
+ }
15
+ evaluate(signal, equity, currentExposureUsd, proposedSizeUsd) {
16
+ const dd = this.drawdown.update(equity);
17
+ if (dd.halted) {
18
+ return {
19
+ allowed: false,
20
+ sizeUsd: 0,
21
+ reason: "Trading halted: max drawdown exceeded",
22
+ drawdownPct: dd.drawdownPct,
23
+ peakEquity: dd.peakEquity,
24
+ halted: dd.halted,
25
+ };
26
+ }
27
+ if (signal.side === "close") {
28
+ return {
29
+ allowed: true,
30
+ sizeUsd: 0,
31
+ drawdownPct: dd.drawdownPct,
32
+ peakEquity: dd.peakEquity,
33
+ halted: dd.halted,
34
+ };
35
+ }
36
+ const sizedByPolicy = this.sizer.calculate(equity, signal.confidence, currentExposureUsd);
37
+ const effectiveProposed = proposedSizeUsd !== undefined
38
+ ? Math.min(proposedSizeUsd, sizedByPolicy)
39
+ : sizedByPolicy;
40
+ if (effectiveProposed <= 0) {
41
+ return {
42
+ allowed: false,
43
+ sizeUsd: 0,
44
+ reason: "Position sizer returned 0",
45
+ drawdownPct: dd.drawdownPct,
46
+ peakEquity: dd.peakEquity,
47
+ halted: dd.halted,
48
+ };
49
+ }
50
+ const check = this.limits.check(signal, effectiveProposed, currentExposureUsd, equity);
51
+ if (!check.allowed) {
52
+ log.info("Risk rejected signal", { market: signal.market, reason: check.reason });
53
+ return {
54
+ allowed: false,
55
+ sizeUsd: 0,
56
+ reason: check.reason,
57
+ drawdownPct: dd.drawdownPct,
58
+ peakEquity: dd.peakEquity,
59
+ halted: dd.halted,
60
+ };
61
+ }
62
+ return {
63
+ allowed: true,
64
+ sizeUsd: check.adjustedSizeUsd ?? effectiveProposed,
65
+ drawdownPct: dd.drawdownPct,
66
+ peakEquity: dd.peakEquity,
67
+ halted: dd.halted,
68
+ };
69
+ }
70
+ updateEquity(equity) {
71
+ return this.drawdown.update(equity);
72
+ }
73
+ get isHalted() {
74
+ return this.drawdown.halted;
75
+ }
76
+ get drawdownState() {
77
+ return {
78
+ peakEquity: this.drawdown.peak,
79
+ halted: this.drawdown.halted,
80
+ };
81
+ }
82
+ resetCircuitBreaker() {
83
+ this.drawdown.reset();
84
+ }
85
+ }
@@ -0,0 +1,33 @@
1
+ import type { PerpDEXAdapter } from "../../adapters/interface.js";
2
+ import type { Config } from "../config.js";
3
+ import type { RiskSignal } from "./limits.js";
4
+ export interface RiskOrderInput {
5
+ market: string;
6
+ side: "long" | "short";
7
+ requestedSizeBase: number;
8
+ confidence?: number;
9
+ referencePriceUsd?: number;
10
+ reason?: string;
11
+ }
12
+ export interface RiskOrderDecision {
13
+ allowed: boolean;
14
+ adjustedSizeBase: number;
15
+ adjustedSizeUsd: number;
16
+ referencePriceUsd: number;
17
+ reason?: string;
18
+ }
19
+ export declare class RiskPolicyMiddleware {
20
+ private readonly config;
21
+ private readonly exchangeId;
22
+ private readonly manager;
23
+ constructor(config: Config, exchangeId: string);
24
+ evaluateUsdSignal(adapter: PerpDEXAdapter, signal: RiskSignal, proposedSizeUsd: number, traceId?: string): Promise<{
25
+ allowed: boolean;
26
+ sizeUsd: number;
27
+ reason?: string;
28
+ }>;
29
+ evaluateOrder(adapter: PerpDEXAdapter, input: RiskOrderInput, traceId?: string): Promise<RiskOrderDecision>;
30
+ private resolveReferencePrice;
31
+ private loadAccountMetrics;
32
+ private recordHeartbeat;
33
+ }