@tradejs/app 3.1.26-beta.243 → 3.1.26-beta.244

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/app",
3
- "version": "3.1.26-beta.243",
3
+ "version": "3.1.26-beta.244",
4
4
  "description": "Installable Next.js UI for the TradeJS TypeScript framework: dashboards, backtests, charts, and runtime data.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -56,11 +56,11 @@
56
56
  "@chakra-ui/charts": "3.36.1",
57
57
  "@chakra-ui/react": "3.36.1",
58
58
  "@emotion/react": "^11.14.0",
59
- "@tradejs/core": "^3.1.26-beta.243",
60
- "@tradejs/indicators": "^3.1.26-beta.243",
61
- "@tradejs/infra": "^3.1.26-beta.243",
62
- "@tradejs/node": "^3.1.26-beta.243",
63
- "@tradejs/types": "^3.1.26-beta.243",
59
+ "@tradejs/core": "^3.1.26-beta.244",
60
+ "@tradejs/indicators": "^3.1.26-beta.244",
61
+ "@tradejs/infra": "^3.1.26-beta.244",
62
+ "@tradejs/node": "^3.1.26-beta.244",
63
+ "@tradejs/types": "^3.1.26-beta.244",
64
64
  "@types/bcryptjs": "2.4.6",
65
65
  "@types/lodash": "4.17.24",
66
66
  "@types/node": "24.13.3",
@@ -20,6 +20,7 @@ import {
20
20
  } from '#components/Shared/OrdersDrawer';
21
21
  import { RuntimeStrategyChart } from './RuntimeStrategyChart';
22
22
  import { RuntimeStrategyConfigDrawer } from './RuntimeStrategyConfigDrawer';
23
+ import { RuntimeStrategyRevisionsDrawer } from './RuntimeStrategyRevisionsDrawer';
23
24
  import { RuntimeStrategyStatsDrawer } from './RuntimeStrategyStatsDrawer';
