@tradejs/app 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.
- package/README.md +16 -3
- package/bin/tradejs-app.mjs +146 -2
- package/next.config.mjs +0 -14
- package/package.json +35 -19
- package/src/app/actions/backtest.ts +54 -0
- package/src/app/actions/kline.ts +10 -5
- package/src/app/actions/scanner.ts +8 -3
- package/src/app/actions/strategies.ts +39 -0
- package/src/app/api/ai/route.ts +5 -4
- package/src/app/api/auth/[...nextauth]/route.ts +1 -1
- package/src/app/api/backtest/configs/route.ts +25 -0
- package/src/app/api/backtest/files/route.ts +5 -2
- package/src/app/api/backtest/order-log/[strategy]/[name]/route.ts +26 -2
- package/src/app/api/backtest/result/[strategy]/[name]/route.ts +27 -2
- package/src/app/api/backtest/runs/[jobId]/route.ts +124 -0
- package/src/app/api/backtest/runs/route.ts +45 -0
- package/src/app/api/backtest/test/[strategy]/[name]/route.ts +19 -2
- package/src/app/api/derivatives/summary/route.ts +8 -1
- package/src/app/api/install/route.ts +51 -0
- package/src/app/api/kline/[provider]/[universe]/[symbol]/[interval]/route.ts +1 -0
- package/src/app/api/kline/{[provider]/[symbol]/[interval]/route.ts → handler.ts} +61 -39
- package/src/app/api/scanner/[provider]/[universe]/route.ts +1 -0
- package/src/app/api/scanner/[provider]/route.ts +30 -12
- package/src/app/api/scanner/route.ts +3 -3
- package/src/app/api/signal/[symbol]/[signalId]/route.ts +1 -1
- package/src/app/api/strategies/ai/[cardId]/route.ts +22 -0
- package/src/app/api/strategies/ai/route.ts +139 -0
- package/src/app/api/strategies/replay/[cardId]/route.ts +18 -0
- package/src/app/api/strategies/replay/route.ts +44 -0
- package/src/app/api/strategies/runtime/route.ts +831 -0
- package/src/app/api/user/runtime-deployments/[deploymentId]/route.ts +25 -0
- package/src/app/api/user/runtime-deployments/route.ts +94 -0
- package/src/app/api/user/runtime-strategy-configs/route.ts +241 -0
- package/src/app/api/user/settings/route.ts +18 -1
- package/src/app/api/user/trading-accounts/[accountId]/route.ts +35 -0
- package/src/app/api/user/trading-accounts/route.ts +134 -0
- package/src/app/auth.ts +1 -19
- package/src/app/components/Backtest/CompareList/index.tsx +1 -1
- package/src/app/components/Backtest/ResultsPageClient.tsx +376 -0
- package/src/app/components/Backtest/TestCard/ActionsMenu/index.tsx +123 -0
- package/src/app/components/Backtest/TestCard/Chart/index.tsx +1 -1
- package/src/app/components/Backtest/TestCard/CompareButton/index.tsx +1 -1
- package/src/app/components/Backtest/TestCard/ConfigDrawer/index.tsx +28 -12
- package/src/app/components/Backtest/TestCard/DeleteButton/index.tsx +33 -17
- package/src/app/components/Backtest/TestCard/FavoriteIndicator/index.tsx +2 -2
- package/src/app/components/Backtest/TestCard/OpenDashboardButton/index.tsx +10 -1
- package/src/app/components/Backtest/TestCard/OrdersDrawer/index.tsx +128 -0
- package/src/app/components/Backtest/TestCard/Root/index.tsx +18 -4
- package/src/app/components/Backtest/TestCard/Stat/index.tsx +2 -0
- package/src/app/components/Backtest/TestCard/StatDrawer/index.tsx +66 -0
- package/src/app/components/Backtest/TestCard/index.ts +6 -2
- package/src/app/components/Backtest/TestList/index.tsx +4 -9
- package/src/app/components/Dashboard/AiDrawer/index.tsx +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useBacktest.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useBbIndicator.ts +10 -5
- package/src/app/components/Dashboard/KlineChart/hooks/useBtcCorrelation.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useBtcIndicator.ts +4 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useEmaIndicator.ts +3 -1
- package/src/app/components/Dashboard/KlineChart/hooks/usePluginIndicators.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useSetup.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useSignal.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useSpreadIndicator.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useTrendLine.ts +1 -1
- package/src/app/components/Dashboard/KlineChart/hooks/useWmaIndicator.ts +3 -1
- package/src/app/components/Dashboard/KlineChart/index.tsx +21 -7
- package/src/app/components/Dashboard/MainChart/index.tsx +3 -22
- package/src/app/components/Shared/AppShell.tsx +2 -2
- package/src/app/components/Shared/BulkSelection/index.tsx +140 -0
- package/src/app/components/Shared/BulkSelection/useBulkSelection.ts +97 -0
- package/src/app/components/Shared/BulkSelection/utils.ts +126 -0
- package/src/app/components/Shared/Filters/Backtest/index.tsx +51 -51
- package/src/app/components/Shared/Filters/FavoriteIndicator/index.tsx +5 -3
- package/src/app/components/Shared/Filters/Indicators/index.tsx +2 -2
- package/src/app/components/Shared/Filters/Interval/index.tsx +1 -1
- package/src/app/components/Shared/Filters/Provider/index.tsx +8 -2
- package/src/app/components/Shared/Filters/Symbol/index.tsx +4 -3
- package/src/app/components/Shared/Filters/Universe/index.tsx +36 -0
- package/src/app/components/Shared/Filters/index.ts +2 -0
- package/src/app/components/Shared/OrdersDrawer.tsx +629 -0
- package/src/app/components/Shared/Sidebar/AccountSettingsDrawer.tsx +55 -28
- package/src/app/components/Shared/Sidebar/TradingAccountsPanel.tsx +445 -0
- package/src/app/components/Shared/Sidebar/index.tsx +17 -3
- package/src/app/components/Strategies/AdvancedMetricsPanel.tsx +727 -0
- package/src/app/components/Strategies/RuntimeStrategyCard.tsx +2204 -0
- package/src/app/components/Strategies/RuntimeStrategyCardSkeleton.tsx +40 -0
- package/src/app/components/Strategies/RuntimeStrategyChart.tsx +121 -0
- package/src/app/components/Strategies/RuntimeStrategyConfigDrawer.tsx +458 -0
- package/src/app/components/Strategies/StrategySnapshotCard.tsx +2890 -0
- package/src/app/components/Strategies/StrategySnapshotChart.tsx +94 -0
- package/src/app/components/Strategies/StrategySnapshotList.tsx +82 -0
- package/src/app/components/UI/Segment/index.tsx +8 -1
- package/src/app/components/UI/Select/index.tsx +24 -13
- package/src/app/components/UI/SelectWithSearch/index.tsx +46 -5
- package/src/app/layout.tsx +1 -1
- package/src/app/lib/backtestJobs.ts +925 -0
- package/src/app/lib/cardPageLayout.ts +1 -0
- package/src/app/lib/connectorCreator.ts +28 -0
- package/src/app/lib/currentUser.ts +1 -1
- package/src/app/lib/errorMessage.ts +34 -0
- package/src/app/lib/installation.ts +83 -0
- package/src/app/lib/marketDefaults.ts +12 -0
- package/src/app/lib/marketRoutes.ts +58 -0
- package/src/app/lib/runtimeStrategies.ts +1376 -0
- package/src/app/lib/runtimeStrategyConfigForm.ts +86 -0
- package/src/app/provider.tsx +1 -1
- package/src/app/routes/backtest/page.tsx +1100 -302
- package/src/app/routes/dashboard/{[provider]/[symbol]/[interval]/page.tsx → Dashboard.tsx} +76 -35
- package/src/app/routes/dashboard/[provider]/[universe]/[symbol]/[interval]/page.tsx +1 -0
- package/src/app/routes/dashboard/page.tsx +15 -3
- package/src/app/routes/derivatives/page.tsx +1222 -165
- package/src/app/routes/install/page.tsx +175 -0
- package/src/app/routes/signin/page.tsx +7 -0
- package/src/app/routes/strategies/StrategiesPageClient.tsx +593 -0
- package/src/app/routes/strategies/[mode]/page.tsx +20 -0
- package/src/app/routes/strategies/page.tsx +7 -0
- package/src/app/store/ai.ts +1 -1
- package/src/app/store/data.ts +73 -17
- package/src/app/store/filters.ts +1 -0
- package/src/app/store/marketKlineStream.ts +87 -0
- package/src/app/store/tests.ts +83 -45
- package/src/app/store/tickers.ts +59 -22
- package/src/proxy.ts +4 -0
- package/tsconfig.json +17 -12
- package/src/app/components/Backtest/TestCard/OpenReportButton/index.tsx +0 -24
- package/src/app/routes/backtest/[test]/page.tsx +0 -33
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CARD_PAGE_CONTENT_MAX_WIDTH = '1400px';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getConnectorCreatorByProvider as getBuiltinConnectorCreatorByProvider } from '@tradejs/connectors';
|
|
2
|
+
import { getConnectorCreatorByProvider as getRegisteredConnectorCreatorByProvider } from '@tradejs/node/connectors';
|
|
3
|
+
import { ConnectorCreator } from '@tradejs/types';
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_CONNECTOR_PROVIDER = 'bybit';
|
|
6
|
+
|
|
7
|
+
export const resolveConnectorCreatorByProvider = async (
|
|
8
|
+
provider: string,
|
|
9
|
+
projectRoot: string,
|
|
10
|
+
fallbackProvider = DEFAULT_CONNECTOR_PROVIDER,
|
|
11
|
+
): Promise<ConnectorCreator | null> => {
|
|
12
|
+
const registeredConnector =
|
|
13
|
+
(await getRegisteredConnectorCreatorByProvider(provider, projectRoot)) ||
|
|
14
|
+
(await getRegisteredConnectorCreatorByProvider(
|
|
15
|
+
fallbackProvider,
|
|
16
|
+
projectRoot,
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
if (registeredConnector) {
|
|
20
|
+
return registeredConnector as ConnectorCreator;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
getBuiltinConnectorCreatorByProvider(provider) ||
|
|
25
|
+
getBuiltinConnectorCreatorByProvider(fallbackProvider) ||
|
|
26
|
+
null
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const getStringField = (value: unknown, key: string) => {
|
|
2
|
+
if (!value || typeof value !== 'object') return null;
|
|
3
|
+
|
|
4
|
+
const field = (value as Record<string, unknown>)[key];
|
|
5
|
+
if (typeof field === 'string' && field.trim()) return field.trim();
|
|
6
|
+
if (key === 'code' && typeof field === 'number') return String(field);
|
|
7
|
+
return null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getErrorMessage = (error: unknown) => {
|
|
11
|
+
if (error instanceof Error && error.message.trim()) {
|
|
12
|
+
return error.message.trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (typeof error === 'string' && error.trim()) {
|
|
16
|
+
return error.trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const code = getStringField(error, 'code');
|
|
20
|
+
const message = getStringField(error, 'message');
|
|
21
|
+
const body = getStringField(error, 'body');
|
|
22
|
+
const details = [code, message].filter(Boolean).join(' ');
|
|
23
|
+
|
|
24
|
+
if (details || body) {
|
|
25
|
+
return [details, body].filter(Boolean).join(': ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const serialized = JSON.stringify(error);
|
|
30
|
+
return serialized && serialized !== '{}' ? serialized : 'Unknown error';
|
|
31
|
+
} catch {
|
|
32
|
+
return 'Unknown error';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs';
|
|
2
|
+
import { getData, redisKeys, setData } from '@tradejs/infra/redis';
|
|
3
|
+
import type { StrategyConfigGrid } from '@tradejs/types';
|
|
4
|
+
|
|
5
|
+
export const INSTALL_USER_NAME = 'root';
|
|
6
|
+
export const FIRST_BACKTEST_CONFIG_ID = 'MaStrategy:base';
|
|
7
|
+
|
|
8
|
+
export const FIRST_BACKTEST_CONFIG: StrategyConfigGrid = {
|
|
9
|
+
INTERVAL: ['15'],
|
|
10
|
+
MAX_LOSS_VALUE: [10],
|
|
11
|
+
MA_FAST: [21],
|
|
12
|
+
MA_SLOW: [55],
|
|
13
|
+
LONG: [
|
|
14
|
+
{
|
|
15
|
+
enable: true,
|
|
16
|
+
direction: 'LONG',
|
|
17
|
+
TP: 2,
|
|
18
|
+
SL: 1,
|
|
19
|
+
minRiskRatio: 1.2,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
SHORT: [
|
|
23
|
+
{
|
|
24
|
+
enable: true,
|
|
25
|
+
direction: 'SHORT',
|
|
26
|
+
TP: 2,
|
|
27
|
+
SL: 1,
|
|
28
|
+
minRiskRatio: 1.2,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getPasswordHash = (user: unknown): string | null => {
|
|
34
|
+
if (!user) return null;
|
|
35
|
+
if (typeof user === 'string') return user;
|
|
36
|
+
if (typeof user !== 'object') return null;
|
|
37
|
+
|
|
38
|
+
const record = user as Record<string, unknown>;
|
|
39
|
+
const direct = record.passwordHash ?? record.password;
|
|
40
|
+
if (typeof direct === 'string') return direct;
|
|
41
|
+
|
|
42
|
+
const nested = record.password as Record<string, unknown> | undefined;
|
|
43
|
+
const nestedHash = nested?.hash;
|
|
44
|
+
if (typeof nestedHash === 'string') return nestedHash;
|
|
45
|
+
|
|
46
|
+
const alt = record.hash;
|
|
47
|
+
return typeof alt === 'string' ? alt : null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const isInstallationRequired = async () => {
|
|
51
|
+
const user = await getData(redisKeys.user(INSTALL_USER_NAME), null);
|
|
52
|
+
return !getPasswordHash(user);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const initializeInstallation = async (password: string) => {
|
|
56
|
+
const existing = (await getData(
|
|
57
|
+
redisKeys.user(INSTALL_USER_NAME),
|
|
58
|
+
null,
|
|
59
|
+
)) as Record<string, unknown> | null;
|
|
60
|
+
|
|
61
|
+
if (getPasswordHash(existing)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const passwordHash = await bcrypt.hash(password, 10);
|
|
66
|
+
await setData(
|
|
67
|
+
redisKeys.user(INSTALL_USER_NAME),
|
|
68
|
+
{
|
|
69
|
+
...(existing ?? {}),
|
|
70
|
+
passwordHash,
|
|
71
|
+
userName: INSTALL_USER_NAME,
|
|
72
|
+
updatedAt: new Date().toISOString(),
|
|
73
|
+
},
|
|
74
|
+
{ expire: 0 },
|
|
75
|
+
);
|
|
76
|
+
await setData(
|
|
77
|
+
redisKeys.backtestConfig(INSTALL_USER_NAME, FIRST_BACKTEST_CONFIG_ID),
|
|
78
|
+
FIRST_BACKTEST_CONFIG,
|
|
79
|
+
{ expire: 0 },
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Interval,
|
|
3
|
+
isMarketUniverse,
|
|
4
|
+
MarketUniverse,
|
|
5
|
+
Provider,
|
|
6
|
+
} from '@tradejs/types';
|
|
7
|
+
|
|
8
|
+
interface MarketRouteParts {
|
|
9
|
+
provider: string;
|
|
10
|
+
universe: MarketUniverse;
|
|
11
|
+
symbol: string;
|
|
12
|
+
interval: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DashboardRouteParts {
|
|
16
|
+
provider: Provider;
|
|
17
|
+
universe: MarketUniverse;
|
|
18
|
+
symbol: string;
|
|
19
|
+
interval: Interval;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const appendRouteQuery = (pathname: string, searchParams?: URLSearchParams) => {
|
|
23
|
+
const params = new URLSearchParams(searchParams);
|
|
24
|
+
const search = params.toString();
|
|
25
|
+
return `${pathname}${search ? `?${search}` : ''}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const buildKlinePath = ({
|
|
29
|
+
provider,
|
|
30
|
+
universe,
|
|
31
|
+
symbol,
|
|
32
|
+
interval,
|
|
33
|
+
}: MarketRouteParts) =>
|
|
34
|
+
`/api/kline/${provider}/${universe}/${symbol}/${interval}`;
|
|
35
|
+
|
|
36
|
+
export const buildDashboardPath = (
|
|
37
|
+
{ provider, universe, symbol, interval }: MarketRouteParts,
|
|
38
|
+
searchParams?: URLSearchParams,
|
|
39
|
+
) =>
|
|
40
|
+
appendRouteQuery(
|
|
41
|
+
`/routes/dashboard/${provider}/${universe}/${symbol}/${interval}`,
|
|
42
|
+
searchParams,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export const parseDashboardPath = (
|
|
46
|
+
pathname: string,
|
|
47
|
+
fallback: Omit<DashboardRouteParts, 'universe'>,
|
|
48
|
+
): DashboardRouteParts => {
|
|
49
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
50
|
+
const rawUniverse = parts[3];
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
provider: (parts[2] || fallback.provider) as Provider,
|
|
54
|
+
universe: isMarketUniverse(rawUniverse) ? rawUniverse : 'crypto',
|
|
55
|
+
symbol: parts[4] || fallback.symbol,
|
|
56
|
+
interval: (parts[5] || fallback.interval) as Interval,
|
|
57
|
+
};
|
|
58
|
+
};
|