@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 @@
1
+ export { createEntryLinePointFigure } from '@tradejs/core/figures';
@@ -0,0 +1 @@
1
+ export { createEntryPointsPointFigure } from '@tradejs/core/figures';
@@ -0,0 +1 @@
1
+ export { createEntryZonePointFigure } from '@tradejs/core/figures';
@@ -0,0 +1,213 @@
1
+ import { registerFigure } from 'klinecharts';
2
+ import { diamond } from './diamond';
3
+ import { rectangle } from './rectangle';
4
+ import { circle } from './circle';
5
+ import { star } from './star';
6
+ import { square } from './square';
7
+ import { triangle } from './triangle';
8
+ import { label } from './label';
9
+
10
+ // label
11
+ registerFigure({
12
+ name: 'btLabel',
13
+ draw: (
14
+ ctx: CanvasRenderingContext2D,
15
+ attrs: {
16
+ x: number;
17
+ y: number;
18
+ text: string;
19
+ width: number;
20
+ height: number;
21
+ color: string;
22
+ },
23
+ ) => {
24
+ label({
25
+ ctx,
26
+ x: attrs.x,
27
+ y: attrs.y,
28
+ text: attrs.text,
29
+ width: attrs.width,
30
+ height: attrs.height,
31
+ color: attrs.color,
32
+ });
33
+ },
34
+ checkEventOn: (coordinate, attrs) => {
35
+ const { x, y } = coordinate;
36
+ const { width, height } = attrs;
37
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
38
+ },
39
+ });
40
+
41
+ // прямоугольник
42
+ registerFigure({
43
+ name: 'btRect',
44
+ draw: (
45
+ ctx: CanvasRenderingContext2D,
46
+ attrs: {
47
+ x: number;
48
+ y: number;
49
+ width: number;
50
+ height: number;
51
+ color: string;
52
+ },
53
+ ) => {
54
+ rectangle({
55
+ ctx,
56
+ x: attrs.x,
57
+ y: attrs.y,
58
+ width: attrs.width,
59
+ height: attrs.height,
60
+ color: attrs.color,
61
+ });
62
+ },
63
+ checkEventOn: (coordinate, attrs) => {
64
+ const { x, y } = coordinate;
65
+ const { width, height } = attrs;
66
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
67
+ },
68
+ });
69
+
70
+ // ромб
71
+ registerFigure({
72
+ name: 'btDiamond',
73
+ draw: (
74
+ ctx: CanvasRenderingContext2D,
75
+ attrs: {
76
+ x: number;
77
+ y: number;
78
+ width: number;
79
+ height: number;
80
+ color: string;
81
+ },
82
+ ) => {
83
+ diamond({
84
+ ctx,
85
+ x: attrs.x,
86
+ y: attrs.y,
87
+ width: attrs.width,
88
+ height: attrs.height,
89
+ color: attrs.color,
90
+ });
91
+ },
92
+ checkEventOn: (coordinate, attrs) => {
93
+ const { x, y } = coordinate;
94
+ const { width, height } = attrs;
95
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
96
+ },
97
+ });
98
+
99
+ // звезда / крестик
100
+ registerFigure({
101
+ name: 'btStar',
102
+ draw: (
103
+ ctx: CanvasRenderingContext2D,
104
+ attrs: {
105
+ x: number;
106
+ y: number;
107
+ width: number;
108
+ height: number;
109
+ color: string;
110
+ },
111
+ ) => {
112
+ star({
113
+ ctx,
114
+ x: attrs.x,
115
+ y: attrs.y,
116
+ width: attrs.width,
117
+ height: attrs.height,
118
+ color: attrs.color,
119
+ });
120
+ },
121
+ checkEventOn: (coordinate, attrs) => {
122
+ const { x, y } = coordinate;
123
+ const { width, height } = attrs;
124
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
125
+ },
126
+ });
127
+
128
+ // кружок
129
+ registerFigure({
130
+ name: 'btCircle',
131
+ draw: (
132
+ ctx: CanvasRenderingContext2D,
133
+ attrs: {
134
+ x: number;
135
+ y: number;
136
+ width: number;
137
+ height: number;
138
+ color: string;
139
+ },
140
+ ) => {
141
+ circle({
142
+ ctx,
143
+ x: attrs.x,
144
+ y: attrs.y,
145
+ width: attrs.width,
146
+ height: attrs.height,
147
+ color: attrs.color,
148
+ });
149
+ },
150
+ checkEventOn: (coordinate, attrs) => {
151
+ const { x, y } = coordinate;
152
+ const { width, height } = attrs;
153
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
154
+ },
155
+ });
156
+
157
+ // квадрат
158
+ registerFigure({
159
+ name: 'btSquare',
160
+ draw: (
161
+ ctx: CanvasRenderingContext2D,
162
+ attrs: {
163
+ x: number;
164
+ y: number;
165
+ width: number;
166
+ height: number;
167
+ color: string;
168
+ },
169
+ ) => {
170
+ square({
171
+ ctx,
172
+ x: attrs.x,
173
+ y: attrs.y,
174
+ width: attrs.width,
175
+ height: attrs.height,
176
+ color: attrs.color,
177
+ });
178
+ },
179
+ checkEventOn: (coordinate, attrs) => {
180
+ const { x, y } = coordinate;
181
+ const { width, height } = attrs;
182
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
183
+ },
184
+ });
185
+
186
+ // треугольник
187
+ registerFigure({
188
+ name: 'btTriangle',
189
+ draw: (
190
+ ctx: CanvasRenderingContext2D,
191
+ attrs: {
192
+ x: number;
193
+ y: number;
194
+ width: number;
195
+ height: number;
196
+ color: string;
197
+ },
198
+ ) => {
199
+ triangle({
200
+ ctx,
201
+ x: attrs.x,
202
+ y: attrs.y,
203
+ width: attrs.width,
204
+ height: attrs.height,
205
+ color: attrs.color,
206
+ });
207
+ },
208
+ checkEventOn: (coordinate, attrs) => {
209
+ const { x, y } = coordinate;
210
+ const { width, height } = attrs;
211
+ return Math.abs(x * height) + Math.abs(y * width) <= (width * height) / 2;
212
+ },
213
+ });
@@ -0,0 +1,14 @@
1
+ import { Figure } from '@tradejs/types';
2
+
3
+ export const label = ({ ctx, x, y, text = '', color }: Figure) => {
4
+ ctx.save();
5
+
6
+ ctx.font = '10px sans-serif';
7
+ ctx.textAlign = 'left';
8
+ ctx.textBaseline = 'middle';
9
+
10
+ ctx.fillStyle = color;
11
+ ctx.fillText(text, x + 6, y);
12
+
13
+ ctx.restore();
14
+ };
@@ -0,0 +1,20 @@
1
+ import { Figure } from '@tradejs/types';
2
+
3
+ export const rectangle = ({
4
+ ctx,
5
+ x: baseX,
6
+ y: baseY,
7
+ width,
8
+ height,
9
+ color,
10
+ }: Figure) => {
11
+ const x = baseX - width / 2;
12
+ const y = baseY - height / 2;
13
+
14
+ ctx.fillStyle = color;
15
+ ctx.fillRect(x, y, width, height);
16
+
17
+ ctx.lineWidth = 1;
18
+ ctx.strokeStyle = 'white';
19
+ ctx.strokeRect(x, y, width, height);
20
+ };
@@ -0,0 +1,21 @@
1
+ interface FigureParams {
2
+ ctx: CanvasRenderingContext2D;
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ color: string;
8
+ }
9
+
10
+ export const square = ({ ctx, x, y, width, height, color }: FigureParams) => {
11
+ const size = Math.min(width, height);
12
+ const half = size / 2;
13
+
14
+ ctx.save();
15
+ ctx.beginPath();
16
+ ctx.rect(x - half, y - half, size, size);
17
+ ctx.fillStyle = color;
18
+ ctx.fill();
19
+ ctx.closePath();
20
+ ctx.restore();
21
+ };
@@ -0,0 +1,39 @@
1
+ import { Figure } from '@tradejs/types';
2
+
3
+ export const star = ({
4
+ ctx,
5
+ x: baseX,
6
+ y: baseY,
7
+ width: baseWidth,
8
+ height: baseHeight,
9
+ color,
10
+ }: Figure) => {
11
+ const x = baseX - 10;
12
+ const y = baseY - 10;
13
+ const width = baseWidth + 10;
14
+ const height = baseHeight + 10;
15
+ const cx = x + width / 2;
16
+ const cy = y + height / 2;
17
+ const outerRadius = Math.min(width, height) / 2;
18
+ const innerRadius = outerRadius / 2.5;
19
+ const points = 5;
20
+
21
+ ctx.beginPath();
22
+ for (let i = 0; i < points * 2; i++) {
23
+ const angle = (Math.PI / points) * i;
24
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
25
+ const sx = cx + Math.cos(angle - Math.PI / 2) * radius;
26
+ const sy = cy + Math.sin(angle - Math.PI / 2) * radius;
27
+ if (i === 0) {
28
+ ctx.moveTo(sx, sy);
29
+ } else {
30
+ ctx.lineTo(sx, sy);
31
+ }
32
+ }
33
+ ctx.closePath();
34
+ ctx.fillStyle = color;
35
+ ctx.fill();
36
+ ctx.lineWidth = 1;
37
+ ctx.strokeStyle = 'white';
38
+ ctx.stroke();
39
+ };
@@ -0,0 +1,44 @@
1
+ type Coordinate = { x: number; y: number };
2
+
3
+ type OverlayWithExtendData<T> = {
4
+ extendData?: T;
5
+ };
6
+
7
+ type CreatePointFiguresParams<T> = {
8
+ coordinates: Coordinate[];
9
+ overlay: OverlayWithExtendData<T>;
10
+ };
11
+
12
+ export type TradeZoneMode = 'TP' | 'SL';
13
+
14
+ type TradeZoneExtendData = {
15
+ mode: TradeZoneMode;
16
+ };
17
+
18
+ export const createTradeZonePointFigure = ({
19
+ coordinates,
20
+ overlay,
21
+ }: CreatePointFiguresParams<TradeZoneExtendData>) => {
22
+ const { mode } = (overlay.extendData || {
23
+ mode: 'TP',
24
+ }) as TradeZoneExtendData;
25
+ const color = mode === 'TP' ? 'rgba(34,197,94,0.22)' : 'rgba(239,68,68,0.22)';
26
+ const borderColor =
27
+ mode === 'TP' ? 'rgba(34,197,94,0.7)' : 'rgba(239,68,68,0.7)';
28
+
29
+ if (coordinates.length < 2) return [];
30
+
31
+ const [p1, p2] = coordinates;
32
+ const x = Math.min(p1.x, p2.x);
33
+ const y = Math.min(p1.y, p2.y);
34
+ const width = Math.abs(p2.x - p1.x);
35
+ const height = Math.abs(p2.y - p1.y);
36
+
37
+ return [
38
+ {
39
+ type: 'rect',
40
+ attrs: { x, y, width, height },
41
+ styles: { color, borderColor, size: 1 },
42
+ },
43
+ ];
44
+ };
@@ -0,0 +1,37 @@
1
+ import { TrendLine } from '@tradejs/types';
2
+
3
+ type Coordinate = { x: number; y: number };
4
+
5
+ type OverlayWithExtendData<T> = {
6
+ extendData?: T;
7
+ };
8
+
9
+ type CreatePointFiguresParams<T> = {
10
+ coordinates: Coordinate[];
11
+ overlay: OverlayWithExtendData<T>;
12
+ };
13
+
14
+ type TrendLineExtendData = {
15
+ mode: TrendLine['mode'];
16
+ };
17
+
18
+ export const createTrendLinePointFigure = ({
19
+ coordinates,
20
+ overlay,
21
+ }: CreatePointFiguresParams<TrendLineExtendData>) => {
22
+ const { mode } = (overlay.extendData || {
23
+ mode: 'lows',
24
+ }) as TrendLineExtendData;
25
+ const figures: any[] = [];
26
+ const color = mode === 'lows' ? '#facc15' : '#fb923c';
27
+
28
+ if (coordinates.length === 2) {
29
+ figures.push({
30
+ type: 'line',
31
+ attrs: { coordinates: [coordinates[0], coordinates[1]] },
32
+ styles: { color, size: 2, style: 'solid' },
33
+ });
34
+ }
35
+
36
+ return figures;
37
+ };
@@ -0,0 +1,26 @@
1
+ type Coordinate = { x: number; y: number };
2
+
3
+ type CreatePointFiguresParams = {
4
+ coordinates: Coordinate[];
5
+ };
6
+
7
+ export const createTrendLinePointsPointFigure = ({
8
+ coordinates,
9
+ }: CreatePointFiguresParams) => {
10
+ const figures: any[] = [];
11
+
12
+ coordinates.forEach(({ x, y }, i) => {
13
+ figures.push({
14
+ type: 'circle',
15
+ key: `pt_${i}`,
16
+ attrs: { x, y, r: 4 },
17
+ styles: {
18
+ style: 'fill',
19
+ color: '#ef4444',
20
+ },
21
+ ignoreEvent: true,
22
+ });
23
+ });
24
+
25
+ return figures;
26
+ };
@@ -0,0 +1,23 @@
1
+ interface FigureParams {
2
+ ctx: CanvasRenderingContext2D;
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ color: string;
8
+ }
9
+
10
+ export const triangle = ({ ctx, x, y, width, height, color }: FigureParams) => {
11
+ const halfWidth = width / 2;
12
+ const halfHeight = height / 2;
13
+
14
+ ctx.save();
15
+ ctx.beginPath();
16
+ ctx.moveTo(x, y - halfHeight);
17
+ ctx.lineTo(x - halfWidth, y + halfHeight);
18
+ ctx.lineTo(x + halfWidth, y + halfHeight);
19
+ ctx.closePath();
20
+ ctx.fillStyle = color;
21
+ ctx.fill();
22
+ ctx.restore();
23
+ };
@@ -0,0 +1,14 @@
1
+ export { useAtrIndicator } from './useAtrIndicator';
2
+ export { useBbIndicator } from './useBbIndicator';
3
+ export { useMaIndicator } from './useMaIndicator';
4
+ export { useEmaIndicator } from './useEmaIndicator';
5
+ export { useWmaIndicator } from './useWmaIndicator';
6
+ export { useVolIndicator } from './useVolIndicator';
7
+ export { useBtcIndicator } from './useBtcIndicator';
8
+ export { useBtcCorrelation } from './useBtcCorrelation';
9
+ export { useSpreadIndicator } from './useSpreadIndicator';
10
+ export { useSignal } from './useSignal';
11
+ export { useBacktest } from './useBacktest';
12
+ export { useSupportResistanceLines } from './useSupportResistanceLines';
13
+ export { useResize } from './useResize';
14
+ export { useSetup } from './useSetup';
@@ -0,0 +1,30 @@
1
+ export const getCloseAtOrBefore = (
2
+ candles: Array<{ timestamp: number; close: number }>,
3
+ timestamp: number,
4
+ ) => {
5
+ let left = 0;
6
+ let right = candles.length - 1;
7
+ let result: number | undefined;
8
+
9
+ while (left <= right) {
10
+ const mid = Math.floor((left + right) / 2);
11
+ const candle = candles[mid];
12
+
13
+ if (candle.timestamp <= timestamp) {
14
+ result = candle.close;
15
+ left = mid + 1;
16
+ } else {
17
+ right = mid - 1;
18
+ }
19
+ }
20
+
21
+ return result;
22
+ };
23
+
24
+ export const grayDashedLineStyle = () =>
25
+ ({
26
+ color: '#9ca3af',
27
+ size: 1,
28
+ style: 'dashed',
29
+ dashedValue: [4, 4],
30
+ }) as any;
@@ -0,0 +1,75 @@
1
+ import { useEffect } from 'react';
2
+ import { registerIndicator, Chart } from 'klinecharts';
3
+ import { ATR_PCT } from '@tradejs/indicators';
4
+
5
+ export const useAtrIndicator = (
6
+ chart: Chart | null,
7
+ enabled: boolean,
8
+ periods: number[],
9
+ ) => {
10
+ useEffect(() => {
11
+ registerIndicator({
12
+ name: 'ATR_PCT',
13
+ shortName: 'ATR%',
14
+ calcParams: periods,
15
+
16
+ figures: periods.flatMap((period) => [
17
+ {
18
+ key: `ATR_SL${period}`,
19
+ title: `ATR% SL(${period}): `,
20
+ type: 'line',
21
+ },
22
+ ]),
23
+
24
+ calc: (kLineDataList) => {
25
+ const SMA_SHORT = 7;
26
+ const SMA_LONG = 50;
27
+
28
+ const seriesByPeriod = periods.map((period) => {
29
+ const { shortLine, longLine } = ATR_PCT(
30
+ kLineDataList,
31
+ period,
32
+ SMA_SHORT,
33
+ SMA_LONG,
34
+ );
35
+
36
+ return { shortLine, longLine };
37
+ });
38
+
39
+ return kLineDataList.reduce<Record<number, Record<string, number>>>(
40
+ (acc, { timestamp }, i) => {
41
+ const out: Record<string, number> = {};
42
+
43
+ periods.forEach((period, pIdx) => {
44
+ const s = seriesByPeriod[pIdx].shortLine[i];
45
+ const l = seriesByPeriod[pIdx].longLine[i];
46
+
47
+ if (
48
+ typeof s === 'number' &&
49
+ Number.isFinite(s) &&
50
+ typeof l === 'number' &&
51
+ Number.isFinite(l)
52
+ ) {
53
+ out[`ATR_SL${period}`] = s / l;
54
+ }
55
+ });
56
+
57
+ acc[timestamp] = out;
58
+ return acc;
59
+ },
60
+ {},
61
+ );
62
+ },
63
+ });
64
+ }, [periods]);
65
+
66
+ useEffect(() => {
67
+ if (!chart || !enabled) return;
68
+
69
+ chart.createIndicator('ATR_PCT', true, { id: 'atr_pct', minHeight: 80 });
70
+
71
+ return () => {
72
+ chart.removeIndicator({ id: 'atr_pct' });
73
+ };
74
+ }, [chart, enabled]);
75
+ };