24
25
  import {
25
26
  buildRuntimeStrategyCardViewModel,
@@ -65,6 +66,7 @@ export const RuntimeStrategyCard = ({
65
66
  const [configOpen, setConfigOpen] = useState(false);
66
67
  const [ordersOpen, setOrdersOpen] = useState(false);
67
68
  const [statsOpen, setStatsOpen] = useState(false);
69
+ const [revisionsOpen, setRevisionsOpen] = useState(false);
68
70
  const [controlSaving, setControlSaving] = useState(false);
69
71
  const viewModel = useMemo(
70
72
  () => buildRuntimeStrategyCardViewModel(strategy),
@@ -209,6 +211,12 @@ export const RuntimeStrategyCard = ({
209
211
  <Menu.Item value="stat" onClick={() => setStatsOpen(true)}>
210
212
  Stat
211
213
  </Menu.Item>
214
+ <Menu.Item
215
+ value="revisions"
216
+ onClick={() => setRevisionsOpen(true)}
217
+ >
218
+ Revisions
219
+ </Menu.Item>
212
220
  </Menu.Content>
213
221
  </Menu.Positioner>
214
222
  </Portal>
@@ -239,6 +247,12 @@ export const RuntimeStrategyCard = ({
239
247
  viewModel={viewModel}
240
248
  />
241
249
 
250
+ <RuntimeStrategyRevisionsDrawer
251
+ open={revisionsOpen}
252
+ strategy={strategy}
253
+ onOpenChange={setRevisionsOpen}
254
+ />
255
+
242
256
  <RuntimeStrategyChart
243
257
  orderLog={strategy.orderLog}
244
258
  revisionChanges={strategy.revisionChanges}
@@ -109,14 +109,6 @@ export const RuntimeStrategyChart = ({
109
109
  x={change.timestamp}
110
110
  stroke={chart.color('orange.400')}
111
111
  strokeDasharray="4 4"
112
- label={{
113
- value: `Revision ${change.strategyRevision
114
- .replace(/^sr1:/, '')
115
- .slice(0, 8)}`,
116
- offset: 8,
117
- fill: chart.color('orange.300'),
118
- position: 'insideTopRight',
119
- }}
120
112
  />
121
113
  ))}
122
114
  <TimeSeriesXAxis
@@ -0,0 +1,154 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import {
5
+ Badge,
6
+ Box,
7
+ Button,
8
+ CloseButton,
9
+ Drawer,
10
+ Flex,
11
+ Portal,
12
+ Text,
13
+ } from '@chakra-ui/react';
14
+ import type {
15
+ RuntimeStrategyRevisionChange,
16
+ RuntimeStrategyView,
17
+ } from '@tradejs/types';
18
+ import { formatDateTime } from '#components/Shared/OrdersDrawer';
19
+
20
+ interface RuntimeStrategyRevisionItem {
21
+ strategyRevision: string;
22
+ changedAt: number | null;
23
+ current: boolean;
24
+ }
25
+
26
+ export const buildRuntimeStrategyRevisionItems = ({
27
+ strategyRevision,
28
+ revisionChanges,
29
+ }: {
30
+ strategyRevision: string;
31
+ revisionChanges: RuntimeStrategyRevisionChange[];
32
+ }): RuntimeStrategyRevisionItem[] => {
33
+ const changedAtByRevision = new Map<string, number>();
34
+
35
+ for (const change of revisionChanges) {
36
+ const previousTimestamp = changedAtByRevision.get(change.strategyRevision);
37
+ if (
38
+ Number.isFinite(change.timestamp) &&
39
+ (previousTimestamp == null || change.timestamp > previousTimestamp)
40
+ ) {
41
+ changedAtByRevision.set(change.strategyRevision, change.timestamp);
42
+ }
43
+ }
44
+
45
+ const revisions = new Set([strategyRevision, ...changedAtByRevision.keys()]);
46
+
47
+ return [...revisions]
48
+ .map((revision) => ({
49
+ strategyRevision: revision,
50
+ changedAt: changedAtByRevision.get(revision) ?? null,
51
+ current: revision === strategyRevision,
52
+ }))
53
+ .sort((left, right) => {
54
+ if (left.current !== right.current) return left.current ? -1 : 1;
55
+ return (right.changedAt ?? 0) - (left.changedAt ?? 0);
56
+ });
57
+ };
58
+
59
+ export const RuntimeStrategyRevisionsDrawer = ({
60
+ open,
61
+ strategy,
62
+ onOpenChange,
63
+ }: {
64
+ open: boolean;
65
+ strategy: RuntimeStrategyView;
66
+ onOpenChange: (open: boolean) => void;
67
+ }) => {
68
+ const revisions = useMemo(
69
+ () =>
70
+ buildRuntimeStrategyRevisionItems({
71
+ strategyRevision: strategy.strategyRevision,
72
+ revisionChanges: strategy.revisionChanges,
73
+ }),
74
+ [strategy.revisionChanges, strategy.strategyRevision],
75
+ );
76
+
77
+ return (
78
+ <Drawer.Root
79
+ size="md"
80
+ open={open}
81
+ onOpenChange={(event) => onOpenChange(event.open)}
82
+ >
83
+ <Portal>
84
+ <Drawer.Backdrop />
85
+ <Drawer.Positioner>
86
+ <Drawer.Content bg="gray.950">
87
+ <Drawer.Header>
88
+ <Drawer.Title>{strategy.strategyName} revisions</Drawer.Title>
89
+ <Drawer.CloseTrigger asChild>
90
+ <CloseButton size="sm" />
91
+ </Drawer.CloseTrigger>
92
+ </Drawer.Header>
93
+ <Drawer.Body
94
+ display="flex"
95
+ flexDirection="column"
96
+ gap={3}
97
+ overflowY="auto"
98
+ >
99
+ <Text color="gray.400">
100
+ Current revision and all revision changes recorded in the
101
+ selected runtime window.
102
+ </Text>
103
+
104
+ {revisions.map((revision) => (
105
+ <Box
106
+ key={revision.strategyRevision}
107
+ p={4}
108
+ borderWidth="1px"
109
+ borderColor={revision.current ? 'teal.800' : 'gray.800'}
110
+ borderRadius="md"
111
+ bg="gray.900"
112
+ >
113
+ <Flex
114
+ alignItems="center"
115
+ justifyContent="space-between"
116
+ gap={3}
117
+ >
118
+ <Text fontSize="xs" color="gray.500">
119
+ Strategy revision
120
+ </Text>
121
+ {revision.current ? (
122
+ <Badge colorPalette="teal" variant="subtle">
123
+ Current
124
+ </Badge>
125
+ ) : null}
126
+ </Flex>
127
+ <Text
128
+ mt={2}
129
+ fontFamily="mono"
130
+ fontSize="sm"
131
+ color="gray.200"
132
+ overflowWrap="anywhere"
133
+ >
134
+ {revision.strategyRevision}
135
+ </Text>
136
+ <Text mt={2} fontSize="xs" color="gray.500">
137
+ {revision.changedAt == null
138
+ ? 'No revision change recorded in this window'
139
+ : `Changed at ${formatDateTime(revision.changedAt)}`}
140
+ </Text>
141
+ </Box>
142
+ ))}
143
+ </Drawer.Body>
144
+ <Drawer.Footer>
145
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
146
+ Close
147
+ </Button>
148
+ </Drawer.Footer>
149
+ </Drawer.Content>
150
+ </Drawer.Positioner>
151
+ </Portal>
152
+ </Drawer.Root>
153
+ );
154
+ };