@dxfeed/dxlink-console-market-data 0.0.0-stage → 0.10.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.
@@ -0,0 +1,444 @@
1
+ import {
2
+ DocLink,
3
+ ORDER_SOURCES_DOC_URL
4
+ } from "./chunk-UHWTI765.mjs";
5
+
6
+ // src/dom/plugin.tsx
7
+ import { defineChannelPlugin } from "@dxfeed/dxlink-console-core";
8
+ import ViewColumnIcon2 from "@mui/icons-material/ViewColumn";
9
+
10
+ // src/dom/dom-channel.tsx
11
+ import { DXLinkChannelState as DXLinkChannelState2 } from "@dxfeed/dxlink-api";
12
+ import { useVM } from "@dxfeed/dxlink-console-core";
13
+ import { ChannelWidget } from "@dxfeed/dxlink-console-core";
14
+ import { useConnectionVM } from "@dxfeed/dxlink-console-core";
15
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
16
+ import PlayArrowIcon from "@mui/icons-material/PlayArrow";
17
+ import ViewColumnIcon from "@mui/icons-material/ViewColumn";
18
+ import Accordion from "@mui/material/Accordion";
19
+ import AccordionDetails from "@mui/material/AccordionDetails";
20
+ import AccordionSummary from "@mui/material/AccordionSummary";
21
+ import Box from "@mui/material/Box";
22
+ import Button from "@mui/material/Button";
23
+ import Chip from "@mui/material/Chip";
24
+ import Stack from "@mui/material/Stack";
25
+ import Table from "@mui/material/Table";
26
+ import TableBody from "@mui/material/TableBody";
27
+ import TableCell from "@mui/material/TableCell";
28
+ import TableHead from "@mui/material/TableHead";
29
+ import TableRow from "@mui/material/TableRow";
30
+ import TextField from "@mui/material/TextField";
31
+ import Typography from "@mui/material/Typography";
32
+ import { useEffect, useState } from "react";
33
+
34
+ // src/dom/dom-view-model.ts
35
+ import { DXLinkChannelState, DXLinkDepthOfMarket, DXLinkLogLevel } from "@dxfeed/dxlink-api";
36
+ import { ChannelErrorTracker, initialChannelErrorState } from "@dxfeed/dxlink-console-core";
37
+ import { createStore } from "zustand/vanilla";
38
+ var FLUSH_INTERVAL_MS = 100;
39
+ var DomViewModel = class {
40
+ constructor(client, params) {
41
+ this.store = createStore(() => ({
42
+ channelState: DXLinkChannelState.REQUESTED,
43
+ config: null,
44
+ snapshot: null,
45
+ ...initialChannelErrorState()
46
+ }));
47
+ this.channelErrors = new ChannelErrorTracker(this.store);
48
+ this.dom = null;
49
+ this.pendingSnapshot = null;
50
+ this.flushHandle = null;
51
+ this.start = () => {
52
+ if (this.dom !== null) return;
53
+ const dom = new DXLinkDepthOfMarket(
54
+ this.client,
55
+ {
56
+ symbol: this.params.symbol,
57
+ sources: this.params.sources
58
+ },
59
+ {
60
+ feed: this.params.feed,
61
+ space: this.params.space,
62
+ // A debug console wants the protocol traffic in the browser log.
63
+ logLevel: DXLinkLogLevel.DEBUG
64
+ }
65
+ );
66
+ dom.addSnapshotListener(this.handleSnapshot);
67
+ dom.addConfigChangeListener(this.handleConfig);
68
+ dom.addStateChangeListener(this.handleState);
69
+ this.channelErrors.attach(dom.getChannel());
70
+ this.dom = dom;
71
+ this.store.setState({ channelState: dom.getState(), config: dom.getConfig() });
72
+ };
73
+ this.stop = () => {
74
+ const dom = this.dom;
75
+ if (dom === null) return;
76
+ this.dom = null;
77
+ if (this.flushHandle !== null) {
78
+ clearTimeout(this.flushHandle);
79
+ this.flushHandle = null;
80
+ }
81
+ this.pendingSnapshot = null;
82
+ dom.removeSnapshotListener(this.handleSnapshot);
83
+ dom.removeConfigChangeListener(this.handleConfig);
84
+ dom.removeStateChangeListener(this.handleState);
85
+ this.channelErrors.detach(dom.getChannel());
86
+ dom.close();
87
+ };
88
+ this.configure = (accept) => {
89
+ this.dom?.configure(accept);
90
+ };
91
+ this.clearErrors = () => this.channelErrors.clear();
92
+ this.close = () => {
93
+ this.stop();
94
+ };
95
+ this.dispose = () => {
96
+ this.stop();
97
+ };
98
+ this.handleSnapshot = (time, bids, asks) => {
99
+ if (this.dom === null) return;
100
+ this.pendingSnapshot = { time, bids, asks };
101
+ if (this.flushHandle === null) {
102
+ this.flushHandle = setTimeout(this.flush, FLUSH_INTERVAL_MS);
103
+ }
104
+ };
105
+ this.flush = () => {
106
+ this.flushHandle = null;
107
+ if (this.dom === null || this.pendingSnapshot === null) return;
108
+ this.store.setState({ snapshot: this.pendingSnapshot });
109
+ this.pendingSnapshot = null;
110
+ };
111
+ this.handleConfig = (config) => {
112
+ this.store.setState({ config });
113
+ };
114
+ this.handleState = (state) => {
115
+ this.store.setState({ channelState: state });
116
+ };
117
+ this.client = client;
118
+ this.params = params;
119
+ }
120
+ };
121
+
122
+ // src/dom/dom-channel.tsx
123
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
124
+ var parseList = (raw) => raw.split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
125
+ var DomStatusChip = ({ state }) => {
126
+ if (state === DXLinkChannelState2.OPENED) {
127
+ return /* @__PURE__ */ jsx(Chip, { size: "small", color: "success", variant: "outlined", label: "streaming" });
128
+ }
129
+ if (state === DXLinkChannelState2.CLOSED) {
130
+ return /* @__PURE__ */ jsx(Chip, { size: "small", variant: "outlined", label: "closed" });
131
+ }
132
+ return /* @__PURE__ */ jsx(Chip, { size: "small", color: "warning", variant: "outlined", label: "opening" });
133
+ };
134
+ var num = (value) => Number.isFinite(value) ? value.toLocaleString(void 0, { maximumFractionDigits: 6 }) : "\u2014";
135
+ var Ladder = ({ snapshot }) => {
136
+ const bids = [...snapshot.bids].sort((a, b) => b.price - a.price);
137
+ const asks = [...snapshot.asks].sort((a, b) => a.price - b.price);
138
+ const rows = Math.max(bids.length, asks.length);
139
+ if (rows === 0) {
140
+ return /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Empty book." });
141
+ }
142
+ const cell = (order, side) => {
143
+ if (order === void 0) {
144
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
145
+ /* @__PURE__ */ jsx(TableCell, {}),
146
+ /* @__PURE__ */ jsx(TableCell, {})
147
+ ] });
148
+ }
149
+ const size = /* @__PURE__ */ jsx(TableCell, { align: "right", sx: { fontVariantNumeric: "tabular-nums" }, children: num(order.size) });
150
+ const price = /* @__PURE__ */ jsx(
151
+ TableCell,
152
+ {
153
+ align: "right",
154
+ sx: {
155
+ color: side === "bid" ? "success.main" : "error.main",
156
+ fontVariantNumeric: "tabular-nums",
157
+ fontWeight: 600
158
+ },
159
+ children: num(order.price)
160
+ }
161
+ );
162
+ return side === "bid" ? /* @__PURE__ */ jsxs(Fragment, { children: [
163
+ size,
164
+ price
165
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ price,
167
+ size
168
+ ] });
169
+ };
170
+ return /* @__PURE__ */ jsxs(Table, { size: "small", children: [
171
+ /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
172
+ /* @__PURE__ */ jsx(TableCell, { align: "right", children: "Bid size" }),
173
+ /* @__PURE__ */ jsx(TableCell, { align: "right", sx: { color: "success.main", fontWeight: 700 }, children: "Bid" }),
174
+ /* @__PURE__ */ jsx(TableCell, { align: "right", sx: { color: "error.main", fontWeight: 700 }, children: "Ask" }),
175
+ /* @__PURE__ */ jsx(TableCell, { align: "right", children: "Ask size" })
176
+ ] }) }),
177
+ /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rows }, (_unused, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
178
+ cell(bids[i], "bid"),
179
+ cell(asks[i], "ask")
180
+ ] }, i)) })
181
+ ] });
182
+ };
183
+ var ConfigurationSection = ({ vm }) => {
184
+ const applied = useVM(vm, (s) => s.config);
185
+ const [aggPeriod, setAggPeriod] = useState("");
186
+ const [depthLimit, setDepthLimit] = useState("");
187
+ const [orderFields, setOrderFields] = useState("price, size");
188
+ const apply = () => vm.configure({
189
+ acceptAggregationPeriod: aggPeriod.trim() === "" ? void 0 : Number(aggPeriod),
190
+ acceptDepthLimit: depthLimit.trim() === "" ? void 0 : Number(depthLimit),
191
+ // Any field name is allowed through, deliberately. The server decides what it
192
+ // actually returns and may honour none of this — which is the point of asking from a
193
+ // debug console: the negotiated result renders below, next to what was requested.
194
+ acceptOrderFields: orderFields.trim() === "" ? void 0 : parseList(orderFields)
195
+ });
196
+ return /* @__PURE__ */ jsxs(Accordion, { disableGutters: true, variant: "outlined", sx: { "&::before": { display: "none" } }, children: [
197
+ /* @__PURE__ */ jsx(AccordionSummary, { expandIcon: /* @__PURE__ */ jsx(ExpandMoreIcon, {}), children: /* @__PURE__ */ jsx(Typography, { sx: { fontWeight: 600 }, children: "Configuration" }) }),
198
+ /* @__PURE__ */ jsx(AccordionDetails, { children: /* @__PURE__ */ jsxs(Box, { sx: { display: "grid", gap: 2.5, gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" } }, children: [
199
+ /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
200
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Requested" }),
201
+ /* @__PURE__ */ jsxs(
202
+ Box,
203
+ {
204
+ sx: {
205
+ display: "grid",
206
+ gap: 2,
207
+ gridTemplateColumns: { xs: "1fr", sm: "repeat(2, 1fr)" }
208
+ },
209
+ children: [
210
+ /* @__PURE__ */ jsx(
211
+ TextField,
212
+ {
213
+ label: "Aggregation period, s",
214
+ value: aggPeriod,
215
+ onChange: (e) => setAggPeriod(e.target.value),
216
+ type: "number",
217
+ size: "small",
218
+ helperText: "0 = none \xB7 empty = server default"
219
+ }
220
+ ),
221
+ /* @__PURE__ */ jsx(
222
+ TextField,
223
+ {
224
+ label: "Depth limit",
225
+ value: depthLimit,
226
+ onChange: (e) => setDepthLimit(e.target.value),
227
+ type: "number",
228
+ size: "small",
229
+ helperText: "0 = no limit \xB7 empty = server default"
230
+ }
231
+ )
232
+ ]
233
+ }
234
+ ),
235
+ /* @__PURE__ */ jsx(
236
+ TextField,
237
+ {
238
+ label: "Accept order fields (comma-separated)",
239
+ value: orderFields,
240
+ onChange: (e) => setOrderFields(e.target.value),
241
+ size: "small",
242
+ fullWidth: true,
243
+ helperText: "Empty = server default. The server may return fewer fields, or different ones \u2014 compare with Order fields under \u201CApplied by server\u201D."
244
+ }
245
+ ),
246
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Button, { variant: "contained", startIcon: /* @__PURE__ */ jsx(PlayArrowIcon, {}), onClick: apply, children: "Apply configuration" }) })
247
+ ] }),
248
+ /* @__PURE__ */ jsxs(Stack, { spacing: 1.5, children: [
249
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Applied by server" }),
250
+ /* @__PURE__ */ jsxs(Stack, { spacing: 0.75, children: [
251
+ /* @__PURE__ */ jsx(
252
+ DefRow,
253
+ {
254
+ label: "Aggregation period",
255
+ value: applied && !Number.isNaN(applied.aggregationPeriod) ? `${applied.aggregationPeriod} s` : "default (server-defined)"
256
+ }
257
+ ),
258
+ /* @__PURE__ */ jsx(DefRow, { label: "Depth limit", value: applied ? String(applied.depthLimit) : "\u2014" }),
259
+ /* @__PURE__ */ jsx(DefRow, { label: "Data format", value: applied ? applied.dataFormat : "\u2014" })
260
+ ] }),
261
+ /* @__PURE__ */ jsxs(Box, { children: [
262
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: "Order fields" }),
263
+ /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap", mt: 0.5 }, children: (applied?.orderFields ?? []).map((field) => /* @__PURE__ */ jsx(Chip, { size: "small", label: field, variant: "outlined" }, field)) })
264
+ ] })
265
+ ] })
266
+ ] }) })
267
+ ] });
268
+ };
269
+ var DefRow = ({ label, value }) => /* @__PURE__ */ jsxs(
270
+ Stack,
271
+ {
272
+ direction: "row",
273
+ spacing: 1,
274
+ sx: { justifyContent: "space-between", alignItems: "baseline" },
275
+ children: [
276
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: label }),
277
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { fontWeight: 600, textAlign: "right" }, children: value })
278
+ ]
279
+ }
280
+ );
281
+ var DomChannel = ({ title, config }) => {
282
+ const connectionVM = useConnectionVM();
283
+ const [vm] = useState(() => {
284
+ const client = connectionVM.getClient();
285
+ if (client === null) {
286
+ throw new Error("DOM channel opened without an active connection");
287
+ }
288
+ return new DomViewModel(client, {
289
+ symbol: config.symbol,
290
+ sources: parseList(config.source),
291
+ feed: config.feed || void 0,
292
+ space: config.space || void 0
293
+ });
294
+ });
295
+ useEffect(() => {
296
+ vm.start();
297
+ return () => vm.stop();
298
+ }, [vm]);
299
+ const channelState = useVM(vm, (s) => s.channelState);
300
+ const snapshot = useVM(vm, (s) => s.snapshot);
301
+ const channelId = useVM(vm, (s) => s.channelId);
302
+ const channelParameters = useVM(vm, (s) => s.channelParameters);
303
+ const errors = useVM(vm, (s) => s.errors);
304
+ return /* @__PURE__ */ jsx(
305
+ ChannelWidget,
306
+ {
307
+ icon: /* @__PURE__ */ jsx(ViewColumnIcon, {}),
308
+ title,
309
+ subtitle: `DOM \xB7 ${config.symbol || "\u2014"}`,
310
+ onClose: vm.close,
311
+ status: /* @__PURE__ */ jsx(DomStatusChip, { state: channelState }),
312
+ channelId,
313
+ parameters: channelParameters,
314
+ errors,
315
+ onClearErrors: vm.clearErrors,
316
+ children: /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
317
+ /* @__PURE__ */ jsx(ConfigurationSection, { vm }),
318
+ /* @__PURE__ */ jsxs(Box, { children: [
319
+ /* @__PURE__ */ jsxs(
320
+ Stack,
321
+ {
322
+ direction: "row",
323
+ spacing: 1,
324
+ sx: { alignItems: "baseline", justifyContent: "space-between", mb: 1 },
325
+ children: [
326
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Order book" }),
327
+ snapshot && /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "text.secondary", children: [
328
+ "Last update ",
329
+ new Date(snapshot.time).toLocaleTimeString()
330
+ ] })
331
+ ]
332
+ }
333
+ ),
334
+ snapshot ? /* @__PURE__ */ jsx(Ladder, { snapshot }) : /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Waiting for the first snapshot\u2026" })
335
+ ] })
336
+ ] })
337
+ }
338
+ );
339
+ };
340
+
341
+ // src/dom/dom-channel-request.tsx
342
+ import ExpandMoreIcon2 from "@mui/icons-material/ExpandMore";
343
+ import Accordion2 from "@mui/material/Accordion";
344
+ import AccordionDetails2 from "@mui/material/AccordionDetails";
345
+ import AccordionSummary2 from "@mui/material/AccordionSummary";
346
+ import Stack2 from "@mui/material/Stack";
347
+ import TextField2 from "@mui/material/TextField";
348
+ import Typography2 from "@mui/material/Typography";
349
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
350
+ var DomChannelRequest = ({ value, onChange }) => /* @__PURE__ */ jsxs2(Stack2, { spacing: 2.5, sx: { pt: 1 }, children: [
351
+ /* @__PURE__ */ jsx2(
352
+ TextField2,
353
+ {
354
+ label: "Symbol",
355
+ required: true,
356
+ value: value.symbol,
357
+ onChange: (e) => onChange({ ...value, symbol: e.target.value }),
358
+ size: "small",
359
+ fullWidth: true,
360
+ helperText: "Subscription symbol for Depth of Market."
361
+ }
362
+ ),
363
+ /* @__PURE__ */ jsx2(
364
+ TextField2,
365
+ {
366
+ label: "Sources",
367
+ value: value.source,
368
+ onChange: (e) => onChange({ ...value, source: e.target.value }),
369
+ size: "small",
370
+ fullWidth: true,
371
+ helperText: /* @__PURE__ */ jsxs2(Fragment2, { children: [
372
+ "Price level sources, comma/space separated \u2014 ",
373
+ /* @__PURE__ */ jsx2("code", { children: "AGGREGATE" }),
374
+ ", ",
375
+ /* @__PURE__ */ jsx2("code", { children: "REGIONAL" }),
376
+ ", or a venue in lower case such as ",
377
+ /* @__PURE__ */ jsx2("code", { children: "ntv" }),
378
+ " or ",
379
+ /* @__PURE__ */ jsx2("code", { children: "glbx" }),
380
+ ". Full list on",
381
+ " ",
382
+ /* @__PURE__ */ jsx2(DocLink, { href: ORDER_SOURCES_DOC_URL, children: "kb.dxfeed.com" }),
383
+ "."
384
+ ] })
385
+ }
386
+ ),
387
+ /* @__PURE__ */ jsxs2(Accordion2, { disableGutters: true, variant: "outlined", sx: { "&::before": { display: "none" } }, children: [
388
+ /* @__PURE__ */ jsx2(AccordionSummary2, { expandIcon: /* @__PURE__ */ jsx2(ExpandMoreIcon2, {}), children: /* @__PURE__ */ jsx2(Typography2, { sx: { fontWeight: 600 }, children: "Feed" }) }),
389
+ /* @__PURE__ */ jsx2(AccordionDetails2, { children: /* @__PURE__ */ jsxs2(Stack2, { spacing: 2.5, children: [
390
+ /* @__PURE__ */ jsx2(
391
+ TextField2,
392
+ {
393
+ label: "Feed qualification",
394
+ value: value.feed,
395
+ onChange: (e) => onChange({ ...value, feed: e.target.value }),
396
+ size: "small",
397
+ fullWidth: true,
398
+ helperText: "Optional \u2014 leave empty to omit from the request."
399
+ }
400
+ ),
401
+ /* @__PURE__ */ jsx2(
402
+ TextField2,
403
+ {
404
+ label: "Feed space",
405
+ value: value.space,
406
+ onChange: (e) => onChange({ ...value, space: e.target.value }),
407
+ size: "small",
408
+ fullWidth: true,
409
+ helperText: "Optional \u2014 leave empty to omit from the request."
410
+ }
411
+ )
412
+ ] }) })
413
+ ] })
414
+ ] });
415
+
416
+ // src/dom/plugin.tsx
417
+ import { jsx as jsx3 } from "react/jsx-runtime";
418
+ var domChannelPlugin = defineChannelPlugin({
419
+ kind: "dom",
420
+ label: "DOM",
421
+ icon: /* @__PURE__ */ jsx3(ViewColumnIcon2, {}),
422
+ dialogTitle: "New DOM channel",
423
+ createRequest: () => ({
424
+ symbol: "AAPL",
425
+ // AGGREGATE is the price-level source that works without picking a venue.
426
+ source: "AGGREGATE",
427
+ feed: "",
428
+ space: ""
429
+ }),
430
+ RequestForm: DomChannelRequest,
431
+ canOpen: (request) => request.symbol.trim().length > 0,
432
+ buildConfig: (request) => ({
433
+ symbol: request.symbol.trim(),
434
+ source: request.source.trim(),
435
+ feed: request.feed.trim(),
436
+ space: request.space.trim()
437
+ }),
438
+ Channel: DomChannel
439
+ });
440
+
441
+ export {
442
+ domChannelPlugin
443
+ };
444
+ //# sourceMappingURL=chunk-2NKG3ZKV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dom/plugin.tsx","../src/dom/dom-channel.tsx","../src/dom/dom-view-model.ts","../src/dom/dom-channel-request.tsx"],"sourcesContent":["import { defineChannelPlugin } from '@dxfeed/dxlink-console-core'\nimport ViewColumnIcon from '@mui/icons-material/ViewColumn'\n\nimport { DomChannel } from './dom-channel'\nimport { DomChannelRequest } from './dom-channel-request'\nimport type { DomConfig, DomRequest } from './types'\n\n/** The DOM service: a price-level ladder for one symbol. */\nexport const domChannelPlugin = defineChannelPlugin<DomConfig, DomRequest>({\n kind: 'dom',\n label: 'DOM',\n icon: <ViewColumnIcon />,\n dialogTitle: 'New DOM channel',\n createRequest: () => ({\n symbol: 'AAPL',\n // AGGREGATE is the price-level source that works without picking a venue.\n source: 'AGGREGATE',\n feed: '',\n space: '',\n }),\n RequestForm: DomChannelRequest,\n canOpen: (request) => request.symbol.trim().length > 0,\n buildConfig: (request) => ({\n symbol: request.symbol.trim(),\n source: request.source.trim(),\n feed: request.feed.trim(),\n space: request.space.trim(),\n }),\n Channel: DomChannel,\n})\n","import { DXLinkChannelState } from '@dxfeed/dxlink-api'\nimport type { DepthOfMarketOrder } from '@dxfeed/dxlink-api'\nimport { useVM } from '@dxfeed/dxlink-console-core'\nimport { ChannelWidget } from '@dxfeed/dxlink-console-core'\nimport { useConnectionVM } from '@dxfeed/dxlink-console-core'\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport PlayArrowIcon from '@mui/icons-material/PlayArrow'\nimport ViewColumnIcon from '@mui/icons-material/ViewColumn'\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport Box from '@mui/material/Box'\nimport Button from '@mui/material/Button'\nimport Chip from '@mui/material/Chip'\nimport Stack from '@mui/material/Stack'\nimport Table from '@mui/material/Table'\nimport TableBody from '@mui/material/TableBody'\nimport TableCell from '@mui/material/TableCell'\nimport TableHead from '@mui/material/TableHead'\nimport TableRow from '@mui/material/TableRow'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { useEffect, useState } from 'react'\n\nimport { DomViewModel } from './dom-view-model'\nimport type { DomSnapshot } from './dom-view-model'\nimport type { DomConfig } from './types'\n\ninterface DomChannelProps {\n title: string\n config: DomConfig\n}\n\n/** Split a comma- or space-separated list into entries, dropping blanks. */\nconst parseList = (raw: string): string[] =>\n raw\n .split(/[\\s,]+/)\n .map((s) => s.trim())\n .filter(Boolean)\n\nconst DomStatusChip = ({ state }: { state: DXLinkChannelState }) => {\n if (state === DXLinkChannelState.OPENED) {\n return <Chip size=\"small\" color=\"success\" variant=\"outlined\" label=\"streaming\" />\n }\n if (state === DXLinkChannelState.CLOSED) {\n return <Chip size=\"small\" variant=\"outlined\" label=\"closed\" />\n }\n return <Chip size=\"small\" color=\"warning\" variant=\"outlined\" label=\"opening\" />\n}\n\nconst num = (value: number): string =>\n Number.isFinite(value) ? value.toLocaleString(undefined, { maximumFractionDigits: 6 }) : '—'\n\n/** Combined bid/ask ladder: best levels at the top, bids left / asks right. */\nconst Ladder = ({ snapshot }: { snapshot: DomSnapshot }) => {\n const bids = [...snapshot.bids].sort((a, b) => b.price - a.price)\n const asks = [...snapshot.asks].sort((a, b) => a.price - b.price)\n const rows = Math.max(bids.length, asks.length)\n\n if (rows === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Empty book.\n </Typography>\n )\n }\n\n const cell = (order: DepthOfMarketOrder | undefined, side: 'bid' | 'ask') => {\n if (order === undefined) {\n return (\n <>\n <TableCell />\n <TableCell />\n </>\n )\n }\n\n const size = (\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {num(order.size)}\n </TableCell>\n )\n const price = (\n <TableCell\n align=\"right\"\n sx={{\n color: side === 'bid' ? 'success.main' : 'error.main',\n fontVariantNumeric: 'tabular-nums',\n fontWeight: 600,\n }}\n >\n {num(order.price)}\n </TableCell>\n )\n\n // The ladder is mirrored around the spread — sizes outside, prices meeting in the\n // middle — so the two sides emit their cells in opposite order to match the header\n // row: `Bid size | Bid | Ask | Ask size`.\n return side === 'bid' ? (\n <>\n {size}\n {price}\n </>\n ) : (\n <>\n {price}\n {size}\n </>\n )\n }\n\n return (\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell align=\"right\">Bid size</TableCell>\n <TableCell align=\"right\" sx={{ color: 'success.main', fontWeight: 700 }}>\n Bid\n </TableCell>\n <TableCell align=\"right\" sx={{ color: 'error.main', fontWeight: 700 }}>\n Ask\n </TableCell>\n <TableCell align=\"right\">Ask size</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {Array.from({ length: rows }, (_unused, i) => (\n <TableRow key={i}>\n {cell(bids[i], 'bid')}\n {cell(asks[i], 'ask')}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )\n}\n\nconst ConfigurationSection = ({ vm }: { vm: DomViewModel }) => {\n const applied = useVM(vm, (s) => s.config)\n const [aggPeriod, setAggPeriod] = useState('')\n const [depthLimit, setDepthLimit] = useState('')\n // What the server actually returns today, so the request starts from reality. Any other\n // field can be typed in — the server decides, and the negotiated result renders beside it.\n const [orderFields, setOrderFields] = useState('price, size')\n\n const apply = () =>\n vm.configure({\n acceptAggregationPeriod: aggPeriod.trim() === '' ? undefined : Number(aggPeriod),\n acceptDepthLimit: depthLimit.trim() === '' ? undefined : Number(depthLimit),\n // Any field name is allowed through, deliberately. The server decides what it\n // actually returns and may honour none of this — which is the point of asking from a\n // debug console: the negotiated result renders below, next to what was requested.\n acceptOrderFields: orderFields.trim() === '' ? undefined : parseList(orderFields),\n })\n\n return (\n <Accordion disableGutters variant=\"outlined\" sx={{ '&::before': { display: 'none' } }}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <Typography sx={{ fontWeight: 600 }}>Configuration</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <Box sx={{ display: 'grid', gap: 2.5, gridTemplateColumns: { xs: '1fr', md: '1fr 1fr' } }}>\n <Stack spacing={2}>\n <Typography variant=\"subtitle2\">Requested</Typography>\n <Box\n sx={{\n display: 'grid',\n gap: 2,\n gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, 1fr)' },\n }}\n >\n <TextField\n label=\"Aggregation period, s\"\n value={aggPeriod}\n onChange={(e) => setAggPeriod(e.target.value)}\n type=\"number\"\n size=\"small\"\n helperText=\"0 = none · empty = server default\"\n />\n <TextField\n label=\"Depth limit\"\n value={depthLimit}\n onChange={(e) => setDepthLimit(e.target.value)}\n type=\"number\"\n size=\"small\"\n helperText=\"0 = no limit · empty = server default\"\n />\n </Box>\n <TextField\n label=\"Accept order fields (comma-separated)\"\n value={orderFields}\n onChange={(e) => setOrderFields(e.target.value)}\n size=\"small\"\n fullWidth\n helperText=\"Empty = server default. The server may return fewer fields, or different ones — compare with Order fields under “Applied by server”.\"\n />\n <Box>\n <Button variant=\"contained\" startIcon={<PlayArrowIcon />} onClick={apply}>\n Apply configuration\n </Button>\n </Box>\n </Stack>\n\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">Applied by server</Typography>\n <Stack spacing={0.75}>\n <DefRow\n label=\"Aggregation period\"\n value={\n applied && !Number.isNaN(applied.aggregationPeriod)\n ? `${applied.aggregationPeriod} s`\n : 'default (server-defined)'\n }\n />\n <DefRow label=\"Depth limit\" value={applied ? String(applied.depthLimit) : '—'} />\n <DefRow label=\"Data format\" value={applied ? applied.dataFormat : '—'} />\n </Stack>\n <Box>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Order fields\n </Typography>\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{ flexWrap: 'wrap', mt: 0.5 }}>\n {(applied?.orderFields ?? []).map((field) => (\n <Chip key={field} size=\"small\" label={field} variant=\"outlined\" />\n ))}\n </Stack>\n </Box>\n </Stack>\n </Box>\n </AccordionDetails>\n </Accordion>\n )\n}\n\nconst DefRow = ({ label, value }: { label: string; value: React.ReactNode }) => (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ justifyContent: 'space-between', alignItems: 'baseline' }}\n >\n <Typography variant=\"caption\" color=\"text.secondary\">\n {label}\n </Typography>\n <Typography variant=\"caption\" sx={{ fontWeight: 600, textAlign: 'right' }}>\n {value}\n </Typography>\n </Stack>\n)\n\n/** Live Depth-of-Market channel — wraps a real {@link DomViewModel}. */\nexport const DomChannel = ({ title, config }: DomChannelProps) => {\n const connectionVM = useConnectionVM()\n const [vm] = useState(() => {\n const client = connectionVM.getClient()\n if (client === null) {\n throw new Error('DOM channel opened without an active connection')\n }\n return new DomViewModel(client, {\n symbol: config.symbol,\n sources: parseList(config.source),\n feed: config.feed || undefined,\n space: config.space || undefined,\n })\n })\n useEffect(() => {\n vm.start()\n return () => vm.stop()\n }, [vm])\n\n const channelState = useVM(vm, (s) => s.channelState)\n const snapshot = useVM(vm, (s) => s.snapshot)\n const channelId = useVM(vm, (s) => s.channelId)\n const channelParameters = useVM(vm, (s) => s.channelParameters)\n const errors = useVM(vm, (s) => s.errors)\n\n return (\n <ChannelWidget\n icon={<ViewColumnIcon />}\n title={title}\n subtitle={`DOM · ${config.symbol || '—'}`}\n onClose={vm.close}\n status={<DomStatusChip state={channelState} />}\n channelId={channelId}\n parameters={channelParameters}\n errors={errors}\n onClearErrors={vm.clearErrors}\n >\n <Stack spacing={2}>\n <ConfigurationSection vm={vm} />\n\n <Box>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'baseline', justifyContent: 'space-between', mb: 1 }}\n >\n <Typography variant=\"subtitle2\">Order book</Typography>\n {snapshot && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n Last update {new Date(snapshot.time).toLocaleTimeString()}\n </Typography>\n )}\n </Stack>\n {snapshot ? (\n <Ladder snapshot={snapshot} />\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Waiting for the first snapshot…\n </Typography>\n )}\n </Box>\n </Stack>\n </ChannelWidget>\n )\n}\n","import { DXLinkChannelState, DXLinkDepthOfMarket, DXLinkLogLevel } from '@dxfeed/dxlink-api'\nimport type {\n DepthOfMarketAcceptConfig,\n DepthOfMarketConfig,\n DepthOfMarketOrder,\n DXLinkClient,\n} from '@dxfeed/dxlink-api'\nimport { ChannelErrorTracker, initialChannelErrorState } from '@dxfeed/dxlink-console-core'\nimport type { ChannelErrorState } from '@dxfeed/dxlink-console-core'\nimport type { ViewModel } from '@dxfeed/dxlink-console-core'\nimport { createStore } from 'zustand/vanilla'\n\nexport interface DomSnapshot {\n time: number\n bids: DepthOfMarketOrder[]\n asks: DepthOfMarketOrder[]\n}\n\nexport interface DomVMState extends ChannelErrorState {\n channelState: DXLinkChannelState\n config: DepthOfMarketConfig | null\n snapshot: DomSnapshot | null\n}\n\n// Coalesce snapshots to the latest one within this window (~10fps).\nconst FLUSH_INTERVAL_MS = 100\n\n/**\n * ViewModel for one DOM (Depth of Market) channel — wraps {@link DXLinkDepthOfMarket}.\n *\n * Construction is PURE (StrictMode-safe); the channel is opened in {@link start}\n * and released in {@link stop}, driven by the view's `useEffect`. Snapshots are\n * full replacements coalesced to the latest within a frame.\n */\nexport class DomViewModel implements ViewModel<DomVMState> {\n readonly store = createStore<DomVMState>(() => ({\n channelState: DXLinkChannelState.REQUESTED,\n config: null,\n snapshot: null,\n ...initialChannelErrorState(),\n }))\n\n private readonly channelErrors = new ChannelErrorTracker(this.store)\n private readonly client: DXLinkClient\n private readonly params: { symbol: string; sources: string[]; feed?: string; space?: string }\n private dom: DXLinkDepthOfMarket | null = null\n private pendingSnapshot: DomSnapshot | null = null\n private flushHandle: ReturnType<typeof setTimeout> | null = null\n\n constructor(\n client: DXLinkClient,\n params: { symbol: string; sources: string[]; feed?: string; space?: string }\n ) {\n this.client = client\n this.params = params\n }\n\n start = (): void => {\n if (this.dom !== null) return\n const dom = new DXLinkDepthOfMarket(\n this.client,\n {\n symbol: this.params.symbol,\n sources: this.params.sources,\n },\n {\n feed: this.params.feed,\n space: this.params.space,\n // A debug console wants the protocol traffic in the browser log.\n logLevel: DXLinkLogLevel.DEBUG,\n }\n )\n dom.addSnapshotListener(this.handleSnapshot)\n dom.addConfigChangeListener(this.handleConfig)\n dom.addStateChangeListener(this.handleState)\n this.channelErrors.attach(dom.getChannel())\n this.dom = dom\n this.store.setState({ channelState: dom.getState(), config: dom.getConfig() })\n }\n\n stop = (): void => {\n const dom = this.dom\n if (dom === null) return\n this.dom = null\n if (this.flushHandle !== null) {\n clearTimeout(this.flushHandle)\n this.flushHandle = null\n }\n this.pendingSnapshot = null\n dom.removeSnapshotListener(this.handleSnapshot)\n dom.removeConfigChangeListener(this.handleConfig)\n dom.removeStateChangeListener(this.handleState)\n this.channelErrors.detach(dom.getChannel())\n dom.close()\n }\n\n configure = (accept: DepthOfMarketAcceptConfig): void => {\n this.dom?.configure(accept)\n }\n\n clearErrors = (): void => this.channelErrors.clear()\n\n close = (): void => {\n this.stop()\n }\n\n dispose = (): void => {\n this.stop()\n }\n\n private handleSnapshot = (\n time: number,\n bids: DepthOfMarketOrder[],\n asks: DepthOfMarketOrder[]\n ): void => {\n if (this.dom === null) return\n this.pendingSnapshot = { time, bids, asks }\n if (this.flushHandle === null) {\n this.flushHandle = setTimeout(this.flush, FLUSH_INTERVAL_MS)\n }\n }\n\n private flush = (): void => {\n this.flushHandle = null\n if (this.dom === null || this.pendingSnapshot === null) return\n this.store.setState({ snapshot: this.pendingSnapshot })\n this.pendingSnapshot = null\n }\n\n private handleConfig = (config: DepthOfMarketConfig): void => {\n this.store.setState({ config })\n }\n\n private handleState = (state: DXLinkChannelState): void => {\n this.store.setState({ channelState: state })\n }\n}\n","import ExpandMoreIcon from '@mui/icons-material/ExpandMore'\nimport Accordion from '@mui/material/Accordion'\nimport AccordionDetails from '@mui/material/AccordionDetails'\nimport AccordionSummary from '@mui/material/AccordionSummary'\nimport Stack from '@mui/material/Stack'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\n\nimport type { DomRequest } from './types'\nimport { DocLink } from '../components/doc-link'\nimport { ORDER_SOURCES_DOC_URL } from '../lib/order-sources'\n\ninterface DomChannelRequestProps {\n value: DomRequest\n onChange: (value: DomRequest) => void\n}\n\n/** DOM channel request form (draft / presentational only). */\nexport const DomChannelRequest = ({ value, onChange }: DomChannelRequestProps) => (\n <Stack spacing={2.5} sx={{ pt: 1 }}>\n <TextField\n label=\"Symbol\"\n required\n value={value.symbol}\n onChange={(e) => onChange({ ...value, symbol: e.target.value })}\n size=\"small\"\n fullWidth\n helperText=\"Subscription symbol for Depth of Market.\"\n />\n <TextField\n label=\"Sources\"\n value={value.source}\n onChange={(e) => onChange({ ...value, source: e.target.value })}\n size=\"small\"\n fullWidth\n helperText={\n <>\n Price level sources, comma/space separated — <code>AGGREGATE</code>, <code>REGIONAL</code>\n , or a venue in lower case such as <code>ntv</code> or <code>glbx</code>. Full list on{' '}\n <DocLink href={ORDER_SOURCES_DOC_URL}>kb.dxfeed.com</DocLink>.\n </>\n }\n />\n <Accordion disableGutters variant=\"outlined\" sx={{ '&::before': { display: 'none' } }}>\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n <Typography sx={{ fontWeight: 600 }}>Feed</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <Stack spacing={2.5}>\n <TextField\n label=\"Feed qualification\"\n value={value.feed}\n onChange={(e) => onChange({ ...value, feed: e.target.value })}\n size=\"small\"\n fullWidth\n helperText=\"Optional — leave empty to omit from the request.\"\n />\n <TextField\n label=\"Feed space\"\n value={value.space}\n onChange={(e) => onChange({ ...value, space: e.target.value })}\n size=\"small\"\n fullWidth\n helperText=\"Optional — leave empty to omit from the request.\"\n />\n </Stack>\n </AccordionDetails>\n </Accordion>\n </Stack>\n)\n"],"mappings":"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","names":["ViewColumnIcon","DXLinkChannelState","DXLinkChannelState","ExpandMoreIcon","Accordion","AccordionDetails","AccordionSummary","Stack","TextField","Typography","Fragment","jsx","jsxs","Stack","TextField","Accordion","AccordionSummary","ExpandMoreIcon","Typography","AccordionDetails","jsx","ViewColumnIcon"]}