@tradejs/app 1.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 (126) hide show
  1. package/README.md +12 -0
  2. package/bin/tradejs-app.mjs +54 -0
  3. package/next-env.d.ts +6 -0
  4. package/next.config.mjs +31 -0
  5. package/package.json +60 -0
  6. package/src/app/actions/ai.ts +33 -0
  7. package/src/app/actions/backtest.ts +55 -0
  8. package/src/app/actions/kline.ts +18 -0
  9. package/src/app/actions/scanner.ts +10 -0
  10. package/src/app/actions/signal.ts +19 -0
  11. package/src/app/api/ai/route.ts +151 -0
  12. package/src/app/api/auth/[...nextauth]/route.ts +5 -0
  13. package/src/app/api/backtest/files/route.ts +60 -0
  14. package/src/app/api/backtest/order-log/[strategy]/[name]/route.ts +47 -0
  15. package/src/app/api/backtest/result/[strategy]/[name]/route.ts +63 -0
  16. package/src/app/api/backtest/test/[strategy]/[name]/route.ts +57 -0
  17. package/src/app/api/cron/route.ts +4 -0
  18. package/src/app/api/derivatives/[symbol]/[interval]/route.ts +57 -0
  19. package/src/app/api/derivatives/summary/route.ts +20 -0
  20. package/src/app/api/files/screenshot/[name]/route.ts +42 -0
  21. package/src/app/api/indicators/route.ts +24 -0
  22. package/src/app/api/kline/[provider]/[symbol]/[interval]/route.ts +123 -0
  23. package/src/app/api/scanner/[provider]/route.ts +41 -0
  24. package/src/app/api/scanner/route.ts +31 -0
  25. package/src/app/api/signal/[symbol]/[signalId]/route.ts +42 -0
  26. package/src/app/api/spread/[symbol]/[interval]/route.ts +57 -0
  27. package/src/app/api/spread/summary/route.ts +20 -0
  28. package/src/app/auth.ts +76 -0
  29. package/src/app/components/Backtest/CompareList/index.tsx +34 -0
  30. package/src/app/components/Backtest/TestCard/Chart/index.tsx +118 -0
  31. package/src/app/components/Backtest/TestCard/Chart/utils/index.ts +81 -0
  32. package/src/app/components/Backtest/TestCard/CompareButton/index.tsx +21 -0
  33. package/src/app/components/Backtest/TestCard/ConfigDrawer/JsonCodeBlock.tsx +46 -0
  34. package/src/app/components/Backtest/TestCard/ConfigDrawer/index.tsx +94 -0
  35. package/src/app/components/Backtest/TestCard/DeleteButton/index.tsx +128 -0
  36. package/src/app/components/Backtest/TestCard/FavoriteIndicator/index.tsx +18 -0
  37. package/src/app/components/Backtest/TestCard/OpenDashboardButton/index.tsx +40 -0
  38. package/src/app/components/Backtest/TestCard/OpenReportButton/index.tsx +24 -0
  39. package/src/app/components/Backtest/TestCard/Root/index.tsx +55 -0
  40. package/src/app/components/Backtest/TestCard/Skeleton/index.tsx +21 -0
  41. package/src/app/components/Backtest/TestCard/Stat/index.tsx +119 -0
  42. package/src/app/components/Backtest/TestCard/Title/index.tsx +84 -0
  43. package/src/app/components/Backtest/TestCard/context.ts +14 -0
  44. package/src/app/components/Backtest/TestCard/index.ts +28 -0
  45. package/src/app/components/Backtest/TestList/index.tsx +124 -0
  46. package/src/app/components/Dashboard/AiDrawer/Message.tsx +34 -0
  47. package/src/app/components/Dashboard/AiDrawer/index.tsx +163 -0
  48. package/src/app/components/Dashboard/KlineChart/figures/backtestFigureTypes.ts +7 -0
  49. package/src/app/components/Dashboard/KlineChart/figures/backtestMarkersPointFigure.ts +76 -0
  50. package/src/app/components/Dashboard/KlineChart/figures/circle.ts +15 -0
  51. package/src/app/components/Dashboard/KlineChart/figures/diamond.ts +25 -0
  52. package/src/app/components/Dashboard/KlineChart/figures/entryLinePointFigure.ts +1 -0
  53. package/src/app/components/Dashboard/KlineChart/figures/entryPointsPointFigure.ts +1 -0
  54. package/src/app/components/Dashboard/KlineChart/figures/entryZonePointFigure.ts +1 -0
  55. package/src/app/components/Dashboard/KlineChart/figures/index.ts +213 -0
  56. package/src/app/components/Dashboard/KlineChart/figures/label.ts +14 -0
  57. package/src/app/components/Dashboard/KlineChart/figures/rectangle.ts +20 -0
  58. package/src/app/components/Dashboard/KlineChart/figures/square.ts +21 -0
  59. package/src/app/components/Dashboard/KlineChart/figures/star.ts +39 -0
  60. package/src/app/components/Dashboard/KlineChart/figures/tradeZonePointFigure.ts +44 -0
  61. package/src/app/components/Dashboard/KlineChart/figures/trendLinePointFigure.ts +37 -0
  62. package/src/app/components/Dashboard/KlineChart/figures/trendLinePointsPointFigure.ts +26 -0
  63. package/src/app/components/Dashboard/KlineChart/figures/triangle.ts +23 -0
  64. package/src/app/components/Dashboard/KlineChart/hooks/index.ts +14 -0
  65. package/src/app/components/Dashboard/KlineChart/hooks/indicatorShared.ts +30 -0
  66. package/src/app/components/Dashboard/KlineChart/hooks/useAtrIndicator.ts +75 -0
  67. package/src/app/components/Dashboard/KlineChart/hooks/useBacktest.ts +533 -0
  68. package/src/app/components/Dashboard/KlineChart/hooks/useBbIndicator.ts +74 -0
  69. package/src/app/components/Dashboard/KlineChart/hooks/useBtcCorrelation.ts +155 -0
  70. package/src/app/components/Dashboard/KlineChart/hooks/useBtcIndicator.ts +185 -0
  71. package/src/app/components/Dashboard/KlineChart/hooks/useEmaIndicator.ts +62 -0
  72. package/src/app/components/Dashboard/KlineChart/hooks/useMaIndicator.ts +62 -0
  73. package/src/app/components/Dashboard/KlineChart/hooks/useManagedIndicator.ts +140 -0
  74. package/src/app/components/Dashboard/KlineChart/hooks/usePluginIndicators.ts +212 -0
  75. package/src/app/components/Dashboard/KlineChart/hooks/useResize.ts +29 -0
  76. package/src/app/components/Dashboard/KlineChart/hooks/useSetup.ts +122 -0
  77. package/src/app/components/Dashboard/KlineChart/hooks/useSignal.ts +85 -0
  78. package/src/app/components/Dashboard/KlineChart/hooks/useSpreadIndicator.ts +243 -0
  79. package/src/app/components/Dashboard/KlineChart/hooks/useSupportResistanceLines.ts +125 -0
  80. package/src/app/components/Dashboard/KlineChart/hooks/useTrendLine.ts +139 -0
  81. package/src/app/components/Dashboard/KlineChart/hooks/useVolIndicator.ts +18 -0
  82. package/src/app/components/Dashboard/KlineChart/hooks/useWmaIndicator.ts +62 -0
  83. package/src/app/components/Dashboard/KlineChart/index.tsx +169 -0
  84. package/src/app/components/Dashboard/KlineChart/styles.ts +70 -0
  85. package/src/app/components/Dashboard/MainChart/index.tsx +35 -0
  86. package/src/app/components/Shared/AppShell.tsx +28 -0
  87. package/src/app/components/Shared/FavoriteButton/index.tsx +23 -0
  88. package/src/app/components/Shared/Filters/Backtest/index.tsx +164 -0
  89. package/src/app/components/Shared/Filters/FavoriteIndicator/index.tsx +18 -0
  90. package/src/app/components/Shared/Filters/Indicators/index.tsx +21 -0
  91. package/src/app/components/Shared/Filters/Interval/index.tsx +31 -0
  92. package/src/app/components/Shared/Filters/Interval/intervals.ts +6 -0
  93. package/src/app/components/Shared/Filters/Provider/index.tsx +32 -0
  94. package/src/app/components/Shared/Filters/Root/index.tsx +28 -0
  95. package/src/app/components/Shared/Filters/Symbol/index.tsx +49 -0
  96. package/src/app/components/Shared/Filters/context.ts +17 -0
  97. package/src/app/components/Shared/Filters/index.ts +17 -0
  98. package/src/app/components/Shared/Sidebar/index.tsx +72 -0
  99. package/src/app/components/UI/ColorMode/index.tsx +112 -0
  100. package/src/app/components/UI/EmptyState/index.tsx +28 -0
  101. package/src/app/components/UI/OverlaySpinner/index.tsx +23 -0
  102. package/src/app/components/UI/Segment/index.tsx +23 -0
  103. package/src/app/components/UI/Select/index.tsx +81 -0
  104. package/src/app/components/UI/SelectWithSearch/index.tsx +104 -0
  105. package/src/app/components/UI/Switcher/index.tsx +24 -0
  106. package/src/app/components/UI/Toaster/index.tsx +45 -0
  107. package/src/app/components/UI/index.ts +8 -0
  108. package/src/app/favicon.ico +0 -0
  109. package/src/app/globals.css +5 -0
  110. package/src/app/layout.tsx +31 -0
  111. package/src/app/page.tsx +14 -0
  112. package/src/app/provider.tsx +39 -0
  113. package/src/app/routes/backtest/[test]/page.tsx +33 -0
  114. package/src/app/routes/backtest/page.tsx +374 -0
  115. package/src/app/routes/dashboard/[provider]/[symbol]/[interval]/page.tsx +124 -0
  116. package/src/app/routes/dashboard/page.tsx +20 -0
  117. package/src/app/routes/derivatives/page.tsx +202 -0
  118. package/src/app/routes/signin/page.tsx +155 -0
  119. package/src/app/store/data.ts +144 -0
  120. package/src/app/store/filters.ts +29 -0
  121. package/src/app/store/index.ts +13 -0
  122. package/src/app/store/indicators.ts +229 -0
  123. package/src/app/store/tests.ts +464 -0
  124. package/src/app/store/tickers.ts +89 -0
  125. package/src/proxy.ts +142 -0
  126. package/tsconfig.json +40 -0
