@tradejs/app 1.0.9 → 1.0.10
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 +7 -2
- 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/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/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 +1 -1
- 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 +16 -6
- package/src/app/components/Dashboard/MainChart/index.tsx +3 -22
- package/src/app/components/Shared/AppShell.tsx +1 -1
- 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 +33 -48
- 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 +792 -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/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 +1051 -304
- package/src/app/routes/dashboard/{[provider]/[symbol]/[interval]/page.tsx → Dashboard.tsx} +35 -15
- 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/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 +1 -1
- package/src/app/store/tickers.ts +59 -22
- 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,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
|
+
};
|
|
@@ -5,7 +5,7 @@ import _ from 'lodash';
|
|
|
5
5
|
import { Box } from '@chakra-ui/react';
|
|
6
6
|
import { TestResultContext } from '../context';
|
|
7
7
|
import { TestCardSkeleton } from '../Skeleton';
|
|
8
|
-
import { useTest, useFavoriteTests } from '
|
|
8
|
+
import { useTest, useFavoriteTests } from '#store';
|
|
9
9
|
|
|
10
10
|
interface TestRootProps {
|
|
11
11
|
testName: string;
|
|
@@ -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
|
-
|
|
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 '
|
|
9
|
-
import { EmptyState } from '
|
|
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}
|
|
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.
|
|
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 '
|
|
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 '
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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(
|
|
156
|
-
|
|
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);
|
|
@@ -1,43 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import {
|
|
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
|
|
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
|
};
|