@tradejs/infra 1.0.9 → 1.0.11

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.
@@ -0,0 +1,190 @@
1
+ import {
2
+ delKey,
3
+ getData,
4
+ getKeys,
5
+ redisKeys,
6
+ setData
7
+ } from "./chunk-XZQ6COOV.mjs";
8
+
9
+ // src/tradingAccounts.ts
10
+ var normalizeId = (value, label) => {
11
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-");
12
+ if (!normalized) {
13
+ throw new Error(`${label} is required`);
14
+ }
15
+ return normalized;
16
+ };
17
+ var isTradingAccount = (value) => Boolean(
18
+ value && typeof value === "object" && typeof value.id === "string" && typeof value.provider === "string"
19
+ );
20
+ var isRuntimeDeployment = (value) => Boolean(
21
+ value && typeof value === "object" && typeof value.id === "string" && typeof value.accountId === "string"
22
+ );
23
+ var withProviderUniverses = (account) => account.provider.toLowerCase() === "bybit" ? { ...account, universes: ["crypto", "tradfi"] } : account;
24
+ var listTradingAccounts = async (userName) => {
25
+ if (typeof redisKeys.tradingAccounts !== "function") return [];
26
+ const keys = await getKeys(redisKeys.tradingAccounts(userName));
27
+ const values = await Promise.all(keys.map((key) => getData(key, null)));
28
+ return values.filter(isTradingAccount).map(withProviderUniverses).sort((left, right) => left.label.localeCompare(right.label));
29
+ };
30
+ var getTradingAccount = async (userName, accountId) => {
31
+ const normalizedId = normalizeId(accountId, "Account id");
32
+ const value = await getData(
33
+ redisKeys.tradingAccount(userName, normalizedId),
34
+ null
35
+ );
36
+ return isTradingAccount(value) ? withProviderUniverses(value) : null;
37
+ };
38
+ var saveTradingAccount = async (userName, account) => {
39
+ const normalized = withProviderUniverses({
40
+ ...account,
41
+ id: normalizeId(account.id, "Account id"),
42
+ label: account.label.trim(),
43
+ provider: account.provider.trim().toLowerCase(),
44
+ universes: [...new Set(account.universes)]
45
+ });
46
+ if (normalized.isDefault) {
47
+ const previousDefaults = (await listTradingAccounts(userName)).filter(
48
+ (candidate) => candidate.provider === normalized.provider && candidate.id !== normalized.id && candidate.isDefault && candidate.universes.some(
49
+ (universe) => normalized.universes.includes(universe)
50
+ )
51
+ );
52
+ await Promise.all(
53
+ previousDefaults.map(
54
+ (candidate) => setData(
55
+ redisKeys.tradingAccount(userName, candidate.id),
56
+ { ...candidate, isDefault: false },
57
+ { expire: 0 }
58
+ )
59
+ )
60
+ );
61
+ }
62
+ await setData(redisKeys.tradingAccount(userName, normalized.id), normalized, {
63
+ expire: 0
64
+ });
65
+ return normalized;
66
+ };
67
+ var deleteTradingAccount = async (userName, accountId) => delKey(
68
+ redisKeys.tradingAccount(userName, normalizeId(accountId, "Account id"))
69
+ );
70
+ var resolveTradingAccount = async ({
71
+ userName,
72
+ accountId,
73
+ provider,
74
+ universe
75
+ }) => {
76
+ if (accountId) {
77
+ const account = await getTradingAccount(userName, accountId);
78
+ if (!account) {
79
+ throw new Error(`Trading account not found: ${accountId}`);
80
+ }
81
+ if (account.provider !== provider.toLowerCase()) {
82
+ throw new Error(
83
+ `Trading account ${accountId} belongs to ${account.provider}, not ${provider}`
84
+ );
85
+ }
86
+ if (!account.enabled) {
87
+ throw new Error(`Trading account is disabled: ${accountId}`);
88
+ }
89
+ if (universe && !account.universes.includes(universe)) {
90
+ throw new Error(
91
+ `Trading account ${accountId} does not support universe ${universe}`
92
+ );
93
+ }
94
+ return account;
95
+ }
96
+ const accounts = (await listTradingAccounts(userName)).filter(
97
+ (account) => account.enabled && account.provider === provider.toLowerCase() && (!universe || account.universes.includes(universe))
98
+ );
99
+ const selected = accounts.find((account) => account.isDefault) ?? (accounts.length === 1 ? accounts[0] : null);
100
+ if (selected) return selected;
101
+ if (provider.toLowerCase() !== "bybit" || accounts.length > 1) {
102
+ return null;
103
+ }
104
+ const legacy = await getData(redisKeys.user(userName));
105
+ const legacyApiKey = String(legacy?.BYBIT_API_KEY ?? "").trim();
106
+ const legacyApiSecret = String(legacy?.BYBIT_API_SECRET ?? "").trim();
107
+ if (!legacyApiKey || !legacyApiSecret) {
108
+ return null;
109
+ }
110
+ return {
111
+ id: "bybit-default",
112
+ label: "Bybit Default",
113
+ provider: "bybit",
114
+ enabled: true,
115
+ isDefault: true,
116
+ universes: ["crypto", "tradfi"],
117
+ environment: "mainnet",
118
+ apiKey: legacyApiKey,
119
+ apiSecret: legacyApiSecret
120
+ };
121
+ };
122
+ var listRuntimeDeployments = async (userName) => {
123
+ const keys = await getKeys(redisKeys.runtimeDeployments(userName));
124
+ const values = await Promise.all(keys.map((key) => getData(key, null)));
125
+ return values.filter(isRuntimeDeployment).sort((left, right) => left.label.localeCompare(right.label));
126
+ };
127
+ var getRuntimeDeployment = async (userName, deploymentId) => {
128
+ const normalizedId = normalizeId(deploymentId, "Deployment id");
129
+ const value = await getData(
130
+ redisKeys.runtimeDeployment(userName, normalizedId),
131
+ null
132
+ );
133
+ return isRuntimeDeployment(value) ? value : null;
134
+ };
135
+ var saveRuntimeDeployment = async (userName, deployment) => {
136
+ const normalized = {
137
+ ...deployment,
138
+ id: normalizeId(deployment.id, "Deployment id"),
139
+ label: deployment.label.trim(),
140
+ provider: deployment.provider.trim().toLowerCase(),
141
+ accountId: normalizeId(deployment.accountId, "Account id")
142
+ };
143
+ await setData(
144
+ redisKeys.runtimeDeployment(userName, normalized.id),
145
+ normalized,
146
+ { expire: 0 }
147
+ );
148
+ return normalized;
149
+ };
150
+ var deleteRuntimeDeployment = async (userName, deploymentId) => {
151
+ const normalizedId = normalizeId(deploymentId, "Deployment id");
152
+ await Promise.all([
153
+ delKey(redisKeys.runtimeDeployment(userName, normalizedId)),
154
+ delKey(redisKeys.runtimeDeploymentHeartbeat(userName, normalizedId))
155
+ ]);
156
+ };
157
+ var getRuntimeDeploymentHeartbeat = async (userName, deploymentId) => {
158
+ const value = await getData(
159
+ redisKeys.runtimeDeploymentHeartbeat(
160
+ userName,
161
+ normalizeId(deploymentId, "Deployment id")
162
+ ),
163
+ null
164
+ );
165
+ return value && typeof value === "object" ? value : null;
166
+ };
167
+ var saveRuntimeDeploymentHeartbeat = async (userName, heartbeat) => {
168
+ await setData(
169
+ redisKeys.runtimeDeploymentHeartbeat(
170
+ userName,
171
+ normalizeId(heartbeat.deploymentId, "Deployment id")
172
+ ),
173
+ heartbeat,
174
+ { expire: 0 }
175
+ );
176
+ return heartbeat;
177
+ };
178
+ export {
179
+ deleteRuntimeDeployment,
180
+ deleteTradingAccount,
181
+ getRuntimeDeployment,
182
+ getRuntimeDeploymentHeartbeat,
183
+ getTradingAccount,
184
+ listRuntimeDeployments,
185
+ listTradingAccounts,
186
+ resolveTradingAccount,
187
+ saveRuntimeDeployment,
188
+ saveRuntimeDeploymentHeartbeat,
189
+ saveTradingAccount
190
+ };
@@ -4,6 +4,7 @@ interface UserRecord extends Record<string, unknown> {
4
4
  BYBIT_API_KEY?: string;
5
5
  BYBIT_API_SECRET?: string;
6
6
  COINALYZE_API_KEY?: string;
7
+ COINMARKETCAP_API_KEY?: string;
7
8
  AI_API_KEY?: string;
8
9
  AI_API_ENDPOINT?: string;
9
10
  AI_MODEL?: string;
@@ -17,6 +18,7 @@ interface UserSettings {
17
18
  BYBIT_API_KEY: string;
18
19
  BYBIT_API_SECRET: string;
19
20
  COINALYZE_API_KEY: string;
21
+ COINMARKETCAP_API_KEY: string;
20
22
  AI_API_KEY: string;
21
23
  AI_API_ENDPOINT: string;
22
24
  AI_MODEL: string;
@@ -4,6 +4,7 @@ interface UserRecord extends Record<string, unknown> {
4
4
  BYBIT_API_KEY?: string;
5
5
  BYBIT_API_SECRET?: string;
6
6
  COINALYZE_API_KEY?: string;
7
+ COINMARKETCAP_API_KEY?: string;
7
8
  AI_API_KEY?: string;
8
9
  AI_API_ENDPOINT?: string;
9
10
  AI_MODEL?: string;
@@ -17,6 +18,7 @@ interface UserSettings {
17
18
  BYBIT_API_KEY: string;
18
19
  BYBIT_API_SECRET: string;
19
20
  COINALYZE_API_KEY: string;
21
+ COINMARKETCAP_API_KEY: string;
20
22
  AI_API_KEY: string;
21
23
  AI_API_ENDPOINT: string;
22
24
  AI_MODEL: string;
@@ -354,9 +354,12 @@ var logger = {
354
354
  };
355
355
  var redisConnectionWarningShown = false;
356
356
  var redisUnavailable = false;
357
- var isRedisConnectivityError = (error) => /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
358
- error.message
359
- );
357
+ var isRedisConnectivityError = (error) => {
358
+ const errorText = [error.name, error.message, String(error)].join(" ");
359
+ return /ECONNREFUSED|ECONNRESET|ECONNABORTED|EPIPE|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|MaxRetriesPerRequestError|Connection is closed|Stream isn't writeable/i.test(
360
+ errorText
361
+ );
362
+ };
360
363
  var toNonNegativeInt = (value, fallback) => {
361
364
  const parsed = Number.parseInt(String(value ?? ""), 10);
362
365
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
@@ -538,13 +541,21 @@ var setData = async (key, data, options = {}) => {
538
541
  var redisKeys = {
539
542
  users: () => "users:index:",
540
543
  user: (userName) => `users:index:${userName}`,
544
+ tradingAccounts: (userName) => `users:${userName}:trading-accounts:`,
545
+ tradingAccount: (userName, accountId) => `users:${userName}:trading-accounts:${accountId}`,
546
+ runtimeDeployments: (userName) => `users:${userName}:runtime:deployments:`,
547
+ runtimeDeployment: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}`,
548
+ runtimeDeploymentHeartbeat: (userName, deploymentId) => `users:${userName}:runtime:deployments:${deploymentId}:heartbeat`,
541
549
  bots: (userName) => `users:${userName}:bots`,
542
550
  botsPrefix: () => "users:",
543
551
  bot: (userName, botId) => `users:${userName}:bots:${botId}`,
544
552
  backtestConfig: (userName, config) => `users:${userName}:backtests:configs:${config}`,
545
553
  strategies: (userName) => `users:${userName}:strategies`,
546
- strategyConfig: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:config`,
554
+ strategyConfig: (userName, strategyName, configId = "config") => `users:${userName}:strategies:${strategyName}:${configId}`,
547
555
  strategyResults: (userName, strategyName) => `users:${userName}:strategies:${strategyName}:results`,
556
+ strategyCharts: (userName, mode) => `users:${userName}:strategies:charts:${mode}`,
557
+ strategyChartCards: (userName, mode) => `users:${userName}:strategies:charts:${mode}:cards:`,
558
+ strategyChartCard: (userName, mode, cardId) => `users:${userName}:strategies:charts:${mode}:cards:${cardId}`,
548
559
  tests: (userName, strategyName) => strategyName ? `users:${userName}:tests:${strategyName}` : `users:${userName}:tests:`,
549
560
  testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
550
561
  testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
@@ -553,6 +564,7 @@ var redisKeys = {
553
564
  cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
554
565
  cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
555
566
  cachePositions: (userName, orderLogId) => `users:${userName}:cache:tests:positions:${orderLogId}`,
567
+ tickerUniverse: (userName, connectorName, universe, accountId) => universe || accountId ? `users:${userName}:cache:tickers:${connectorName}:${universe ?? "crypto"}:${accountId ?? "default"}` : `users:${userName}:cache:tickers:${connectorName}`,
556
568
  signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
557
569
  signalsBySymbol: (symbol) => `signals:${symbol}:`,
558
570
  storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
@@ -568,12 +580,20 @@ var redisKeys = {
568
580
  runtimeSignalEvaluationStatsBucket: (userName, dayKey, strategyName) => `users:${userName}:runtime:signal-evaluation-stats:days:${dayKey}:${strategyName}`,
569
581
  runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
570
582
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
583
+ runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
584
+ runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
571
585
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
572
- runtimeActiveTrade: (userName, symbol) => `users:${userName}:runtime:active-trades:${symbol}`,
586
+ runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
573
587
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
574
588
  analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
575
589
  screenshotSessionToken: (token) => `auth:screenshot:${token}`,
576
590
  backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
591
+ backtestJobs: (userName) => `users:${userName}:backtests:jobs:`,
592
+ backtestJob: (userName, jobId) => `users:${userName}:backtests:jobs:${jobId}`,
593
+ backtestRuns: (userName) => `users:${userName}:backtests:runs:`,
594
+ backtestRun: (userName, runId) => `users:${userName}:backtests:runs:${runId}`,
595
+ backtestRunResults: (userName, runId) => `users:${userName}:backtests:runs:${runId}:results`,
596
+ backtestLatestRun: (userName, config) => `users:${userName}:backtests:latest:${config}`,
577
597
  researchRuns: (userName) => `users:${userName}:research:runs:`,
578
598
  researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
579
599
  researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
@@ -605,6 +625,7 @@ var getUserSettings = async (userName) => {
605
625
  BYBIT_API_KEY: readUserString(record, "BYBIT_API_KEY"),
606
626
  BYBIT_API_SECRET: readUserString(record, "BYBIT_API_SECRET"),
607
627
  COINALYZE_API_KEY: readUserString(record, "COINALYZE_API_KEY"),
628
+ COINMARKETCAP_API_KEY: readUserString(record, "COINMARKETCAP_API_KEY"),
608
629
  AI_API_KEY: readUserString(record, "AI_API_KEY"),
609
630
  AI_API_ENDPOINT: aiApiEndpoint,
610
631
  AI_MODEL: normalizeAiModel(
@@ -2,7 +2,7 @@ import {
2
2
  getData,
3
3
  redisKeys,
4
4
  setData
5
- } from "./chunk-MLVWC2I2.mjs";
5
+ } from "./chunk-XZQ6COOV.mjs";
6
6
  import {
7
7
  DEFAULT_AI_RESPONSE_LANGUAGE,
8
8
  normalizeAiResponseLanguage
@@ -34,6 +34,7 @@ var getUserSettings = async (userName) => {
34
34
  BYBIT_API_KEY: readUserString(record, "BYBIT_API_KEY"),
35
35
  BYBIT_API_SECRET: readUserString(record, "BYBIT_API_SECRET"),
36
36
  COINALYZE_API_KEY: readUserString(record, "COINALYZE_API_KEY"),
37
+ COINMARKETCAP_API_KEY: readUserString(record, "COINMARKETCAP_API_KEY"),
37
38
  AI_API_KEY: readUserString(record, "AI_API_KEY"),
38
39
  AI_API_ENDPOINT: aiApiEndpoint,
39
40
  AI_MODEL: normalizeAiModel(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/infra",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Server-only infrastructure adapters for the TradeJS open-source framework: Redis, Timescale, ML, logging, and IO.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git+https://github.com/tradejs-dev/tradejs.git",
16
+ "url": "https://github.com/TradeJS-Dev/TradeJS",
17
17
  "directory": "packages/infra"
18
18
  },
19
19
  "bugs": {
@@ -44,6 +44,11 @@
44
44
  "import": "./dist/aiModels.mjs",
45
45
  "require": "./dist/aiModels.js"
46
46
  },
47
+ "./backtestArtifacts": {
48
+ "types": "./dist/backtestArtifacts.d.ts",
49
+ "import": "./dist/backtestArtifacts.mjs",
50
+ "require": "./dist/backtestArtifacts.js"
51
+ },
47
52
  "./files": {
48
53
  "types": "./dist/files.d.ts",
49
54
  "import": "./dist/files.mjs",
@@ -78,12 +83,17 @@
78
83
  "types": "./dist/timescale.d.ts",
79
84
  "import": "./dist/timescale.mjs",
80
85
  "require": "./dist/timescale.js"
86
+ },
87
+ "./tradingAccounts": {
88
+ "types": "./dist/tradingAccounts.d.ts",
89
+ "import": "./dist/tradingAccounts.mjs",
90
+ "require": "./dist/tradingAccounts.js"
81
91
  }
82
92
  },
83
93
  "dependencies": {
84
94
  "@grpc/grpc-js": "^1.10.7",
85
95
  "@grpc/proto-loader": "^0.8.0",
86
- "@tradejs/types": "^1.0.9",
96
+ "@tradejs/types": "^1.0.11",
87
97
  "chalk": "4.1.2",
88
98
  "ioredis": "5.8.0",
89
99
  "pg": "8.16.3",