@tradejs/app 2.0.18 → 2.0.20

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 (52) hide show
  1. package/package.json +14 -8
  2. package/src/app/actions/backtest.ts +2 -1
  3. package/src/app/actions/scanner.ts +2 -1
  4. package/src/app/api/backtest/files/route.ts +2 -1
  5. package/src/app/api/backtest/test/[strategy]/[name]/route.ts +1 -1
  6. package/src/app/api/derivatives/[symbol]/[interval]/route.ts +1 -1
  7. package/src/app/api/derivatives/summary/route.ts +1 -1
  8. package/src/app/api/signal/[symbol]/[signalId]/route.ts +2 -1
  9. package/src/app/api/spread/[symbol]/[interval]/route.ts +1 -1
  10. package/src/app/api/spread/summary/route.ts +1 -1
  11. package/src/app/api/strategies/runtime/route.ts +4 -674
  12. package/src/app/api/user/runtime-deployments/[deploymentId]/route.ts +1 -1
  13. package/src/app/api/user/runtime-deployments/route.ts +2 -2
  14. package/src/app/api/user/runtime-strategy-configs/route.ts +22 -212
  15. package/src/app/api/user/trading-accounts/[accountId]/route.ts +1 -1
  16. package/src/app/components/Backtest/TestList/index.tsx +1 -1
  17. package/src/app/components/Dashboard/KlineChart/figures/circle.ts +1 -1
  18. package/src/app/components/Dashboard/KlineChart/figures/diamond.ts +1 -1
  19. package/src/app/components/Dashboard/KlineChart/figures/label.ts +1 -1
  20. package/src/app/components/Dashboard/KlineChart/figures/rectangle.ts +1 -1
  21. package/src/app/components/Dashboard/KlineChart/figures/star.ts +1 -1
  22. package/src/app/components/Dashboard/KlineChart/index.tsx +2 -1
  23. package/src/app/components/Shared/Filters/Root/index.tsx +1 -1
  24. package/src/app/components/Shared/Filters/context.ts +1 -1
  25. package/src/app/components/Strategies/RuntimeStrategyCard.tsx +82 -861
  26. package/src/app/components/Strategies/RuntimeStrategyChart.tsx +115 -184
  27. package/src/app/components/Strategies/StrategyEvidencePopover.tsx +259 -0
  28. package/src/app/components/Strategies/StrategyPerformanceCharts.tsx +419 -0
  29. package/src/app/components/Strategies/StrategySnapshotCard.tsx +122 -937
  30. package/src/app/components/UI/Segment/index.tsx +1 -1
  31. package/src/app/components/UI/Select/index.tsx +1 -1
  32. package/src/app/components/UI/SelectWithSearch/index.tsx +1 -1
  33. package/src/app/lib/backtestJobContracts.ts +67 -0
  34. package/src/app/lib/backtestJobProgress.ts +28 -0
  35. package/src/app/lib/backtestJobRequest.ts +107 -0
  36. package/src/app/lib/backtestJobs.ts +25 -257
  37. package/src/app/lib/runtimeDashboard.ts +700 -0
  38. package/src/app/lib/runtimeStrategies.ts +22 -457
  39. package/src/app/lib/runtimeStrategyConfigService.ts +279 -0
  40. package/src/app/lib/runtimeStrategyLineage.ts +264 -0
  41. package/src/app/lib/runtimeTradeReconciliation.ts +113 -0
  42. package/src/app/lib/runtimeTradeSync.ts +1 -1
  43. package/src/app/lib/strategyEvidenceTimeline.ts +298 -0
  44. package/src/app/lib/strategyPerformance.ts +387 -0
  45. package/src/app/routes/dashboard/Dashboard.tsx +2 -6
  46. package/src/app/routes/derivatives/derivativesViewModel.ts +253 -0
  47. package/src/app/routes/derivatives/page.tsx +70 -262
  48. package/src/app/store/filters.ts +2 -1
  49. package/src/app/store/indicators.ts +2 -1
  50. package/src/app/store/tests.ts +1 -1
  51. package/src/app/store/tickers.ts +2 -1
  52. package/src/app/types/ui.ts +20 -0