@@ -0,0 +1,89 @@
1
+ import { useEffect, useMemo, useCallback } from 'react';
2
+ import { create } from 'zustand';
3
+ import { persist } from 'zustand/middleware';
4
+ import _ from 'lodash';
5
+ import { scan } from '@actions/scanner';
6
+ import { Items } from '@tradejs/types';
7
+
8
+ const LOCAL_STORAGE_KEY = 'tickers';
9
+
10
+ interface FavoriteTickersState {
11
+ favorites: string[];
12
+ toggleFavorite: (ticker: string) => void;
13
+ }
14
+
15
+ const useFavoriteTickersStore = create<FavoriteTickersState>()(
16
+ persist(
17
+ (set) => ({
18
+ favorites: ['BTCUSDT', 'ETHUSDT'],
19
+ toggleFavorite: (ticker: string) =>
20
+ set(({ favorites }) => {
21
+ if (favorites.includes(ticker)) {
22
+ return {
23
+ favorites: favorites.filter((favorite) => favorite !== ticker),
24
+ };
25
+ }
26
+ return {
27
+ favorites: [...favorites, ticker],
28
+ };
29
+ }),
30
+ }),
31
+ {
32
+ name: LOCAL_STORAGE_KEY,
33
+ },
34
+ ),
35
+ );
36
+
37
+ interface TickersScannerState {
38
+ provider: string;
39
+ tickers: Items;
40
+ setTickers: (provider: string, tickers: Items) => void;
41
+ }
42
+
43
+ const useScannerStore = create<TickersScannerState>((set) => ({
44
+ provider: 'bybit',
45
+ tickers: [] as Items,
46
+ setTickers: (provider, coins) =>
47
+ set(() => ({ provider, tickers: _.sortBy(coins, 'label') })),
48
+ }));
49
+
50
+ export const useTickers = (provider = 'bybit') => {
51
+ const favorites = useFavoriteTickersStore((s) => s.favorites);
52
+ const loadedProvider = useScannerStore((s) => s.provider);
53
+ const tickers = useScannerStore((s) => s.tickers);
54
+ const toggleFavorite = useFavoriteTickersStore((s) => s.toggleFavorite);
55
+ const setTickers = useScannerStore((s) => s.setTickers);
56
+ const checkIsFavorite = useCallback(
57
+ (ticker: string) => favorites.includes(ticker),
58
+ [favorites],
59
+ );
60
+
61
+ useEffect(() => {
62
+ if (tickers.length && loadedProvider === provider) {
63
+ return;
64
+ }
65
+
66
+ scan(provider).then((coins) => {
67
+ setTickers(provider, coins);
68
+ });
69
+ }, [loadedProvider, provider, setTickers, tickers.length]);
70
+
71
+ const items = useMemo(() => {
72
+ const favoriteItems = tickers
73
+ .filter((s) => checkIsFavorite(s.value))
74
+ .map((s) => ({
75
+ ...s,
76
+ description: `${s.description} ⭐️`,
77
+ }));
78
+
79
+ return _.uniqBy([...favoriteItems, ...tickers], (item) => item.value);
80
+ }, [tickers, checkIsFavorite]);
81
+
82
+ return {
83
+ tickers: items,
84
+ favorites,
85
+ checkIsFavorite,
86
+ toggleFavorite,
87
+ setTickers,
88
+ };
89
+ };
package/src/proxy.ts ADDED
@@ -0,0 +1,142 @@
1
+ import { NextResponse, type NextRequest } from 'next/server';
2
+ import { encode, getToken } from 'next-auth/jwt';
3
+ import { getData, getKeys, redisKeys } from '@tradejs/infra/redis';
4
+
5
+ const SIGNIN_PATH = '/routes/signin';
6
+ const SCREENSHOT_API_PREFIX = '/api/files/screenshot';
7
+ const SESSION_COOKIE_NAME = 'authjs.session-token';
8
+ const SECURE_SESSION_COOKIE_NAME = '__Secure-authjs.session-token';
9
+ const SESSION_MAX_AGE = 30 * 24 * 60 * 60;
10
+ const PUBLIC_FILE_RE = /\.(?:png|jpg|jpeg|webp|gif|svg|ico|txt|xml|json|map)$/i;
11
+
12
+ const getRootToken = async (): Promise<string | null> => {
13
+ const rootUser = await getData(redisKeys.user('root'), null);
14
+ if (!rootUser || typeof rootUser !== 'object') return null;
15
+ const record = rootUser as Record<string, unknown>;
16
+ const token = record.token;
17
+ return typeof token === 'string' ? token : null;
18
+ };
19
+
20
+ const findUserByToken = async (token: string): Promise<string | null> => {
21
+ const keys = await getKeys(redisKeys.users());
22
+ for (const key of keys) {
23
+ const userNameFromKey = key.slice(redisKeys.users().length);
24
+ if (!userNameFromKey) continue;
25
+
26
+ const user = await getData(redisKeys.user(userNameFromKey), null);
27
+ if (!user || typeof user !== 'object') continue;
28
+ const record = user as Record<string, unknown>;
29
+ if (record.token !== token) continue;
30
+ const userName =
31
+ typeof record.userName === 'string' ? record.userName : userNameFromKey;
32
+ if (userName) return userName;
33
+ }
34
+ return null;
35
+ };
36
+
37
+ const isSecureRequest = (req: NextRequest) => {
38
+ if (req.nextUrl.protocol === 'https:') return true;
39
+ const forwarded = req.headers.get('x-forwarded-proto');
40
+ if (forwarded) return forwarded.split(',')[0].trim() === 'https';
41
+ const nextAuthUrl = process.env.NEXTAUTH_URL;
42
+ return typeof nextAuthUrl === 'string' && nextAuthUrl.startsWith('https://');
43
+ };
44
+
45
+ const issueSession = async (
46
+ req: NextRequest,
47
+ userName: string,
48
+ response: NextResponse,
49
+ ) => {
50
+ const secret = process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET;
51
+ if (!secret) return response;
52
+
53
+ const secure = isSecureRequest(req);
54
+ const cookieName = secure ? SECURE_SESSION_COOKIE_NAME : SESSION_COOKIE_NAME;
55
+ const jwt = await encode({
56
+ token: { id: userName, name: userName },
57
+ secret,
58
+ salt: cookieName,
59
+ maxAge: SESSION_MAX_AGE,
60
+ });
61
+
62
+ response.cookies.set(cookieName, jwt, {
63
+ httpOnly: true,
64
+ sameSite: 'lax',
65
+ path: '/',
66
+ secure,
67
+ maxAge: SESSION_MAX_AGE,
68
+ });
69
+
70
+ return response;
71
+ };
72
+
73
+ export const proxy = async (req: NextRequest) => {
74
+ const { pathname } = req.nextUrl;
75
+
76
+ if (
77
+ pathname.startsWith('/_next') ||
78
+ pathname === '/favicon.ico' ||
79
+ pathname.startsWith(SIGNIN_PATH) ||
80
+ pathname.startsWith('/api/auth') ||
81
+ pathname.startsWith(SCREENSHOT_API_PREFIX) ||
82
+ PUBLIC_FILE_RE.test(pathname)
83
+ ) {
84
+ return NextResponse.next();
85
+ }
86
+
87
+ const queryToken = req.nextUrl.searchParams.get('token');
88
+ if (queryToken) {
89
+ const rootToken = await getRootToken();
90
+ if (rootToken && queryToken === rootToken) {
91
+ const cleaned = new URL(req.url);
92
+ cleaned.searchParams.delete('token');
93
+ const res =
94
+ req.method === 'GET' && !pathname.startsWith('/api')
95
+ ? NextResponse.redirect(cleaned)
96
+ : NextResponse.next();
97
+ return issueSession(req, 'root', res);
98
+ }
99
+
100
+ const userName = await findUserByToken(queryToken);
101
+ if (userName) {
102
+ const cleaned = new URL(req.url);
103
+ cleaned.searchParams.delete('token');
104
+ const res =
105
+ req.method === 'GET' && !pathname.startsWith('/api')
106
+ ? NextResponse.redirect(cleaned)
107
+ : NextResponse.next();
108
+ return issueSession(req, userName, res);
109
+ }
110
+ }
111
+
112
+ const secret = process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET;
113
+ const token = await getToken({
114
+ req,
115
+ secret,
116
+ secureCookie: isSecureRequest(req),
117
+ });
118
+
119
+ if (!token) {
120
+ if (pathname.startsWith('/api')) {
121
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
122
+ }
123
+
124
+ const signInUrl = new URL(SIGNIN_PATH, req.url);
125
+ const nextAuthBase = process.env.NEXTAUTH_URL;
126
+ const currentUrl = new URL(req.url);
127
+ const callbackUrl = nextAuthBase
128
+ ? new URL(
129
+ `${currentUrl.pathname}${currentUrl.search}`,
130
+ nextAuthBase,
131
+ ).toString()
132
+ : req.url;
133
+ signInUrl.searchParams.set('callbackUrl', callbackUrl);
134
+ return NextResponse.redirect(signInUrl);
135
+ }
136
+
137
+ return NextResponse.next();
138
+ };
139
+
140
+ export const config = {
141
+ matcher: ['/:path*'],
142
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "types": ["jest", "node"],
6
+ "allowJs": true,
7
+ "skipLibCheck": true,
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "bundler",
13
+ "baseUrl": "./src",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "jsx": "react-jsx",
17
+ "incremental": true,
18
+ "plugins": [
19
+ {
20
+ "name": "next"
21
+ }
22
+ ],
23
+ "paths": {
24
+ "@app/*": ["app/*"],
25
+ "@actions/*": ["app/actions/*"],
26
+ "@store": ["app/store/index"],
27
+ "@shared/*": ["app/components/Shared/*"],
28
+ "@UI": ["app/components/UI/index"],
29
+ "@components/*": ["app/components/*"]
30
+ }
31
+ },
32
+ "include": [
33
+ "next-env.d.ts",
34
+ "src/**/*.ts",
35
+ "src/**/*.tsx",
36
+ ".next/types/**/*.ts",
37
+ ".next/dev/types/**/*.ts"
38
+ ],
39
+ "exclude": ["node_modules"]
40
+ }