@tradejs/app 2.0.21 → 3.0.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 (25) hide show
  1. package/package.json +8 -8
  2. package/src/app/actions/backtest.ts +1 -1
  3. package/src/app/actions/strategies.ts +1 -1
  4. package/src/app/api/ai/route.ts +13 -6
  5. package/src/app/api/user/settings/route.ts +48 -28
  6. package/src/app/components/Shared/OrdersDrawer.tsx +4 -2
  7. package/src/app/components/Shared/Sidebar/AccountSettingsDrawer.tsx +3 -3
  8. package/src/app/components/Strategies/RuntimeStrategyCard.presenter.ts +536 -0
  9. package/src/app/components/Strategies/RuntimeStrategyCard.tsx +16 -1207
  10. package/src/app/components/Strategies/RuntimeStrategyConfigDrawer.tsx +1 -1
  11. package/src/app/components/Strategies/RuntimeStrategyStatsDrawer.tsx +677 -0
  12. package/src/app/components/Strategies/StrategySnapshotCard.details.presenter.ts +38 -0
  13. package/src/app/components/Strategies/StrategySnapshotCard.diagnostics.presenter.ts +263 -0
  14. package/src/app/components/Strategies/StrategySnapshotCard.orders.presenter.ts +680 -0
  15. package/src/app/components/Strategies/StrategySnapshotCard.presenter.ts +119 -0
  16. package/src/app/components/Strategies/StrategySnapshotCard.ranking.presenter.ts +76 -0
  17. package/src/app/components/Strategies/StrategySnapshotCard.tsx +13 -1793
  18. package/src/app/components/Strategies/StrategySnapshotCardDetailsDrawer.tsx +682 -0
  19. package/src/app/lib/runtimeStrategies.ts +7 -80
  20. package/src/app/lib/runtimeStrategyContracts.ts +85 -0
  21. package/src/app/routes/backtest/BacktestJobItem.tsx +275 -0
  22. package/src/app/routes/backtest/BacktestRunForm.tsx +502 -0
  23. package/src/app/routes/backtest/page.tsx +16 -1099
  24. package/src/app/routes/backtest/useBacktestRunsController.ts +441 -0
  25. package/src/app/routes/strategies/StrategiesPageClient.tsx +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/app",
3
- "version": "2.0.21",
3
+ "version": "3.0.0",
4
4
  "description": "Installable Next.js UI for the TradeJS TypeScript framework: dashboards, backtests, charts, and runtime data.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -51,13 +51,13 @@
51
51
  "@emotion/react": "^11.14.0",
52
52
  "@langchain/core": "^1.2.3",
53
53
  "@langchain/openai": "^1.5.5",
54
- "@tradejs/connectors": "^2.0.21",
55
- "@tradejs/core": "^2.0.21",
56
- "@tradejs/indicators": "^2.0.21",
57
- "@tradejs/infra": "^2.0.21",
58
- "@tradejs/node": "^2.0.21",
59
- "@tradejs/strategies": "^2.0.21",
60
- "@tradejs/types": "^2.0.21",
54
+ "@tradejs/connectors": "^3.0.0",
55
+ "@tradejs/core": "^3.0.0",
56
+ "@tradejs/indicators": "^3.0.0",
57
+ "@tradejs/infra": "^3.0.0",
58
+ "@tradejs/node": "^3.0.0",
59
+ "@tradejs/strategies": "^3.0.0",
60
+ "@tradejs/types": "^3.0.0",
61
61
  "@types/bcryptjs": "2.4.6",
62
62
  "@types/lodash": "4.17.24",
63
63
  "@types/node": "24.13.3",
@@ -5,7 +5,7 @@ import type {
5
5
  BacktestConfigSummary,
6
6
  BacktestJobRecord,
7
7
  BacktestJobRequest,
8
- } from '#app/lib/backtestJobs';
8
+ } from '#app/lib/backtestJobContracts';
9
9
 
10
10
  const API_BASE = '/api/backtest';
11
11
 
@@ -1,6 +1,6 @@
1
1
  import { API } from '@tradejs/core/api';
2
2
  import type { StrategyChartsSnapshotResponse } from '@tradejs/types';
3
- import type { RuntimeStrategiesResponse } from '#app/lib/runtimeStrategies';
3
+ import type { RuntimeStrategiesResponse } from '#app/lib/runtimeStrategyContracts';
4
4
 
5
5
  const API_PATH = '/api/strategies/runtime';
6
6
  const REPLAY_API_PATH = '/api/strategies/replay';
@@ -7,7 +7,12 @@ import {
7
7
  } from '@langchain/core/messages';
8
8
  import { TTL_1M } from '@tradejs/core/constants';