@@ -0,0 +1,253 @@
1
+ export type DerivativesInterval = '15m' | '1h';
2
+
3
+ export type SummaryItem = {
4
+ symbol: string;
5
+ interval: DerivativesInterval;
6
+ points: number;
7
+ last_ts: string;
8
+ first_ts: string;
9
+ latest_open_interest: number | null;
10
+ first_open_interest: number | null;
11
+ oi_change: number | null;
12
+ oi_change_pct: number | null;
13
+ latest_funding_rate: number | null;
14
+ first_funding_rate: number | null;
15
+ funding_change: number | null;
16
+ sum_liq_long: number | null;
17
+ sum_liq_short: number | null;
18
+ sum_liq_total: number | null;
19
+ };
20
+
21
+ export type SummaryResponse = {
22
+ hours: number;
23
+ items: SummaryItem[];
24
+ };
25
+
26
+ export type DetailRow = {
27
+ symbol: string;
28
+ interval: DerivativesInterval;
29
+ ts: string;
30
+ open_interest: number | null;
31
+ funding_rate: number | null;
32
+ liq_long: number | null;
33
+ liq_short: number | null;
34
+ liq_total: number | null;
35
+ };
36
+
37
+ export type DetailResponse = {
38
+ rows: DetailRow[];
39
+ symbol: string;
40
+ interval: DerivativesInterval;
41
+ };
42
+
43
+ export type PriceRow = {
44
+ close: number;
45
+ timestamp: number;
46
+ };
47
+
48
+ export type PriceResponse = {
49
+ data?: PriceRow[];
50
+ };
51
+
52
+ export type SymbolMetrics = {
53
+ symbol: string;
54
+ lastTs: string | null;
55
+ currentOpenInterest: number | null;
56
+ oiChange: number | null;
57
+ oiChangePct: number | null;
58
+ currentFundingRate: number | null;
59
+ fundingChange: number | null;
60
+ sumLiqLong: number | null;
61
+ sumLiqShort: number | null;
62
+ sumLiqTotal: number | null;
63
+ };
64
+
65
+ export type DerivativesChartRow = {
66
+ timestamp: number;
67
+ openInterest: number;
68
+ funding: number;
69
+ longLiquidations: number;
70
+ shortLiquidations: number;
71
+ };
72
+
73
+ export type PriceChartRow = {
74
+ price: number;
75
+ timestamp: number;
76
+ };
77
+
78
+ type BiasTone = 'teal' | 'green' | 'red' | 'orange' | 'gray';
79
+
80
+ export type MarketBias = {
81
+ label: string;
82
+ tone: BiasTone;
83
+ };
84
+
85
+ export const toFiniteNumber = (value: number | null | undefined) =>
86
+ typeof value === 'number' && Number.isFinite(value) ? value : null;
87
+
88
+ export const mapRowsToChartRows = (rows: DetailRow[]): DerivativesChartRow[] =>
89
+ rows.map((row) => ({
90
+ timestamp: new Date(row.ts).getTime(),
91
+ openInterest: toFiniteNumber(row.open_interest) ?? 0,
92
+ funding: (toFiniteNumber(row.funding_rate) ?? 0) * 10_000,
93
+ longLiquidations: -(toFiniteNumber(row.liq_long) ?? 0),
94
+ shortLiquidations: toFiniteNumber(row.liq_short) ?? 0,
95
+ }));
96
+
97
+ export const mapPriceRowsToChartRows = (rows: PriceRow[]): PriceChartRow[] =>
98
+ rows.map((row) => ({
99
+ price: toFiniteNumber(row.close) ?? 0,
100
+ timestamp: row.timestamp,
101
+ }));
102
+
103
+ const getBias = (metrics: SymbolMetrics): MarketBias => {
104
+ const funding = toFiniteNumber(metrics.currentFundingRate) ?? 0;
105
+ const oiChangePct = toFiniteNumber(metrics.oiChangePct) ?? 0;
106
+ const longLiq = toFiniteNumber(metrics.sumLiqLong) ?? 0;
107
+ const shortLiq = toFiniteNumber(metrics.sumLiqShort) ?? 0;
108
+
109
+ if (shortLiq > longLiq * 1.35) {
110
+ return { label: 'Short squeeze', tone: 'green' };
111
+ }
112
+
113
+ if (longLiq > shortLiq * 1.35) {
114
+ return { label: 'Long flush', tone: 'red' };
115
+ }
116
+
117
+ if (oiChangePct > 1 && funding > 0) {
118
+ return { label: 'Crowded longs', tone: 'orange' };
119
+ }
120
+
121
+ if (oiChangePct > 1 && funding < 0) {
122
+ return { label: 'Crowded shorts', tone: 'teal' };
123
+ }
124
+
125
+ return { label: 'Balanced', tone: 'gray' };
126
+ };
127
+
128
+ const buildMetricsFromRows = (
129
+ symbol: string,
130
+ rows: DetailRow[],
131
+ summaryRow?: SummaryItem,
132
+ ): SymbolMetrics => {
133
+ if (!rows.length) {
134
+ return {
135
+ symbol,
136
+ lastTs: summaryRow?.last_ts ?? null,
137
+ currentOpenInterest: summaryRow?.latest_open_interest ?? null,
138
+ oiChange: summaryRow?.oi_change ?? null,
139
+ oiChangePct: summaryRow?.oi_change_pct ?? null,
140
+ currentFundingRate: summaryRow?.latest_funding_rate ?? null,
141
+ fundingChange: summaryRow?.funding_change ?? null,
142
+ sumLiqLong: summaryRow?.sum_liq_long ?? null,
143
+ sumLiqShort: summaryRow?.sum_liq_short ?? null,
144
+ sumLiqTotal: summaryRow?.sum_liq_total ?? null,
145
+ };
146
+ }
147
+
148
+ const first = rows[0];
149
+ const last = rows[rows.length - 1];
150
+ const firstOi = toFiniteNumber(first.open_interest);
151
+ const lastOi = toFiniteNumber(last.open_interest);
152
+ const oiChange = firstOi != null && lastOi != null ? lastOi - firstOi : null;
153
+ const oiChangePct =
154
+ oiChange != null && firstOi != null && Math.abs(firstOi) > 0
155
+ ? (oiChange / Math.abs(firstOi)) * 100
156
+ : null;
157
+ const firstFunding = toFiniteNumber(first.funding_rate);
158
+ const lastFunding = toFiniteNumber(last.funding_rate);
159
+ const fundingChange =
160
+ firstFunding != null && lastFunding != null
161
+ ? lastFunding - firstFunding
162
+ : null;
163
+
164
+ return rows.reduce<SymbolMetrics>(
165
+ (metrics, row) => ({
166
+ ...metrics,
167
+ sumLiqLong: (metrics.sumLiqLong ?? 0) + Number(row.liq_long || 0),
168
+ sumLiqShort: (metrics.sumLiqShort ?? 0) + Number(row.liq_short || 0),
169
+ sumLiqTotal: (metrics.sumLiqTotal ?? 0) + Number(row.liq_total || 0),
170
+ }),
171
+ {
172
+ symbol,
173
+ lastTs: last.ts,
174
+ currentOpenInterest: lastOi,
175
+ oiChange,
176
+ oiChangePct,
177
+ currentFundingRate: lastFunding,
178
+ fundingChange,
179
+ sumLiqLong: 0,
180
+ sumLiqShort: 0,
181
+ sumLiqTotal: 0,
182
+ },
183
+ );
184
+ };
185
+
186
+ export const buildDerivativesDashboardViewModel = ({
187
+ symbols,
188
+ selectedInterval,
189
+ summary,
190
+ detailsBySymbol,
191
+ pricesBySymbol,
192
+ summaryLoading,
193
+ detailLoading,
194
+ summaryError,
195
+ detailError,
196
+ }: {
197
+ symbols: readonly string[];
198
+ selectedInterval: DerivativesInterval;
199
+ summary: SummaryResponse | null;
200
+ detailsBySymbol: Record<string, DetailRow[]>;
201
+ pricesBySymbol: Record<string, PriceRow[]>;
202
+ summaryLoading: boolean;
203
+ detailLoading: boolean;
204
+ summaryError: string;
205
+ detailError: string;
206
+ }) => {
207
+ const filteredSummary = (summary?.items ?? []).filter(
208
+ (item) =>
209
+ item.interval === selectedInterval && symbols.includes(item.symbol),
210
+ );
211
+ const summaryBySymbol = Object.fromEntries(
212
+ filteredSummary.map((item) => [item.symbol, item]),
213
+ ) as Record<string, SummaryItem | undefined>;
214
+ const metricsBySymbol = Object.fromEntries(
215
+ symbols.map((symbol) => [
216
+ symbol,
217
+ buildMetricsFromRows(
218
+ symbol,
219
+ detailsBySymbol[symbol] ?? [],
220
+ summaryBySymbol[symbol],
221
+ ),
222
+ ]),
223
+ ) as Record<string, SymbolMetrics>;
224
+ const chartDataBySymbol = Object.fromEntries(
225
+ symbols.map((symbol) => [
226
+ symbol,
227
+ {
228
+ derivatives: mapRowsToChartRows(detailsBySymbol[symbol] ?? []),
229
+ prices: mapPriceRowsToChartRows(pricesBySymbol[symbol] ?? []),
230
+ },
231
+ ]),
232
+ ) as Record<
233
+ string,
234
+ { derivatives: DerivativesChartRow[]; prices: PriceChartRow[] }
235
+ >;
236
+
237
+ return {
238
+ metricsBySymbol,
239
+ chartDataBySymbol,
240
+ overviewRows: symbols.map((symbol) => ({
241
+ symbol,
242
+ metrics: metricsBySymbol[symbol],
243
+ bias: getBias(metricsBySymbol[symbol]),
244
+ })),
245
+ noSummaryData:
246
+ !summaryLoading && !summaryError && filteredSummary.length === 0,
247
+ noDetailData:
248
+ !detailLoading &&
249
+ !detailError &&
250
+ symbols.every((symbol) => (detailsBySymbol[symbol] ?? []).length === 0),
251
+ showSkeleton: (summaryLoading || detailLoading) && !summary,
252
+ };
253
+ };