@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.
Files changed (125) hide show
  1. package/README.md +16 -3
  2. package/bin/tradejs-app.mjs +146 -2
  3. package/next.config.mjs +0 -14
  4. package/package.json +35 -19
  5. package/src/app/actions/backtest.ts +54 -0
  6. package/src/app/actions/kline.ts +10 -5
  7. package/src/app/actions/scanner.ts +8 -3
  8. package/src/app/actions/strategies.ts +39 -0
  9. package/src/app/api/ai/route.ts +5 -4
  10. package/src/app/api/auth/[...nextauth]/route.ts +1 -1
  11. package/src/app/api/backtest/configs/route.ts +25 -0
  12. package/src/app/api/backtest/files/route.ts +5 -2
  13. package/src/app/api/backtest/order-log/[strategy]/[name]/route.ts +26 -2
  14. package/src/app/api/backtest/result/[strategy]/[name]/route.ts +27 -2
  15. package/src/app/api/backtest/runs/[jobId]/route.ts +124 -0
  16. package/src/app/api/backtest/runs/route.ts +45 -0
  17. package/src/app/api/backtest/test/[strategy]/[name]/route.ts +19 -2
  18. package/src/app/api/derivatives/summary/route.ts +8 -1
  19. package/src/app/api/install/route.ts +51 -0
  20. package/src/app/api/kline/[provider]/[universe]/[symbol]/[interval]/route.ts +1 -0
  21. package/src/app/api/kline/{[provider]/[symbol]/[interval]/route.ts → handler.ts} +61 -39
  22. package/src/app/api/scanner/[provider]/[universe]/route.ts +1 -0
  23. package/src/app/api/scanner/[provider]/route.ts +30 -12
  24. package/src/app/api/scanner/route.ts +3 -3
  25. package/src/app/api/signal/[symbol]/[signalId]/route.ts +1 -1
  26. package/src/app/api/strategies/ai/[cardId]/route.ts +22 -0
  27. package/src/app/api/strategies/ai/route.ts +139 -0
  28. package/src/app/api/strategies/replay/[cardId]/route.ts +18 -0
  29. package/src/app/api/strategies/replay/route.ts +44 -0
  30. package/src/app/api/strategies/runtime/route.ts +831 -0
  31. package/src/app/api/user/runtime-deployments/[deploymentId]/route.ts +25 -0
  32. package/src/app/api/user/runtime-deployments/route.ts +94 -0
  33. package/src/app/api/user/runtime-strategy-configs/route.ts +241 -0
  34. package/src/app/api/user/settings/route.ts +18 -1
  35. package/src/app/api/user/trading-accounts/[accountId]/route.ts +35 -0
  36. package/src/app/api/user/trading-accounts/route.ts +134 -0
  37. package/src/app/auth.ts +1 -19
  38. package/src/app/components/Backtest/CompareList/index.tsx +1 -1
  39. package/src/app/components/Backtest/ResultsPageClient.tsx +376 -0
  40. package/src/app/components/Backtest/TestCard/ActionsMenu/index.tsx +123 -0
  41. package/src/app/components/Backtest/TestCard/Chart/index.tsx +1 -1
  42. package/src/app/components/Backtest/TestCard/CompareButton/index.tsx +1 -1
  43. package/src/app/components/Backtest/TestCard/ConfigDrawer/index.tsx +28 -12
  44. package/src/app/components/Backtest/TestCard/DeleteButton/index.tsx +33 -17
  45. package/src/app/components/Backtest/TestCard/FavoriteIndicator/index.tsx +2 -2
  46. package/src/app/components/Backtest/TestCard/OpenDashboardButton/index.tsx +10 -1
  47. package/src/app/components/Backtest/TestCard/OrdersDrawer/index.tsx +128 -0
  48. package/src/app/components/Backtest/TestCard/Root/index.tsx +18 -4
  49. package/src/app/components/Backtest/TestCard/Stat/index.tsx +2 -0
  50. package/src/app/components/Backtest/TestCard/StatDrawer/index.tsx +66 -0
  51. package/src/app/components/Backtest/TestCard/index.ts +6 -2
  52. package/src/app/components/Backtest/TestList/index.tsx +4 -9
  53. package/src/app/components/Dashboard/AiDrawer/index.tsx +1 -1
  54. package/src/app/components/Dashboard/KlineChart/hooks/useBacktest.ts +1 -1
  55. package/src/app/components/Dashboard/KlineChart/hooks/useBbIndicator.ts +10 -5
  56. package/src/app/components/Dashboard/KlineChart/hooks/useBtcCorrelation.ts +1 -1
  57. package/src/app/components/Dashboard/KlineChart/hooks/useBtcIndicator.ts +4 -1
  58. package/src/app/components/Dashboard/KlineChart/hooks/useEmaIndicator.ts +3 -1
  59. package/src/app/components/Dashboard/KlineChart/hooks/usePluginIndicators.ts +1 -1
  60. package/src/app/components/Dashboard/KlineChart/hooks/useSetup.ts +1 -1
  61. package/src/app/components/Dashboard/KlineChart/hooks/useSignal.ts +1 -1
  62. package/src/app/components/Dashboard/KlineChart/hooks/useSpreadIndicator.ts +1 -1
  63. package/src/app/components/Dashboard/KlineChart/hooks/useTrendLine.ts +1 -1
  64. package/src/app/components/Dashboard/KlineChart/hooks/useWmaIndicator.ts +3 -1
  65. package/src/app/components/Dashboard/KlineChart/index.tsx +21 -7
  66. package/src/app/components/Dashboard/MainChart/index.tsx +3 -22
  67. package/src/app/components/Shared/AppShell.tsx +2 -2
  68. package/src/app/components/Shared/BulkSelection/index.tsx +140 -0
  69. package/src/app/components/Shared/BulkSelection/useBulkSelection.ts +97 -0
  70. package/src/app/components/Shared/BulkSelection/utils.ts +126 -0
  71. package/src/app/components/Shared/Filters/Backtest/index.tsx +51 -51
  72. package/src/app/components/Shared/Filters/FavoriteIndicator/index.tsx +5 -3
  73. package/src/app/components/Shared/Filters/Indicators/index.tsx +2 -2
  74. package/src/app/components/Shared/Filters/Interval/index.tsx +1 -1
  75. package/src/app/components/Shared/Filters/Provider/index.tsx +8 -2
  76. package/src/app/components/Shared/Filters/Symbol/index.tsx +4 -3
  77. package/src/app/components/Shared/Filters/Universe/index.tsx +36 -0
  78. package/src/app/components/Shared/Filters/index.ts +2 -0
  79. package/src/app/components/Shared/OrdersDrawer.tsx +629 -0
  80. package/src/app/components/Shared/Sidebar/AccountSettingsDrawer.tsx +55 -28
  81. package/src/app/components/Shared/Sidebar/TradingAccountsPanel.tsx +445 -0
  82. package/src/app/components/Shared/Sidebar/index.tsx +17 -3
  83. package/src/app/components/Strategies/AdvancedMetricsPanel.tsx +727 -0
  84. package/src/app/components/Strategies/RuntimeStrategyCard.tsx +2204 -0
  85. package/src/app/components/Strategies/RuntimeStrategyCardSkeleton.tsx +40 -0
  86. package/src/app/components/Strategies/RuntimeStrategyChart.tsx +121 -0
  87. package/src/app/components/Strategies/RuntimeStrategyConfigDrawer.tsx +458 -0
  88. package/src/app/components/Strategies/StrategySnapshotCard.tsx +2890 -0
  89. package/src/app/components/Strategies/StrategySnapshotChart.tsx +94 -0
  90. package/src/app/components/Strategies/StrategySnapshotList.tsx +82 -0
  91. package/src/app/components/UI/Segment/index.tsx +8 -1
  92. package/src/app/components/UI/Select/index.tsx +24 -13
  93. package/src/app/components/UI/SelectWithSearch/index.tsx +46 -5
  94. package/src/app/layout.tsx +1 -1
  95. package/src/app/lib/backtestJobs.ts +925 -0
  96. package/src/app/lib/cardPageLayout.ts +1 -0
  97. package/src/app/lib/connectorCreator.ts +28 -0
  98. package/src/app/lib/currentUser.ts +1 -1
  99. package/src/app/lib/errorMessage.ts +34 -0
  100. package/src/app/lib/installation.ts +83 -0
  101. package/src/app/lib/marketDefaults.ts +12 -0
  102. package/src/app/lib/marketRoutes.ts +58 -0
  103. package/src/app/lib/runtimeStrategies.ts +1376 -0
  104. package/src/app/lib/runtimeStrategyConfigForm.ts +86 -0
  105. package/src/app/provider.tsx +1 -1
  106. package/src/app/routes/backtest/page.tsx +1100 -302
  107. package/src/app/routes/dashboard/{[provider]/[symbol]/[interval]/page.tsx → Dashboard.tsx} +76 -35
  108. package/src/app/routes/dashboard/[provider]/[universe]/[symbol]/[interval]/page.tsx +1 -0
  109. package/src/app/routes/dashboard/page.tsx +15 -3
  110. package/src/app/routes/derivatives/page.tsx +1222 -165
  111. package/src/app/routes/install/page.tsx +175 -0
  112. package/src/app/routes/signin/page.tsx +7 -0
  113. package/src/app/routes/strategies/StrategiesPageClient.tsx +593 -0
  114. package/src/app/routes/strategies/[mode]/page.tsx +20 -0
  115. package/src/app/routes/strategies/page.tsx +7 -0
  116. package/src/app/store/ai.ts +1 -1
  117. package/src/app/store/data.ts +73 -17
  118. package/src/app/store/filters.ts +1 -0
  119. package/src/app/store/marketKlineStream.ts +87 -0
  120. package/src/app/store/tests.ts +83 -45
  121. package/src/app/store/tickers.ts +59 -22
  122. package/src/proxy.ts +4 -0
  123. package/tsconfig.json +17 -12
  124. package/src/app/components/Backtest/TestCard/OpenReportButton/index.tsx +0 -24
  125. package/src/app/routes/backtest/[test]/page.tsx +0 -33