9
9
  import { toJson } from '@tradejs/core/data';
10
- import { getAiResponseLanguagePromptName } from '@tradejs/infra/aiLanguages';
10
+ import {
11
+ getAiResponseLanguagePromptName,
12
+ normalizeAiResponseLanguage,
13
+ } from '@tradejs/core/aiLanguages';
14
+ import { normalizeAiEndpoint } from '@tradejs/core/aiEndpoints';
15
+ import { normalizeAiModel } from '@tradejs/core/aiModels';
11
16
  import { DEFAULT_AI_MODEL, getOpenRouterModelKwargs } from '@tradejs/node/ai';
12
17
  import {
13
18
  AIChatHistory,
@@ -98,19 +103,21 @@ const buildMessages = (
98
103
 
99
104
  const invokeChatModel = async (messages: BaseMessage[], userName: string) => {
100
105
  const settings = await getUserSettings(userName);
101
- if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
106
+ const endpoint = normalizeAiEndpoint(settings.AI_API_ENDPOINT);
107
+ if (!settings.AI_API_KEY || !endpoint) {
102
108
  throw new Error(`AI settings are incomplete for user ${userName}`);
103
109
  }
104
110
 
105
- const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
111
+ const modelKwargs = getOpenRouterModelKwargs(endpoint);
106
112
 
107
113
  const model = new ChatOpenAI({
108
114
  temperature: 0.7,
109
- modelName: settings.AI_MODEL || DEFAULT_AI_MODEL,
115
+ modelName:
116
+ normalizeAiModel(settings.AI_MODEL, endpoint) || DEFAULT_AI_MODEL,
110
117
  apiKey: settings.AI_API_KEY,
111
118
  ...(Object.keys(modelKwargs).length ? { modelKwargs } : {}),
112
119
  configuration: {
113
- baseURL: settings.AI_API_ENDPOINT,
120
+ baseURL: endpoint,
114
121
  },
115
122
  });
116
123
 
@@ -200,7 +207,7 @@ export const POST = async (request: NextRequest) => {
200
207
  filters,
201
208
  message,
202
209
  data.slice(-100),
203
- settings.AI_RESPONSE_LANGUAGE,
210
+ normalizeAiResponseLanguage(settings.AI_RESPONSE_LANGUAGE),
204
211
  );
205
212
 
206
213
  const response = await invokeChatModel(chatMessages, userName);
@@ -1,8 +1,11 @@
1
1
  import bcrypt from 'bcryptjs';
2
2
  import { NextResponse } from 'next/server';
3
- import { normalizeAiResponseLanguage } from '@tradejs/infra/aiLanguages';
4
- import { normalizeAiEndpoint } from '@tradejs/infra/aiEndpoints';
5
- import { normalizeAiModel } from '@tradejs/infra/aiModels';
3
+ import {
4
+ DEFAULT_AI_RESPONSE_LANGUAGE,
5
+ normalizeAiResponseLanguage,
6
+ } from '@tradejs/core/aiLanguages';
7
+ import { normalizeAiEndpoint } from '@tradejs/core/aiEndpoints';
8
+ import { normalizeAiModel } from '@tradejs/core/aiModels';
6
9
  import {
7
10
  getUserRecord,
8
11
  getUserSettings,
@@ -79,31 +82,46 @@ const maskSecret = (value: string) => {
79
82
  return `${'*'.repeat(12)}${trimmed.slice(-4) || trimmed}`;
80
83
  };
81
84
 
82
- const toResponse = (settings: UserSettings) => ({
83
- userName: settings.userName,
84
- settings: {
85
- bybit: {
86
- apiKey: maskSecret(settings.BYBIT_API_KEY),
87
- apiSecret: maskSecret(settings.BYBIT_API_SECRET),
88
- },
89
- coinalyze: {
90
- apiKey: maskSecret(settings.COINALYZE_API_KEY),
91
- },
92
- coinmarketcap: {
93
- apiKey: maskSecret(settings.COINMARKETCAP_API_KEY),
94
- },
95
- ai: {
96
- apiKey: maskSecret(settings.AI_API_KEY),
97
- apiEndpoint: settings.AI_API_ENDPOINT,
98
- model: settings.AI_MODEL,
99
- responseLanguage: settings.AI_RESPONSE_LANGUAGE,
100
- },
101
- telegram: {
102
- botToken: maskSecret(settings.TG_BOT_TOKEN),
103
- chatId: settings.TG_CHAT_ID,
85
+ const normalizeSettingsForResponse = (settings: UserSettings): UserSettings => {
86
+ const endpoint = normalizeAiEndpoint(settings.AI_API_ENDPOINT);
87
+ return {
88
+ ...settings,
89
+ AI_API_ENDPOINT: endpoint,
90
+ AI_MODEL: normalizeAiModel(settings.AI_MODEL, endpoint),
91
+ AI_RESPONSE_LANGUAGE:
92
+ normalizeAiResponseLanguage(settings.AI_RESPONSE_LANGUAGE) ||
93
+ DEFAULT_AI_RESPONSE_LANGUAGE,
94
+ };
95
+ };
96
+
97
+ const toResponse = (rawSettings: UserSettings) => {
98
+ const settings = normalizeSettingsForResponse(rawSettings);
99
+ return {
100
+ userName: settings.userName,
101
+ settings: {
102
+ bybit: {
103
+ apiKey: maskSecret(settings.BYBIT_API_KEY),
104
+ apiSecret: maskSecret(settings.BYBIT_API_SECRET),
105
+ },
106
+ coinalyze: {
107
+ apiKey: maskSecret(settings.COINALYZE_API_KEY),
108
+ },
109
+ coinmarketcap: {
110
+ apiKey: maskSecret(settings.COINMARKETCAP_API_KEY),
111
+ },
112
+ ai: {
113
+ apiKey: maskSecret(settings.AI_API_KEY),
114
+ apiEndpoint: settings.AI_API_ENDPOINT,
115
+ model: settings.AI_MODEL,
116
+ responseLanguage: settings.AI_RESPONSE_LANGUAGE,
117
+ },
118
+ telegram: {
119
+ botToken: maskSecret(settings.TG_BOT_TOKEN),
120
+ chatId: settings.TG_CHAT_ID,
121
+ },
104
122
  },
105
- },
106
- });
123
+ };
124
+ };
107
125
 
108
126
  const hasKeys = (patch: Partial<UserRecord>) => Object.keys(patch).length > 0;
109
127
 
@@ -198,7 +216,9 @@ export const PATCH = async (request: Request) => {
198
216
  }
199
217
 
200
218
  if (body.section === 'ai') {
201
- const currentSettings = await getUserSettings(userName);
219
+ const currentSettings = normalizeSettingsForResponse(
220
+ await getUserSettings(userName),
221
+ );
202
222
  const patch: Partial<UserRecord> = {};
203
223
  const apiKey = cleanOptionalText(body.data?.apiKey);
204
224
  const apiEndpoint = normalizeAiEndpoint(body.data?.apiEndpoint);
@@ -25,6 +25,7 @@ export interface OrdersDrawerMetric {
25
25
  title: string;
26
26
  value: ReactNode;
27
27
  detail?: ReactNode;
28
+ detailLines?: readonly string[];
28
29
  color?: string;
29
30
  }
30
31
 
@@ -339,6 +340,7 @@ const OrderMetric = ({
339
340
  title,
340
341
  value,
341
342
  detail,
343
+ detailLines,
342
344
  color = 'gray.300',
343
345
  }: OrdersDrawerMetric) => (
344
346
  <Box minW="0">
@@ -362,7 +364,7 @@ const OrderMetric = ({
362
364
  >
363
365
  {value}
364
366
  </Text>
365
- {detail ? (
367
+ {detail || detailLines?.length ? (
366
368
  <Box
367
369
  mt={1}
368
370
  color="gray.500"
@@ -370,7 +372,7 @@ const OrderMetric = ({
370
372
  lineHeight="1.3"
371
373
  wordBreak="break-word"
372
374
  >
373
- {detail}
375
+ {detailLines?.map((line) => <Text key={line}>{line}</Text>) ?? detail}
374
376
  </Box>
375
377
  ) : null}
376
378
  </Box>
@@ -20,18 +20,18 @@ import { FiEdit2, FiSettings } from 'react-icons/fi';
20
20
  import {
21
21
  AI_RESPONSE_LANGUAGE_OPTIONS,
22
22
  normalizeAiResponseLanguage,
23
- } from '@tradejs/infra/aiLanguages';
23
+ } from '@tradejs/core/aiLanguages';
24
24
  import {
25
25
  AI_CUSTOM_ENDPOINT_VALUE,
26
26
  AI_ENDPOINT_OPTIONS,
27
27
  normalizeAiEndpoint,
28
- } from '@tradejs/infra/aiEndpoints';
28
+ } from '@tradejs/core/aiEndpoints';
29
29
  import {
30
30
  AI_CUSTOM_MODEL_VALUE,
31
31
  getAiModelOptionsForEndpoint,
32
32
  hasPresetAiModelsForEndpoint,
33
33
  normalizeAiModel,
34
- } from '@tradejs/infra/aiModels';
34
+ } from '@tradejs/core/aiModels';
35
35
  import { toaster } from '#ui';
36
36
  import { TradingAccountsPanel } from './TradingAccountsPanel';
37
37