@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,109 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { CREDENTIAL_VAULT_PATH } from "./paths.js";
4
+ import { encryptSecret } from "./secrets.js";
5
+ import { ensurePrivateDir, hardenPrivateFile, PRIVATE_FILE_MODE } from "./fs-security.js";
6
+ const VAULT_SCHEMA_VERSION = 1;
7
+ function createVault() {
8
+ return {
9
+ version: VAULT_SCHEMA_VERSION,
10
+ updatedAt: new Date().toISOString(),
11
+ entries: [],
12
+ };
13
+ }
14
+ function readVault() {
15
+ if (!existsSync(CREDENTIAL_VAULT_PATH)) {
16
+ return createVault();
17
+ }
18
+ try {
19
+ hardenPrivateFile(CREDENTIAL_VAULT_PATH);
20
+ const raw = readFileSync(CREDENTIAL_VAULT_PATH, "utf-8");
21
+ const parsed = JSON.parse(raw);
22
+ return {
23
+ version: VAULT_SCHEMA_VERSION,
24
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : new Date().toISOString(),
25
+ entries: Array.isArray(parsed.entries) ? parsed.entries : [],
26
+ };
27
+ }
28
+ catch {
29
+ return createVault();
30
+ }
31
+ }
32
+ function writeVault(vault) {
33
+ ensurePrivateDir(dirname(CREDENTIAL_VAULT_PATH));
34
+ writeFileSync(CREDENTIAL_VAULT_PATH, `${JSON.stringify(vault, null, 2)}\n`, {
35
+ mode: PRIVATE_FILE_MODE,
36
+ });
37
+ hardenPrivateFile(CREDENTIAL_VAULT_PATH);
38
+ }
39
+ function profileToSecretMap(profile) {
40
+ const secrets = {};
41
+ if (profile.credentials.hyperliquid?.privateKey) {
42
+ secrets.HYPERLIQUID_PRIVATE_KEY = profile.credentials.hyperliquid.privateKey;
43
+ }
44
+ if (profile.credentials.decibel?.apiWalletPrivateKey) {
45
+ secrets.DECIBEL_API_WALLET_PRIVATE_KEY = profile.credentials.decibel.apiWalletPrivateKey;
46
+ }
47
+ if (profile.credentials.decibel?.apiBearerToken) {
48
+ secrets.DECIBEL_API_BEARER_TOKEN = profile.credentials.decibel.apiBearerToken;
49
+ }
50
+ if (profile.credentials.aevo?.signingKey) {
51
+ secrets.AEVO_SIGNING_KEY = profile.credentials.aevo.signingKey;
52
+ }
53
+ if (profile.credentials.aevo?.apiKey) {
54
+ secrets.AEVO_API_KEY = profile.credentials.aevo.apiKey;
55
+ }
56
+ if (profile.credentials.aevo?.apiSecret) {
57
+ secrets.AEVO_API_SECRET = profile.credentials.aevo.apiSecret;
58
+ }
59
+ if (profile.credentials.orderly?.tradingSecret) {
60
+ secrets.ORDERLY_TRADING_SECRET = profile.credentials.orderly.tradingSecret;
61
+ }
62
+ if (profile.credentials.orderly?.key) {
63
+ secrets.ORDERLY_KEY = profile.credentials.orderly.key;
64
+ }
65
+ if (profile.credentials.orderly?.secret) {
66
+ secrets.ORDERLY_SECRET = profile.credentials.orderly.secret;
67
+ }
68
+ if (profile.credentials.paradex?.privateKey) {
69
+ secrets.PARADEX_PRIVATE_KEY = profile.credentials.paradex.privateKey;
70
+ }
71
+ if (profile.credentials.paradex?.apiBearerToken) {
72
+ secrets.PARADEX_API_BEARER_TOKEN = profile.credentials.paradex.apiBearerToken;
73
+ }
74
+ return secrets;
75
+ }
76
+ function buildId(profile) {
77
+ const day = profile.generatedAt.slice(0, 10).replace(/-/g, "");
78
+ const suffix = Math.random().toString(36).slice(2, 8);
79
+ return `onb-${day}-${suffix}`;
80
+ }
81
+ export function saveOnboardingProfileToVault(profile) {
82
+ const vault = readVault();
83
+ const id = buildId(profile);
84
+ const plainSecrets = profileToSecretMap(profile);
85
+ const encrypted = {};
86
+ for (const [key, value] of Object.entries(plainSecrets)) {
87
+ encrypted[key] = encryptSecret(value);
88
+ }
89
+ const entry = {
90
+ id,
91
+ createdAt: profile.generatedAt,
92
+ mode: profile.mode,
93
+ exchanges: [...profile.exchanges],
94
+ wallets: { ...profile.wallets },
95
+ encrypted,
96
+ };
97
+ vault.entries.unshift(entry);
98
+ vault.updatedAt = new Date().toISOString();
99
+ writeVault(vault);
100
+ return { id, path: CREDENTIAL_VAULT_PATH };
101
+ }
102
+ export function getCredentialVaultSummary() {
103
+ const vault = readVault();
104
+ return {
105
+ path: CREDENTIAL_VAULT_PATH,
106
+ entries: vault.entries.length,
107
+ updatedAt: vault.updatedAt,
108
+ };
109
+ }
@@ -0,0 +1,83 @@
1
+ import type { Address, Hex } from "viem";
2
+ /**
3
+ * Account types
4
+ */
5
+ export type AccountType = "readonly" | "api_wallet";
6
+ export type AccountSource = "cli_import";
7
+ /**
8
+ * Account interface
9
+ */
10
+ export interface Account {
11
+ id: number;
12
+ alias: string;
13
+ exchange: string;
14
+ userAddress: Address;
15
+ type: AccountType;
16
+ source: AccountSource;
17
+ apiWalletPrivateKey: Hex | null;
18
+ apiWalletPublicKey: Address | null;
19
+ isDefault: boolean;
20
+ createdAt: number;
21
+ updatedAt: number;
22
+ }
23
+ /**
24
+ * Input for creating an account
25
+ */
26
+ export interface CreateAccountInput {
27
+ alias: string;
28
+ exchange: string;
29
+ userAddress: Address;
30
+ type: AccountType;
31
+ source?: AccountSource;
32
+ apiWalletPrivateKey?: Hex;
33
+ apiWalletPublicKey?: Address;
34
+ setAsDefault?: boolean;
35
+ }
36
+ /**
37
+ * Create a new account
38
+ */
39
+ export declare function createAccount(input: CreateAccountInput): Account;
40
+ /**
41
+ * Get an account by ID
42
+ */
43
+ export declare function getAccountById(id: number): Account | null;
44
+ /**
45
+ * Get an account by alias
46
+ */
47
+ export declare function getAccountByAlias(alias: string, exchange?: string): Account | null;
48
+ /**
49
+ * Get an account by alias scoped to a specific exchange
50
+ */
51
+ export declare function getAccountByAliasForExchange(alias: string, exchange: string): Account | null;
52
+ /**
53
+ * Get the default account
54
+ */
55
+ export declare function getDefaultAccount(): Account | null;
56
+ /**
57
+ * Get the default account for a specific exchange
58
+ */
59
+ export declare function getDefaultAccountForExchange(exchange: string): Account | null;
60
+ /**
61
+ * Get all accounts
62
+ */
63
+ export declare function getAllAccounts(): Account[];
64
+ /**
65
+ * Set an account as default by alias
66
+ */
67
+ export declare function setDefaultAccount(alias: string, exchange?: string): Account;
68
+ /**
69
+ * Delete an account by alias
70
+ */
71
+ export declare function deleteAccount(alias: string, exchange?: string): boolean;
72
+ /**
73
+ * Check if an alias is already taken
74
+ */
75
+ export declare function isAliasTaken(alias: string, exchange?: string): boolean;
76
+ /**
77
+ * Update an account's API wallet credentials
78
+ */
79
+ export declare function updateAccountApiWallet(alias: string, apiWalletPrivateKey: Hex, apiWalletPublicKey: Address, exchange?: string): Account;
80
+ /**
81
+ * Get account count
82
+ */
83
+ export declare function getAccountCount(exchange?: string): number;
@@ -0,0 +1,203 @@
1
+ import { getDb } from "./index.js";
2
+ import { decryptSecret, encryptSecret, isEncryptedSecret } from "../secrets.js";
3
+ function decodePrivateKey(row) {
4
+ if (!row.api_wallet_private_key) {
5
+ return null;
6
+ }
7
+ if (isEncryptedSecret(row.api_wallet_private_key)) {
8
+ return decryptSecret(row.api_wallet_private_key);
9
+ }
10
+ // Legacy plaintext rows are migrated in-place on first read.
11
+ const encrypted = encryptSecret(row.api_wallet_private_key);
12
+ getDb()
13
+ .prepare("UPDATE accounts SET api_wallet_private_key = ?, updated_at = strftime('%s', 'now') WHERE id = ?")
14
+ .run(encrypted, row.id);
15
+ return row.api_wallet_private_key;
16
+ }
17
+ /**
18
+ * Convert database row to Account object
19
+ */
20
+ function rowToAccount(row) {
21
+ return {
22
+ id: row.id,
23
+ alias: row.alias,
24
+ exchange: row.exchange,
25
+ userAddress: row.user_address,
26
+ type: row.type,
27
+ source: row.source,
28
+ apiWalletPrivateKey: decodePrivateKey(row),
29
+ apiWalletPublicKey: row.api_wallet_public_key,
30
+ isDefault: row.is_default === 1,
31
+ createdAt: row.created_at,
32
+ updatedAt: row.updated_at,
33
+ };
34
+ }
35
+ /**
36
+ * Create a new account
37
+ */
38
+ export function createAccount(input) {
39
+ const db = getDb();
40
+ if (isAliasTaken(input.alias, input.exchange)) {
41
+ throw new Error(`Alias "${input.alias}" is already in use for exchange "${input.exchange}". Choose a different alias.`);
42
+ }
43
+ // If this is the first account or setAsDefault is true, make it default
44
+ const accountCount = db.prepare("SELECT COUNT(*) as count FROM accounts WHERE exchange = ?").get(input.exchange);
45
+ const shouldBeDefault = accountCount.count === 0 || input.setAsDefault;
46
+ // If setting as default for this exchange, unset current default first
47
+ if (shouldBeDefault) {
48
+ db.prepare("UPDATE accounts SET is_default = 0 WHERE is_default = 1 AND exchange = ?").run(input.exchange);
49
+ }
50
+ const result = db.prepare(`
51
+ INSERT INTO accounts (
52
+ alias,
53
+ exchange,
54
+ user_address,
55
+ type,
56
+ source,
57
+ api_wallet_private_key,
58
+ api_wallet_public_key,
59
+ is_default
60
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
61
+ `).run(input.alias, input.exchange, input.userAddress, input.type, input.source || "cli_import", input.apiWalletPrivateKey ? encryptSecret(input.apiWalletPrivateKey) : null, input.apiWalletPublicKey || null, shouldBeDefault ? 1 : 0);
62
+ return getAccountById(Number(result.lastInsertRowid));
63
+ }
64
+ /**
65
+ * Get an account by ID
66
+ */
67
+ export function getAccountById(id) {
68
+ const db = getDb();
69
+ const row = db.prepare("SELECT * FROM accounts WHERE id = ?").get(id);
70
+ return row ? rowToAccount(row) : null;
71
+ }
72
+ /**
73
+ * Get an account by alias
74
+ */
75
+ export function getAccountByAlias(alias, exchange) {
76
+ const db = getDb();
77
+ if (exchange) {
78
+ const row = db
79
+ .prepare("SELECT * FROM accounts WHERE alias = ? AND exchange = ?")
80
+ .get(alias, exchange);
81
+ return row ? rowToAccount(row) : null;
82
+ }
83
+ const rows = db.prepare("SELECT * FROM accounts WHERE alias = ? ORDER BY created_at ASC").all(alias);
84
+ if (rows.length === 0) {
85
+ return null;
86
+ }
87
+ if (rows.length > 1) {
88
+ throw new Error(`Multiple accounts found with alias "${alias}". Re-run with --exchange to disambiguate.`);
89
+ }
90
+ return rowToAccount(rows[0]);
91
+ }
92
+ /**
93
+ * Get an account by alias scoped to a specific exchange
94
+ */
95
+ export function getAccountByAliasForExchange(alias, exchange) {
96
+ return getAccountByAlias(alias, exchange);
97
+ }
98
+ /**
99
+ * Get the default account
100
+ */
101
+ export function getDefaultAccount() {
102
+ const db = getDb();
103
+ const row = db.prepare("SELECT * FROM accounts WHERE is_default = 1").get();
104
+ return row ? rowToAccount(row) : null;
105
+ }
106
+ /**
107
+ * Get the default account for a specific exchange
108
+ */
109
+ export function getDefaultAccountForExchange(exchange) {
110
+ const db = getDb();
111
+ const row = db
112
+ .prepare("SELECT * FROM accounts WHERE is_default = 1 AND exchange = ?")
113
+ .get(exchange);
114
+ return row ? rowToAccount(row) : null;
115
+ }
116
+ /**
117
+ * Get all accounts
118
+ */
119
+ export function getAllAccounts() {
120
+ const db = getDb();
121
+ const rows = db.prepare("SELECT * FROM accounts ORDER BY is_default DESC, created_at ASC").all();
122
+ return rows.map(rowToAccount);
123
+ }
124
+ /**
125
+ * Set an account as default by alias
126
+ */
127
+ export function setDefaultAccount(alias, exchange) {
128
+ const db = getDb();
129
+ // Check if account exists
130
+ const account = getAccountByAlias(alias, exchange);
131
+ if (!account) {
132
+ const exchangeLabel = exchange ? ` for exchange "${exchange}"` : "";
133
+ throw new Error(`Account with alias "${alias}" not found${exchangeLabel}`);
134
+ }
135
+ // Unset current default for this exchange only
136
+ db.prepare("UPDATE accounts SET is_default = 0 WHERE is_default = 1 AND exchange = ?").run(account.exchange);
137
+ // Set new default
138
+ db.prepare("UPDATE accounts SET is_default = 1, updated_at = strftime('%s', 'now') WHERE id = ?").run(account.id);
139
+ return getAccountById(account.id);
140
+ }
141
+ /**
142
+ * Delete an account by alias
143
+ */
144
+ export function deleteAccount(alias, exchange) {
145
+ const db = getDb();
146
+ const account = getAccountByAlias(alias, exchange);
147
+ if (!account) {
148
+ return false;
149
+ }
150
+ const wasDefault = account.isDefault;
151
+ db.prepare("DELETE FROM accounts WHERE id = ?").run(account.id);
152
+ // If deleted account was default, set the first remaining account as default
153
+ if (wasDefault) {
154
+ const firstAccount = db
155
+ .prepare("SELECT * FROM accounts WHERE exchange = ? ORDER BY created_at ASC LIMIT 1")
156
+ .get(account.exchange);
157
+ if (firstAccount) {
158
+ db.prepare("UPDATE accounts SET is_default = 1 WHERE id = ?").run(firstAccount.id);
159
+ }
160
+ }
161
+ return true;
162
+ }
163
+ /**
164
+ * Check if an alias is already taken
165
+ */
166
+ export function isAliasTaken(alias, exchange) {
167
+ const db = getDb();
168
+ const row = exchange
169
+ ? db.prepare("SELECT 1 FROM accounts WHERE alias = ? AND exchange = ?").get(alias, exchange)
170
+ : db.prepare("SELECT 1 FROM accounts WHERE alias = ?").get(alias);
171
+ return row !== undefined;
172
+ }
173
+ /**
174
+ * Update an account's API wallet credentials
175
+ */
176
+ export function updateAccountApiWallet(alias, apiWalletPrivateKey, apiWalletPublicKey, exchange) {
177
+ const db = getDb();
178
+ const account = getAccountByAlias(alias, exchange);
179
+ if (!account) {
180
+ const exchangeLabel = exchange ? ` for exchange "${exchange}"` : "";
181
+ throw new Error(`Account with alias "${alias}" not found${exchangeLabel}`);
182
+ }
183
+ db.prepare(`
184
+ UPDATE accounts
185
+ SET
186
+ api_wallet_private_key = ?,
187
+ api_wallet_public_key = ?,
188
+ type = 'api_wallet',
189
+ updated_at = strftime('%s', 'now')
190
+ WHERE id = ?
191
+ `).run(encryptSecret(apiWalletPrivateKey), apiWalletPublicKey, account.id);
192
+ return getAccountById(account.id);
193
+ }
194
+ /**
195
+ * Get account count
196
+ */
197
+ export function getAccountCount(exchange) {
198
+ const db = getDb();
199
+ const result = exchange
200
+ ? db.prepare("SELECT COUNT(*) as count FROM accounts WHERE exchange = ?").get(exchange)
201
+ : db.prepare("SELECT COUNT(*) as count FROM accounts").get();
202
+ return result.count;
203
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Funding History Database Operations
3
+ * Store and retrieve funding rate history for arbitrage analysis
4
+ */
5
+ export interface FundingRecord {
6
+ id?: number;
7
+ exchange: string;
8
+ market: string;
9
+ rate: number;
10
+ annualized: number;
11
+ nextFunding?: number;
12
+ recordedAt: number;
13
+ }
14
+ export interface FundingSpread {
15
+ market: string;
16
+ highExchange: string;
17
+ highRate: number;
18
+ lowExchange: string;
19
+ lowRate: number;
20
+ spread: number;
21
+ annualizedSpread: number;
22
+ recordedAt: number;
23
+ }
24
+ /**
25
+ * Store a funding rate record
26
+ */
27
+ export declare function storeFundingRate(record: Omit<FundingRecord, "id">): number;
28
+ /**
29
+ * Store multiple funding rate records
30
+ */
31
+ export declare function storeFundingRates(records: Omit<FundingRecord, "id">[]): void;
32
+ /**
33
+ * Get funding history for a specific market across all exchanges
34
+ */
35
+ export declare function getFundingHistory(market: string, options?: {
36
+ hours?: number;
37
+ exchanges?: string[];
38
+ }): FundingRecord[];
39
+ /**
40
+ * Get latest funding rate for each exchange for a market
41
+ */
42
+ export declare function getLatestFundingRates(market: string): FundingRecord[];
43
+ /**
44
+ * Calculate average funding spreads over time
45
+ */
46
+ export declare function getFundingSpreads(market: string, hours?: number): FundingSpread[];
47
+ /**
48
+ * Get average spread statistics
49
+ */
50
+ export declare function getSpreadStats(market: string, hours?: number): {
51
+ avgSpread: number;
52
+ avgAnnualized: number;
53
+ minSpread: number;
54
+ maxSpread: number;
55
+ consistency: number;
56
+ samples: number;
57
+ bestPair: {
58
+ high: string;
59
+ low: string;
60
+ } | null;
61
+ };
62
+ /**
63
+ * Clean up old records (keep last N days)
64
+ */
65
+ export declare function cleanupOldRecords(daysToKeep?: number): number;
66
+ /**
67
+ * Get record count
68
+ */
69
+ export declare function getRecordCount(): number;
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Funding History Database Operations
3
+ * Store and retrieve funding rate history for arbitrage analysis
4
+ */
5
+ import { getDb } from "./index.js";
6
+ /**
7
+ * Store a funding rate record
8
+ */
9
+ export function storeFundingRate(record) {
10
+ const db = getDb();
11
+ const stmt = db.prepare(`
12
+ INSERT INTO funding_history (exchange, market, rate, annualized, next_funding, recorded_at)
13
+ VALUES (?, ?, ?, ?, ?, ?)
14
+ `);
15
+ const result = stmt.run(record.exchange, record.market, record.rate, record.annualized, record.nextFunding || null, record.recordedAt);
16
+ return result.lastInsertRowid;
17
+ }
18
+ /**
19
+ * Store multiple funding rate records
20
+ */
21
+ export function storeFundingRates(records) {
22
+ const db = getDb();
23
+ const stmt = db.prepare(`
24
+ INSERT INTO funding_history (exchange, market, rate, annualized, next_funding, recorded_at)
25
+ VALUES (?, ?, ?, ?, ?, ?)
26
+ `);
27
+ const insertMany = db.transaction((items) => {
28
+ for (const r of items) {
29
+ stmt.run(r.exchange, r.market, r.rate, r.annualized, r.nextFunding || null, r.recordedAt);
30
+ }
31
+ });
32
+ insertMany(records);
33
+ }
34
+ /**
35
+ * Get funding history for a specific market across all exchanges
36
+ */
37
+ export function getFundingHistory(market, options = {}) {
38
+ const db = getDb();
39
+ const hours = options.hours || 24;
40
+ const cutoff = Date.now() - hours * 60 * 60 * 1000;
41
+ let sql = `
42
+ SELECT id, exchange, market, rate, annualized, next_funding as nextFunding, recorded_at as recordedAt
43
+ FROM funding_history
44
+ WHERE market = ? AND recorded_at >= ?
45
+ `;
46
+ const params = [market, cutoff];
47
+ if (options.exchanges && options.exchanges.length > 0) {
48
+ sql += ` AND exchange IN (${options.exchanges.map(() => "?").join(",")})`;
49
+ params.push(...options.exchanges);
50
+ }
51
+ sql += " ORDER BY recorded_at DESC";
52
+ return db.prepare(sql).all(...params);
53
+ }
54
+ /**
55
+ * Get latest funding rate for each exchange for a market
56
+ */
57
+ export function getLatestFundingRates(market) {
58
+ const db = getDb();
59
+ const sql = `
60
+ SELECT f.id, f.exchange, f.market, f.rate, f.annualized,
61
+ f.next_funding as nextFunding, f.recorded_at as recordedAt
62
+ FROM funding_history f
63
+ INNER JOIN (
64
+ SELECT exchange, MAX(recorded_at) as max_time
65
+ FROM funding_history
66
+ WHERE market = ?
67
+ GROUP BY exchange
68
+ ) latest ON f.exchange = latest.exchange AND f.recorded_at = latest.max_time
69
+ WHERE f.market = ?
70
+ ORDER BY f.rate DESC
71
+ `;
72
+ return db.prepare(sql).all(market, market);
73
+ }
74
+ /**
75
+ * Calculate average funding spreads over time
76
+ */
77
+ export function getFundingSpreads(market, hours = 24) {
78
+ const db = getDb();
79
+ const cutoff = Date.now() - hours * 60 * 60 * 1000;
80
+ // Get all records grouped by time bucket (hourly)
81
+ const sql = `
82
+ SELECT
83
+ exchange,
84
+ market,
85
+ rate,
86
+ annualized,
87
+ recorded_at as recordedAt,
88
+ (recorded_at / 3600000) * 3600000 as bucket -- Round to hour
89
+ FROM funding_history
90
+ WHERE market = ? AND recorded_at >= ?
91
+ ORDER BY bucket, rate DESC
92
+ `;
93
+ const records = db.prepare(sql).all(market, cutoff);
94
+ // Group by bucket and calculate spreads
95
+ const buckets = new Map();
96
+ for (const r of records) {
97
+ const existing = buckets.get(r.bucket) || [];
98
+ existing.push(r);
99
+ buckets.set(r.bucket, existing);
100
+ }
101
+ const spreads = [];
102
+ for (const [bucket, rates] of buckets) {
103
+ if (rates.length < 2)
104
+ continue;
105
+ const sorted = [...rates].sort((a, b) => b.rate - a.rate);
106
+ const high = sorted[0];
107
+ const low = sorted[sorted.length - 1];
108
+ spreads.push({
109
+ market,
110
+ highExchange: high.exchange,
111
+ highRate: high.rate,
112
+ lowExchange: low.exchange,
113
+ lowRate: low.rate,
114
+ spread: high.rate - low.rate,
115
+ annualizedSpread: (high.rate - low.rate) * 24 * 365,
116
+ recordedAt: bucket,
117
+ });
118
+ }
119
+ return spreads.sort((a, b) => b.recordedAt - a.recordedAt);
120
+ }
121
+ /**
122
+ * Get average spread statistics
123
+ */
124
+ export function getSpreadStats(market, hours = 24) {
125
+ const spreads = getFundingSpreads(market, hours);
126
+ if (spreads.length === 0) {
127
+ return {
128
+ avgSpread: 0,
129
+ avgAnnualized: 0,
130
+ minSpread: 0,
131
+ maxSpread: 0,
132
+ consistency: 0,
133
+ samples: 0,
134
+ bestPair: null,
135
+ };
136
+ }
137
+ const avgSpread = spreads.reduce((sum, s) => sum + s.spread, 0) / spreads.length;
138
+ const avgAnnualized = spreads.reduce((sum, s) => sum + s.annualizedSpread, 0) / spreads.length;
139
+ const minSpread = Math.min(...spreads.map(s => s.spread));
140
+ const maxSpread = Math.max(...spreads.map(s => s.spread));
141
+ const positiveCount = spreads.filter(s => s.spread > 0.00001).length; // > 0.001%
142
+ // Find most common high/low pair
143
+ const pairCounts = new Map();
144
+ for (const s of spreads) {
145
+ const key = `${s.highExchange}|${s.lowExchange}`;
146
+ pairCounts.set(key, (pairCounts.get(key) || 0) + 1);
147
+ }
148
+ let bestPair = null;
149
+ let maxCount = 0;
150
+ for (const [key, count] of pairCounts) {
151
+ if (count > maxCount) {
152
+ maxCount = count;
153
+ const [high, low] = key.split("|");
154
+ bestPair = { high, low };
155
+ }
156
+ }
157
+ return {
158
+ avgSpread,
159
+ avgAnnualized,
160
+ minSpread,
161
+ maxSpread,
162
+ consistency: (positiveCount / spreads.length) * 100,
163
+ samples: spreads.length,
164
+ bestPair,
165
+ };
166
+ }
167
+ /**
168
+ * Clean up old records (keep last N days)
169
+ */
170
+ export function cleanupOldRecords(daysToKeep = 7) {
171
+ const db = getDb();
172
+ const cutoff = Date.now() - daysToKeep * 24 * 60 * 60 * 1000;
173
+ const result = db.prepare("DELETE FROM funding_history WHERE recorded_at < ?").run(cutoff);
174
+ return result.changes;
175
+ }
176
+ /**
177
+ * Get record count
178
+ */
179
+ export function getRecordCount() {
180
+ const db = getDb();
181
+ const result = db.prepare("SELECT COUNT(*) as count FROM funding_history").get();
182
+ return result.count;
183
+ }
@@ -0,0 +1,11 @@
1
+ import Database from "better-sqlite3";
2
+ /**
3
+ * Get or create the database connection
4
+ */
5
+ export declare function getDb(): Database.Database;
6
+ /**
7
+ * Close the database connection
8
+ */
9
+ export declare function closeDb(): void;
10
+ export * from "./accounts.js";
11
+ export * from "./funding-history.js";