@@ -0,0 +1,128 @@
1
+ 'use client';
2
+
3
+ import { useMemo, useState } from 'react';
4
+ import { Button } from '@chakra-ui/react';
5
+ import type { OrderLogData } from '@tradejs/types';
6
+ import {
7
+ formatCompactNumber,
8
+ formatFee,
9
+ formatInteger,
10
+ formatPriceUsdt,
11
+ formatUsdt,
12
+ getPnlColor,
13
+ OrdersDrawerPanel,
14
+ type OrdersDrawerOrder,
15
+ } from '#components/Shared/OrdersDrawer';
16
+ import { useBacktest } from '#store';
17
+ import { useTestContext } from '../context';
18
+
19
+ const BACKTEST_ORDER_ROW_HEIGHT = 254;
20
+
21
+ const backtestStatusFilterOptions = [
22
+ { label: 'All', value: 'all' },
23
+ { label: 'Open', value: 'active' },
24
+ { label: 'Closed', value: 'closed' },
25
+ ] as const;
26
+
27
+ const getOrderStatus = (type: OrderLogData[number]['type']) => {
28
+ if (type.startsWith('OPEN')) {
29
+ return { label: 'OPEN', color: 'orange' };
30
+ }
31
+
32
+ if (type.startsWith('TAKE_PROFIT')) {
33
+ return { label: 'TP', color: 'teal' };
34
+ }
35
+
36
+ if (type.startsWith('STOP_LOSS')) {
37
+ return { label: 'SL', color: 'red' };
38
+ }
39
+
40
+ return { label: 'CLOSE', color: 'gray' };
41
+ };
42
+
43
+ const mapBacktestOrder = (order: OrderLogData[number]): OrdersDrawerOrder => {
44
+ const status = getOrderStatus(order.type);
45
+
46
+ return {
47
+ id: `${order.index}:${order.timestamp}:${order.type}`,
48
+ title: order.symbol,
49
+ period: {
50
+ start: order.timestamp,
51
+ },
52
+ direction: order.direction,
53
+ status: order.type.startsWith('OPEN') ? 'active' : 'closed',
54
+ statusLabel: status.label,
55
+ statusColor: status.color,
56
+ pnl: order.profit,
57
+ accentColor: order.type.startsWith('OPEN')
58
+ ? 'orange.300'
59
+ : getPnlColor(order.profit),
60
+ metrics: [
61
+ {
62
+ title: 'Price',
63
+ value: formatPriceUsdt(order.price),
64
+ },
65
+ {
66
+ title: 'Qty',
67
+ value: formatCompactNumber(order.qty),
68
+ },
69
+ {
70
+ title: 'Notional',
71
+ value: formatUsdt(order.price * order.qty),
72
+ },
73
+ {
74
+ title: 'Fee',
75
+ value: formatFee(order.fee ?? null),
76
+ },
77
+ {
78
+ title: 'Equity',
79
+ value: formatUsdt(order.amount),
80
+ },
81
+ {
82
+ title: 'Index',
83
+ value: formatInteger(order.index),
84
+ },
85
+ ],
86
+ };
87
+ };
88
+
89
+ interface TestCardOrdersDrawerPanelProps {
90
+ open: boolean;
91
+ onOpenChange: (open: boolean) => void;
92
+ }
93
+
94
+ export const TestCardOrdersDrawerPanel = ({
95
+ open,
96
+ onOpenChange,
97
+ }: TestCardOrdersDrawerPanelProps) => {
98
+ const {
99
+ testResult: { test },
100
+ } = useTestContext();
101
+ const { backtest, loading } = useBacktest(open ? test.name : undefined);
102
+ const orders = useMemo(() => backtest.map(mapBacktestOrder), [backtest]);
103
+
104
+ return (
105
+ <OrdersDrawerPanel
106
+ title={`${test.symbol} orders`}
107
+ open={open}
108
+ orders={orders}
109
+ rowHeight={BACKTEST_ORDER_ROW_HEIGHT}
110
+ statusFilterOptions={backtestStatusFilterOptions}
111
+ emptyText={loading ? 'Loading orders...' : 'No orders for this backtest.'}
112
+ onOpenChange={onOpenChange}
113
+ />
114
+ );
115
+ };
116
+
117
+ export const TestCardOrdersDrawer = () => {
118
+ const [open, setOpen] = useState(false);
119
+
120
+ return (
121
+ <>
122
+ <Button size="sm" variant="outline" onClick={() => setOpen(true)}>
123
+ Orders
124
+ </Button>
125
+ <TestCardOrdersDrawerPanel open={open} onOpenChange={setOpen} />
126
+ </>
127
+ );
128
+ };
@@ -1,11 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import { PropsWithChildren } from 'react';
4
- import _ from 'lodash';
5
- import { Box } from '@chakra-ui/react';
4
+ import { Box, Text } from '@chakra-ui/react';
6
5
  import { TestResultContext } from '../context';
7
6
  import { TestCardSkeleton } from '../Skeleton';
8
- import { useTest, useFavoriteTests } from '@store';
7
+ import { useTest, useFavoriteTests } from '#store';
9
8
 
10
9
  interface TestRootProps {
11
10
  testName: string;
@@ -20,7 +19,22 @@ export const TestCardRoot = ({
20
19
  const testResult = useTest(testName);
21
20
  const { checkIsFavorite } = useFavoriteTests();
22
21
 
23
- if (_.isEmpty(testResult)) {
22
+ if (testResult === null) {
23
+ return (
24
+ <Box
25
+ p={4}
26
+ mb={4}
27
+ borderRadius="md"
28
+ borderWidth="1px"
29
+ borderColor="gray.700"
30
+ color="gray.400"
31
+ >
32
+ <Text>Backtest data is no longer available.</Text>
33
+ </Box>
34
+ );
35
+ }
36
+
37
+ if (!testResult) {
24
38
  return <TestCardSkeleton />;
25
39
  }
26
40
 
@@ -11,6 +11,8 @@ const getColorByLevel = (level: ThresholdLevel) => {
11
11
  return 'teal.500';
12
12
  case 'warning':
13
13
  return 'fg.warning';
14
+ case 'neutral':
15
+ return 'gray.300';
14
16
  case 'error':
15
17
  default:
16
18
  return 'fg.error';
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Button, CloseButton, Drawer, Portal } from '@chakra-ui/react';
5
+ import { TestCardStatTable } from '../Stat';
6
+ import { useTestContext } from '../context';
7
+
8
+ interface TestCardStatDrawerPanelProps {
9
+ open: boolean;
10
+ onOpenChange: (open: boolean) => void;
11
+ }
12
+
13
+ export const TestCardStatDrawerPanel = ({
14
+ open,
15
+ onOpenChange,
16
+ }: TestCardStatDrawerPanelProps) => {
17
+ const {
18
+ testResult: { test },
19
+ } = useTestContext();
20
+
21
+ return (
22
+ <Drawer.Root
23
+ size="xl"
24
+ open={open}
25
+ onOpenChange={(event) => onOpenChange(event.open)}
26
+ >
27
+ <Portal>
28
+ <Drawer.Backdrop />
29
+ <Drawer.Positioner>
30
+ <Drawer.Content
31
+ display="flex"
32
+ flexDirection="column"
33
+ w="50vw"
34
+ minW="640px"
35
+ maxW="50vw"
36
+ bg="gray.950"
37
+ >
38
+ <Drawer.Header>
39
+ <Drawer.Title>{test.symbol} stat</Drawer.Title>
40
+ <Drawer.CloseTrigger asChild>
41
+ <CloseButton size="sm" />
42
+ </Drawer.CloseTrigger>
43
+ </Drawer.Header>
44
+
45
+ <Drawer.Body overflowY="auto" flex="1" minH="0" w="full">
46
+ <TestCardStatTable />
47
+ </Drawer.Body>
48
+ </Drawer.Content>
49
+ </Drawer.Positioner>
50
+ </Portal>
51
+ </Drawer.Root>
52
+ );
53
+ };
54
+
55
+ export const TestCardStatDrawer = () => {
56
+ const [open, setOpen] = useState(false);
57
+
58
+ return (
59
+ <>
60
+ <Button size="sm" variant="outline" onClick={() => setOpen(true)}>
61
+ Stat
62
+ </Button>
63
+ <TestCardStatDrawerPanel open={open} onOpenChange={setOpen} />
64
+ </>
65
+ );
66
+ };
@@ -8,9 +8,11 @@ import { TestCardSkeleton } from './Skeleton';
8
8
  import { TestCardConfigDrawer } from './ConfigDrawer';
9
9
  import { TestCardCompareButton } from './CompareButton';
10
10
  import { TestCardFavoriteIndicator } from './FavoriteIndicator';
11
- import { TestCardOpenReportButton } from './OpenReportButton';
12
11
  import { TestCardOpenDashboardButton } from './OpenDashboardButton';
13
12
  import { TestCardDeleteButton } from './DeleteButton';
13
+ import { TestCardOrdersDrawer } from './OrdersDrawer';
14
+ import { TestCardStatDrawer } from './StatDrawer';
15
+ import { TestCardActionsMenu } from './ActionsMenu';
14
16
 
15
17
  export const TestCard = {
16
18
  Root: TestCardRoot,
@@ -23,6 +25,8 @@ export const TestCard = {
23
25
  CompareButton: TestCardCompareButton,
24
26
  FavoriteIndicator: TestCardFavoriteIndicator,
25
27
  OpenDashboardButton: TestCardOpenDashboardButton,
26
- OpenReportButton: TestCardOpenReportButton,
28
+ OrdersDrawer: TestCardOrdersDrawer,
29
+ StatDrawer: TestCardStatDrawer,
27
30
  DeleteButton: TestCardDeleteButton,
31
+ ActionsMenu: TestCardActionsMenu,
28
32
  };
@@ -5,8 +5,8 @@ import AutoSizer from 'react-virtualized-auto-sizer';
5
5
  import { FixedSizeList, ListChildComponentProps } from 'react-window';
6
6
  import { FiFolder } from 'react-icons/fi';
7
7
  import { Box, Checkbox, Code } from '@chakra-ui/react';
8
- import { TestCard } from '@components/Backtest/TestCard';
9
- import { EmptyState } from '@UI';
8
+ import { TestCard } from '#components/Backtest/TestCard';
9
+ import { EmptyState } from '#ui';
10
10
  import { Items } from '@tradejs/types';
11
11
 
12
12
  interface ListProps {
@@ -44,7 +44,7 @@ export const TestList = ({
44
44
  ({ index, style }: ListChildComponentProps) => {
45
45
  const item = tests[index];
46
46
  return (
47
- <Box style={style} px={2}>
47
+ <Box style={style}>
48
48
  <TestCard.Root key={item.value} testName={item.value}>
49
49
  <TestCard.Title
50
50
  leftSlot={
@@ -61,12 +61,7 @@ export const TestList = ({
61
61
  </Checkbox.Root>
62
62
  }
63
63
  >
64
- <TestCard.CompareButton />
65
- <TestCard.FavoriteIndicator />
66
- <TestCard.ConfigDrawer />
67
- <TestCard.OpenDashboardButton />
68
- <TestCard.OpenReportButton />
69
- <TestCard.DeleteButton />
64
+ <TestCard.ActionsMenu />
70
65
  </TestCard.Title>
71
66
  <TestCard.Chart />
72
67
  <TestCard.StatLine />
@@ -17,7 +17,7 @@ import {
17
17
  SkeletonText,
18
18
  } from '@chakra-ui/react';
19
19
  import { GiArtificialHive } from 'react-icons/gi';
20
- import { useAiChatStore, useFilters } from '@store';
20
+ import { useAiChatStore, useFilters } from '#store';
21
21
  import { Message } from './Message';
22
22
 
23
23
  export const AiDrawer = () => {
@@ -4,7 +4,7 @@ import { useEffect } from 'react';
4
4
  import _ from 'lodash';
5
5
  import { registerOverlay, registerIndicator, Chart } from 'klinecharts';
6
6
  import { KlineChartItem, OrderLogData } from '@tradejs/types';
7
- import { useBacktest as useBacktestStore } from '@store';
7
+ import { useBacktest as useBacktestStore } from '#store';
8
8
  import {
9
9
  TradeZoneMode,
10
10
  createTradeZonePointFigure,
@@ -1,8 +1,10 @@
1
1
  import _ from 'lodash';
2
2
  import { useEffect } from 'react';
3
- import { BollingerBands } from 'technicalindicators';
4
3
  import { registerIndicator, Chart } from 'klinecharts';
5
4
 
5
+ const { BollingerBands } =
6
+ require('fast-technical-indicators') as typeof import('fast-technical-indicators');
7
+
6
8
  export const useBbIndicator = (
7
9
  chart: Chart | null,
8
10
  enabled: boolean,
@@ -43,10 +45,13 @@ export const useBbIndicator = (
43
45
  const bb: Record<string, number> = {};
44
46
  periods.forEach((period, periodIndex) => {
45
47
  if (candleIndex >= period - 1) {
46
- bb[`BBUpper${period}`] =
47
- values[periodIndex][candleIndex - (period - 1)].upper;
48
- bb[`BBLower${period}`] =
49
- values[periodIndex][candleIndex - (period - 1)].lower;
48
+ const value = values[periodIndex][candleIndex - (period - 1)];
49
+ if (value.upper != null) {
50
+ bb[`BBUpper${period}`] = value.upper;
51
+ }
52
+ if (value.lower != null) {
53
+ bb[`BBLower${period}`] = value.lower;
54
+ }
50
55
  }
51
56
  });
52
57
 
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import _ from 'lodash';
3
3
  import { Chart } from 'klinecharts';
4
4
  import { Filters, Provider } from '@tradejs/types';
5
- import { useData } from '@store';
5
+ import { useData } from '#store';
6
6
  import { CORRELATION_WINDOW } from '@tradejs/core/constants';
7
7
  import { getCloseAtOrBefore, grayDashedLineStyle } from './indicatorShared';
8
8
  import { useManagedIndicator } from './useManagedIndicator';
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import _ from 'lodash';
3
3
  import { Chart } from 'klinecharts';
4
4
  import { Filters, Provider } from '@tradejs/types';
5
- import { useData } from '@store';
5
+ import { useData } from '#store';
6
6
  import { getCloseAtOrBefore } from './indicatorShared';
7
7
  import { useManagedIndicator } from './useManagedIndicator';
8
8
 
@@ -62,16 +62,19 @@ export const useBtcIndicator = (
62
62
  () => ({
63
63
  bybit: {
64
64
  ...filters,
65
+ universe: 'crypto' as const,
65
66
  provider: 'bybit' as Provider,
66
67
  symbol: enabled ? 'BTCUSDT' : '',
67
68
  },
68
69
  binance: {
69
70
  ...filters,
71
+ universe: 'crypto' as const,
70
72
  provider: 'binance' as Provider,
71
73
  symbol: enabled ? 'BTCUSDT' : '',
72
74
  },
73
75
  coinbase: {
74
76
  ...filters,
77
+ universe: 'crypto' as const,
75
78
  provider: 'coinbase' as Provider,
76
79
  symbol: enabled ? 'BTCUSDT' : '',
77
80
  },
@@ -1,7 +1,9 @@
1
1
  import { useEffect } from 'react';
2
- import { EMA } from 'technicalindicators';
3
2
  import { registerIndicator, Chart } from 'klinecharts';
4
3
 
4
+ const { EMA } =
5
+ require('fast-technical-indicators') as typeof import('fast-technical-indicators');
6
+
5
7
  export const useEmaIndicator = (
6
8
  chart: Chart | null,
7
9
  enabled: boolean,
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { Chart, registerIndicator } from 'klinecharts';
3
3
  import { Indicator, KlineChartData } from '@tradejs/types';
4
- import { IndicatorRendererConfig } from '@store';
4
+ import { IndicatorRendererConfig } from '#store';
5
5
 
6
6
  type RenderersMap = Record<string, IndicatorRendererConfig>;
7
7
  type RuntimeEntry = {
@@ -3,7 +3,7 @@
3
3
  import { useEffect, useMemo, useState } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import { Chart, registerOverlay } from 'klinecharts';
6
- import { getSignal } from '@actions/signal';
6
+ import { getSignal } from '#actions/signal';
7
7
  import { toMs } from '@tradejs/core/time';
8
8
  import { Signal } from '@tradejs/types';
9
9
  import { createTradeZonePointFigure } from '../figures/tradeZonePointFigure';
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import _ from 'lodash';
6
6
  import { Chart } from 'klinecharts';
7
- import { getSignal } from '@actions/signal';
7
+ import { getSignal } from '#actions/signal';
8
8
  import { Signal } from '@tradejs/types';
9
9
  import {
10
10
  drawSignalFigures,
@@ -4,7 +4,7 @@ import { Chart } from 'klinecharts';
4
4
  import { Filters, Interval, Provider } from '@tradejs/types';
5
5
  import { smoothSpreadSeries } from '@tradejs/indicators';
6
6
  import { API } from '@tradejs/core/api';
7
- import { useData } from '@store';
7
+ import { useData } from '#store';
8
8
  import { grayDashedLineStyle } from './indicatorShared';
9
9
  import { useManagedIndicator } from './useManagedIndicator';
10
10
 
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
4
4
  import { useSearchParams } from 'next/navigation';
5
5
  import _ from 'lodash';
6
6
  import { Chart, registerOverlay } from 'klinecharts';
7
- import { getSignal } from '@actions/signal';
7
+ import { getSignal } from '#actions/signal';
8
8
  import { toMs } from '@tradejs/core/time';
9
9
  import { createTrendlineEngine } from '@tradejs/core/indicators';
10
10
  import { Signal, TrendLine } from '@tradejs/types';
@@ -1,7 +1,9 @@
1
1
  import { useEffect } from 'react';
2
- import { WMA } from 'technicalindicators';
3
2
  import { registerIndicator, Chart } from 'klinecharts';
4
3
 
4
+ const { WMA } =
5
+ require('fast-technical-indicators') as typeof import('fast-technical-indicators');
6
+
5
7
  export const useWmaIndicator = (
6
8
  chart: Chart | null,
7
9
  enabled: boolean,
@@ -8,7 +8,7 @@ import {
8
8
  dispose,
9
9
  DataLoaderSubscribeBarParams,
10
10
  } from 'klinecharts';
11
- import { OverlaySpinner } from '@UI';
11
+ import { OverlaySpinner } from '#ui';
12
12
  import { Indicator, UIFilters } from '@tradejs/types';
13
13
  import {
14
14
  useBbIndicator,
@@ -27,7 +27,7 @@ import {
27
27
  useSetup,
28
28
  } from './hooks';
29
29
  import { usePluginIndicators } from './hooks/usePluginIndicators';
30
- import { IndicatorRendererConfig, useData } from '@store';
30
+ import { IndicatorRendererConfig, useData } from '#store';
31
31
  import { darkTheme } from './styles';
32
32
 
33
33
  interface KlineChartProps {
@@ -35,6 +35,7 @@ interface KlineChartProps {
35
35
  filters: UIFilters;
36
36
  indicators: Record<string, Indicator>;
37
37
  indicatorRenderers: Record<string, IndicatorRendererConfig>;
38
+ live?: boolean;
38
39
  }
39
40
 
40
41
  export const KlineChart = ({
@@ -42,9 +43,10 @@ export const KlineChart = ({
42
43
  filters,
43
44
  indicators,
44
45
  indicatorRenderers,
46
+ live = true,
45
47
  }: KlineChartProps) => {
46
48
  const chartRef = useRef<Chart | null>(null);
47
- const { data, key, fulfilled } = useData(filters);
49
+ const { data, key, fulfilled } = useData(filters, live);
48
50
  const updateDataCallback = useRef<
49
51
  DataLoaderSubscribeBarParams['callback'] | null
50
52
  >(null);
@@ -63,7 +65,7 @@ export const KlineChart = ({
63
65
  }, [id]);
64
66
 
65
67
  useEffect(() => {
66
- if (!chartRef.current || _.isEmpty(data)) {
68
+ if (!chartRef.current || !fulfilled || _.isEmpty(data)) {
67
69
  return;
68
70
  }
69
71
 
@@ -152,8 +154,16 @@ export const KlineChart = ({
152
154
  useWmaIndicator(chart, indicators.wma.enabled, indicators.wma.periods || []);
153
155
  useVolIndicator(chart, indicators.vol.enabled);
154
156
  useBtcIndicator(chart, indicators.btc.enabled, filters);
155
- useBtcCorrelation(chart, indicators.btcCorrelation?.enabled, filters);
156
- useSpreadIndicator(chart, indicators.spread?.enabled, filters);
157
+ useBtcCorrelation(
158
+ chart,
159
+ indicators.btcCorrelation?.enabled && filters.universe !== 'tradfi',
160
+ filters,
161
+ );
162
+ useSpreadIndicator(
163
+ chart,
164
+ indicators.spread?.enabled && filters.universe !== 'tradfi',
165
+ filters,
166
+ );
157
167
  useBacktest(chart, filters.backtestId || undefined);
158
168
  useSupportResistanceLines(chart, indicators.resistant?.enabled);
159
169
  useSignal(chart, true);
@@ -162,7 +172,11 @@ export const KlineChart = ({
162
172
 
163
173
  return (
164
174
  <>
165
- <div id={id} />
175
+ <div
176
+ id={id}
177
+ data-testid="market-chart"
178
+ data-chart-ready={fulfilled && !_.isEmpty(data) ? 'true' : 'false'}
179
+ />
166
180
  {!fulfilled && <OverlaySpinner />}
167
181
  </>
168
182
  );
@@ -1,43 +1,24 @@
1
1
  'use client';
2
2
 
3
3
  import _ from 'lodash';
4
- import { useEffect } from 'react';
5
- import { useFilters, useIndicators } from '@store';
6
- import { getTimestamp } from '@tradejs/core/time';
4
+ import { useFilters, useIndicators } from '#store';
7
5
  import { KlineChart } from '../KlineChart';
8
6
 
9
- const DASHBOARD_REFRESH_DELAY = 10_000;
10
-
11
7
  interface MainChartProps {
12
8
  screenshotMode?: boolean;
13
9
  }
14
10
 
15
11
  export const MainChart = ({ screenshotMode = false }: MainChartProps) => {
16
- const { filters, setFilters } = useFilters();
12
+ const { filters } = useFilters();
17
13
  const { indicatorsByKey, indicatorRenderers } = useIndicators();
18
14
 
19
- useEffect(() => {
20
- if (screenshotMode) {
21
- return;
22
- }
23
-
24
- const intervalId = setInterval(() => {
25
- setFilters({
26
- end: getTimestamp(),
27
- });
28
- }, DASHBOARD_REFRESH_DELAY);
29
-
30
- return () => {
31
- clearInterval(intervalId);
32
- };
33
- }, [screenshotMode, setFilters]);
34
-
35
15
  return (
36
16
  <KlineChart
37
17
  id="main-chart"
38
18
  filters={filters}
39
19
  indicators={indicatorsByKey}
40
20
  indicatorRenderers={indicatorRenderers}
21
+ live={!screenshotMode && !filters.backtestId}
41
22
  />
42
23
  );
43
24
  };
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { Box } from '@chakra-ui/react';
4
4
  import { usePathname } from 'next/navigation';
5
- import { Sidebar } from '@shared/Sidebar';
5
+ import { Sidebar } from '#shared/Sidebar';
6
6
 
7
- const AUTH_ROUTES = ['/routes/signin'];
7
+ const AUTH_ROUTES = ['/routes/signin', '/routes/install'];
8
8
 
9
9
  const isAuthRoute = (pathname: string) =>
10
10
  AUTH_ROUTES.some((route) => pathname.startsWith(route));