@stocksharp/trading-controls 1.3.0 → 1.4.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.
- package/README.md +113 -5
- package/dist/esm/chart-engine.js +15 -0
- package/dist/esm/chart-engine.js.map +1 -0
- package/dist/esm/control-types.js +4 -0
- package/dist/esm/control-types.js.map +1 -1
- package/dist/esm/equity-widget.js +261 -0
- package/dist/esm/equity-widget.js.map +1 -0
- package/dist/esm/heatmap-grid.js +271 -0
- package/dist/esm/heatmap-grid.js.map +1 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/optimization-heatmap-widget.js +303 -0
- package/dist/esm/optimization-heatmap-widget.js.map +1 -0
- package/dist/esm/option-smile-widget.js +252 -0
- package/dist/esm/option-smile-widget.js.map +1 -0
- package/dist/esm/pnl-curve.js +5 -1
- package/dist/esm/pnl-curve.js.map +1 -1
- package/dist/esm/surface-grid.js +282 -0
- package/dist/esm/surface-grid.js.map +1 -0
- package/dist/esm/surface-widget.js +379 -0
- package/dist/esm/surface-widget.js.map +1 -0
- package/dist/sstradingcontrols.js +1595 -168
- package/dist/sstradingcontrols.js.map +4 -4
- package/dist/types/chart-engine.d.ts +3 -0
- package/dist/types/chart-engine.d.ts.map +1 -0
- package/dist/types/control-types.d.ts +4 -0
- package/dist/types/control-types.d.ts.map +1 -1
- package/dist/types/equity-widget.d.ts +34 -0
- package/dist/types/equity-widget.d.ts.map +1 -0
- package/dist/types/heatmap-grid.d.ts +88 -0
- package/dist/types/heatmap-grid.d.ts.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/optimization-heatmap-widget.d.ts +45 -0
- package/dist/types/optimization-heatmap-widget.d.ts.map +1 -0
- package/dist/types/option-smile-widget.d.ts +39 -0
- package/dist/types/option-smile-widget.d.ts.map +1 -0
- package/dist/types/pnl-curve.d.ts +3 -0
- package/dist/types/pnl-curve.d.ts.map +1 -1
- package/dist/types/surface-grid.d.ts +64 -0
- package/dist/types/surface-grid.d.ts.map +1 -0
- package/dist/types/surface-widget.d.ts +64 -0
- package/dist/types/surface-widget.d.ts.map +1 -0
- package/package.json +28 -2
- package/screenshots/equity.png +0 -0
- package/screenshots/optimization-surface.png +0 -0
- package/screenshots/optimization.png +0 -0
- package/screenshots/option-smile.png +0 -0
- package/screenshots/panels.jpg +0 -0
- package/src/chart-engine.global.ts +64 -0
- package/src/chart-engine.ts +29 -0
- package/src/control-types.ts +4 -0
- package/src/equity-widget.ts +289 -0
- package/src/heatmap-grid.ts +405 -0
- package/src/index.ts +46 -0
- package/src/optimization-heatmap-widget.ts +347 -0
- package/src/option-smile-widget.ts +281 -0
- package/src/pnl-curve.ts +13 -1
- package/src/surface-grid.ts +410 -0
- package/src/surface-widget.ts +457 -0
- package/styles/trading-controls.css +281 -0
- package/translation-keys.json +14 -1
package/README.md
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
7
|
**StockSharp JS Trading Controls** are the browser panels a trading screen is made
|
|
8
|
-
of.
|
|
8
|
+
of. Fifteen of them: the **active orders**, **positions** and **trade history**
|
|
9
9
|
blotters, a **watchlist** with live quotes and category tabs, an **order entry**
|
|
10
10
|
pad, a **trade feed**, an **order book** ladder, a **statistics** table, a
|
|
11
|
-
**strategies** dashboard, a **log monitor**,
|
|
11
|
+
**strategies** dashboard, a **log monitor**, an **option desk** and its
|
|
12
|
+
**smile**, an **equity** curve, and an optimisation **heatmap** with the same
|
|
13
|
+
sweep as a turnable 3D **surface**.
|
|
12
14
|
|
|
13
15
|
Each control builds its own DOM, renders its own table through
|
|
14
16
|
[`@stocksharp/grids`](https://www.npmjs.com/package/@stocksharp/grids), and reaches
|
|
@@ -19,7 +21,7 @@ the outside world through exactly one object — a `TradingHost`.
|
|
|
19
21
|
[GitHub repository](https://github.com/StockSharp/JS-TradingControls) ·
|
|
20
22
|
[Issue tracker](https://github.com/StockSharp/JS-TradingControls/issues)
|
|
21
23
|
|
|
22
|
-

|
|
24
|
+

|
|
23
25
|
|
|
24
26
|
The page above is `demo/` — the published bundle over a demo `TradingHost`, no
|
|
25
27
|
server and no network, laid out by the same dockview-core the StockSharp web
|
|
@@ -28,12 +30,28 @@ candlestick panel fed by the same simulated prices. The **Host port traffic**
|
|
|
28
30
|
tab records every call the controls made into that host, which is the whole of
|
|
29
31
|
what they can reach.
|
|
30
32
|
|
|
33
|
+
It is three boards over one set of data, because these controls do not all belong
|
|
34
|
+
on one screen. `index.html` is the trading screen: chart, ladder, tape, watchlist,
|
|
35
|
+
order pad, blotters and the option desk. `strategies.html` is a running strategy —
|
|
36
|
+
the dashboard, its equity, its statistics and what it said. `optimization.html` is
|
|
37
|
+
one parameter sweep read two ways, as a map of pairs and as the landscape they
|
|
38
|
+
make. The panels, the host and the tape are shared; a board decides only which
|
|
39
|
+
panels are on it and where.
|
|
40
|
+
|
|
31
41
|
## Quick start
|
|
32
42
|
|
|
33
43
|
```sh
|
|
34
44
|
npm install @stocksharp/trading-controls
|
|
35
45
|
```
|
|
36
46
|
|
|
47
|
+
`@stocksharp/chart` is a peer dependency, needed by two panels: the equity curve
|
|
48
|
+
and the option smile are charts, and they are built on the engine rather than on
|
|
49
|
+
canvases of their own. Install it beside this package if you use either:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npm install @stocksharp/chart
|
|
53
|
+
```
|
|
54
|
+
|
|
37
55
|
```ts
|
|
38
56
|
import { PositionsWidget } from '@stocksharp/trading-controls';
|
|
39
57
|
import '@stocksharp/trading-controls/styles.css';
|
|
@@ -61,6 +79,17 @@ The package also ships a ready-to-use browser bundle exposed as
|
|
|
61
79
|
</script>
|
|
62
80
|
```
|
|
63
81
|
|
|
82
|
+
The bundle does not carry the chart engine — a page that loads both would
|
|
83
|
+
otherwise hold two copies of it, and two registries of series definitions that do
|
|
84
|
+
not recognise each other's. `EquityWidget` and `OptionSmileWidget` read it off the
|
|
85
|
+
`SSChart` global instead, so load the chart's own bundle (and the indicators
|
|
86
|
+
bundle it needs) alongside:
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<script src="https://cdn.jsdelivr.net/npm/@stocksharp/indicators/dist/ssindicators.js"></script>
|
|
90
|
+
<script src="https://cdn.jsdelivr.net/npm/@stocksharp/chart/dist/sschart.js"></script>
|
|
91
|
+
```
|
|
92
|
+
|
|
64
93
|
## The host port is the whole API surface
|
|
65
94
|
|
|
66
95
|
A control imports no translator, no settings singleton, no panel registry and no
|
|
@@ -125,7 +154,7 @@ Two pairs are deliberately separate rather than merged:
|
|
|
125
154
|
reads the port names (`getExecutions`, `searchInstruments`), which the host
|
|
126
155
|
implements.
|
|
127
156
|
|
|
128
|
-
### The
|
|
157
|
+
### The 235 keys a host has to answer
|
|
129
158
|
|
|
130
159
|
`t()` cannot fail. A key the host does not know is rendered to the user as
|
|
131
160
|
itself, so `NoActiveOrders` appears in the empty blotter and `ClosePanel`
|
|
@@ -134,7 +163,7 @@ error. The complete list ships with the package:
|
|
|
134
163
|
|
|
135
164
|
```ts
|
|
136
165
|
import keys from '@stocksharp/trading-controls/translation-keys.json';
|
|
137
|
-
// { count:
|
|
166
|
+
// { count: 235, keys: ['Actions', 'ActiveOrders', …] }
|
|
138
167
|
```
|
|
139
168
|
|
|
140
169
|
It is **generated from the sources** (`npm run i18n:update`) and re-checked by
|
|
@@ -383,6 +412,85 @@ strike as nothing.
|
|
|
383
412
|
|
|
384
413
|

|
|
385
414
|
|
|
415
|
+
### `OptionSmileWidget`
|
|
416
|
+
|
|
417
|
+
The same chain the desk tabulates, drawn as the shape a trader is actually looking
|
|
418
|
+
for: implied volatility against strike, one curve per side, both on one scale
|
|
419
|
+
because the distance between them is the skew. It takes the desk's own
|
|
420
|
+
`OptionStrike[]` and `OptionChainContext`, so a host feeding one feeds the other
|
|
421
|
+
with no conversion.
|
|
422
|
+
|
|
423
|
+
A strike quoted on one side and not the other leaves a gap rather than a straight
|
|
424
|
+
line through it - a curve drawn across missing data invents a quote that nobody
|
|
425
|
+
made. The underlying's price is marked, since where the money sits is what makes
|
|
426
|
+
a smile a smile rather than a squiggle.
|
|
427
|
+
|
|
428
|
+
Drawn by `@stocksharp/chart`, which is a time-series engine and this axis is a
|
|
429
|
+
ladder of strikes — one `timeScale.formatter` is all that stands between the two,
|
|
430
|
+
and the axis carries the strike itself, so the label and the value cannot drift
|
|
431
|
+
apart. Spacing is ordinal, because a listed chain is evenly spaced by listing: a
|
|
432
|
+
venue that lists 67000, 67250 and then 68000 means three rungs, not a hole. The
|
|
433
|
+
crosshair, the wheel zoom, the drag and the tick steps come with the engine; the
|
|
434
|
+
readout above the chart is worded here, naming the strike and both sides at it,
|
|
435
|
+
because a smile is read by the distance between the two curves.
|
|
436
|
+
|
|
437
|
+

|
|
438
|
+
|
|
439
|
+
### `EquityWidget`
|
|
440
|
+
|
|
441
|
+
A run's cumulative P&L at the size of a chart. It is the same curve
|
|
442
|
+
`StrategiesWidget` draws in a column and the same arithmetic behind it - the
|
|
443
|
+
package computes that curve once, in `pnl-curve.ts`, and a host that wants an
|
|
444
|
+
equity panel gets it rather than reimplementing the layout for the third time.
|
|
445
|
+
|
|
446
|
+
What the panel affords that a sparkline cannot: a crosshair that names the moment
|
|
447
|
+
and the figure under the pointer, a wheel that zooms about it, a drag that pans,
|
|
448
|
+
and an axis that picks its own step. All of that is `@stocksharp/chart`'s, which
|
|
449
|
+
is why the panel is built on it; what stays here is the part that is this
|
|
450
|
+
package's — turning a run into a series, wording the moment through
|
|
451
|
+
`presentation.timeText`, and taking the curve's colours from
|
|
452
|
+
`presentation.canvasPalette()` so a panel matches the sparkline in the table
|
|
453
|
+
beside it. The colour follows where the run *ended*: a run that peaked and gave it
|
|
454
|
+
all back is a loss, and the fill says so.
|
|
455
|
+
|
|
456
|
+
The sparkline is not this. A cell forty pixels tall gets a canvas — a chart engine
|
|
457
|
+
per table row is absurd — so the two draw the same run with different machinery at
|
|
458
|
+
sizes that want different things.
|
|
459
|
+
|
|
460
|
+

|
|
461
|
+
|
|
462
|
+
### `OptimizationHeatmapWidget`
|
|
463
|
+
|
|
464
|
+
One metric over two parameters. It knows nothing about optimisation - a grid of
|
|
465
|
+
`{x, y, value}` is the same object whatever produced it - so a backtest sweep and
|
|
466
|
+
anything else that varies two things share a control.
|
|
467
|
+
|
|
468
|
+
`betterWhen` is required rather than assumed: without it the same map of drawdowns
|
|
469
|
+
would paint its worst corner in the winning colour. A pair with no run leaves a
|
|
470
|
+
gap, drawn as one, because an absent result and a zero result are different
|
|
471
|
+
things. Two runs at one pair are two samples of one cell, so the cell is their
|
|
472
|
+
mean and says how many it is the mean of.
|
|
473
|
+
|
|
474
|
+

|
|
475
|
+
|
|
476
|
+
### `SurfaceWidget`
|
|
477
|
+
|
|
478
|
+
The same sweep as a landscape: the metric as height as well as colour, so the
|
|
479
|
+
shape a set of parameters makes is read directly instead of being inferred from a
|
|
480
|
+
grid of tints. It takes the heatmap's own input, so one set of results feeds both
|
|
481
|
+
panels and a consumer offers either.
|
|
482
|
+
|
|
483
|
+
It is turned, tipped and zoomed by hand, and there is no library under it. The
|
|
484
|
+
projection is six multiplications and the ordering is a sort by depth, both in
|
|
485
|
+
`surface-grid.ts` as arithmetic that is checked against numbers rather than
|
|
486
|
+
against pixels. The gestures are pointer events, so a mouse, a pen and a thumb
|
|
487
|
+
take the same path: one pointer turns, two pinch to zoom, a wheel zooms.
|
|
488
|
+
Orthographic rather than perspective, because a surface is read by comparing
|
|
489
|
+
heights across it and perspective makes the far side of a ridge shorter than the
|
|
490
|
+
near side of the same ridge.
|
|
491
|
+
|
|
492
|
+

|
|
493
|
+
|
|
386
494
|
## Exported sheets say what the screen says
|
|
387
495
|
|
|
388
496
|
Every blotter exports to `.xlsx` through the grid, and a column states both
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// The chart engine, in one place.
|
|
2
|
+
//
|
|
3
|
+
// Two panels here are charts rather than drawings - an equity curve over time and a smile over
|
|
4
|
+
// strike - and @stocksharp/chart already does what a chart does: a crosshair that names the
|
|
5
|
+
// point under the pointer, a wheel that zooms about it, a drag that pans, tick labels that pick
|
|
6
|
+
// their own step. Re-implementing that was two engines and a poorer result, so it is a peer
|
|
7
|
+
// dependency now and the panels are built on it.
|
|
8
|
+
//
|
|
9
|
+
// Every import of it goes through this module, for one reason: the IIFE bundle must NOT inline
|
|
10
|
+
// the engine. A page that loads `sschart.js` and `sstradingcontrols.js` would otherwise hold two
|
|
11
|
+
// copies of it - a megabyte twice over, and two registries of series definitions that do not
|
|
12
|
+
// recognise each other's. `build.mjs` points this specifier at `chart-engine.global.ts` for that
|
|
13
|
+
// build, which reads the engine off the global the chart's own bundle publishes.
|
|
14
|
+
export { AreaSeries, CrosshairMode, LineSeries, createChart, } from '@stocksharp/chart';
|
|
15
|
+
//# sourceMappingURL=chart-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-engine.js","sourceRoot":"","sources":["../../src/chart-engine.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+FAA+F;AAC/F,4FAA4F;AAC5F,gGAAgG;AAChG,4FAA4F;AAC5F,iDAAiD;AACjD,EAAE;AACF,+FAA+F;AAC/F,iGAAiG;AACjG,6FAA6F;AAC7F,iGAAiG;AACjG,iFAAiF;AACjF,OAAO,EACH,UAAU,EACV,aAAa,EACb,UAAU,EACV,WAAW,GACd,MAAM,mBAAmB,CAAC"}
|
|
@@ -26,5 +26,9 @@ export const ControlTypes = {
|
|
|
26
26
|
LogMonitor: 'logMonitor',
|
|
27
27
|
Strategies: 'strategies',
|
|
28
28
|
OptionDesk: 'optionDesk',
|
|
29
|
+
OptionSmile: 'optionSmile',
|
|
30
|
+
Equity: 'equity',
|
|
31
|
+
OptimizationHeatmap: 'optimizationHeatmap',
|
|
32
|
+
OptimizationSurface: 'optimizationSurface',
|
|
29
33
|
};
|
|
30
34
|
//# sourceMappingURL=control-types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-types.js","sourceRoot":"","sources":["../../src/control-types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iBAAiB;AACjB,EAAE;AACF,gFAAgF;AAChF,yEAAyE;AACzE,4EAA4E;AAC5E,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,0EAA0E;AAC1E,2BAA2B;AAC3B,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,SAAS;AACT,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,SAAS,EAAE,WAAW;IACtB,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"control-types.js","sourceRoot":"","sources":["../../src/control-types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iBAAiB;AACjB,EAAE;AACF,gFAAgF;AAChF,yEAAyE;AACzE,4EAA4E;AAC5E,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,0EAA0E;AAC1E,2BAA2B;AAC3B,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,SAAS;AACT,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,SAAS,EAAE,WAAW;IACtB,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,aAAa;IAC1B,MAAM,EAAE,QAAQ;IAChB,mBAAmB,EAAE,qBAAqB;IAC1C,mBAAmB,EAAE,qBAAqB;CACpC,CAAC"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Equity curve — multi-instance.
|
|
2
|
+
//
|
|
3
|
+
// A run's cumulative P&L at the size of a chart, drawn by @stocksharp/chart. It used to be a
|
|
4
|
+
// canvas of its own with a crosshair, a legend and a zoom written here; all three are the
|
|
5
|
+
// engine's, better than the versions this file had, and an equity curve over time is exactly
|
|
6
|
+
// what a time-series engine is for.
|
|
7
|
+
//
|
|
8
|
+
// What is left here is the part that is this package's: turning a run into a series, wording the
|
|
9
|
+
// readout through the host, and taking the engine's colours from `presentation.canvasPalette()`
|
|
10
|
+
// so a curve in a panel matches the sparkline in a table cell beside it.
|
|
11
|
+
//
|
|
12
|
+
// The sparkline is NOT this. A cell forty pixels tall gets `pnl-curve.ts` and a canvas, because
|
|
13
|
+
// a chart engine per table row is absurd - the two draw the same run at sizes that want
|
|
14
|
+
// different machinery.
|
|
15
|
+
import { formatPnl } from './formatters.js';
|
|
16
|
+
import { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';
|
|
17
|
+
import { ControlTypes } from './control-types.js';
|
|
18
|
+
import { assertHost } from './trading-host.js';
|
|
19
|
+
import { AreaSeries, CrosshairMode, createChart, } from './chart-engine.js';
|
|
20
|
+
// How solid the area under the curve is drawn. Not a colour - the two colours are the host's -
|
|
21
|
+
// but how far they are faded, which is this chart's own business.
|
|
22
|
+
const FILL_TOP_ALPHA = 0.28;
|
|
23
|
+
const FILL_BOTTOM_ALPHA = 0.02;
|
|
24
|
+
const LINE_WIDTH = 2;
|
|
25
|
+
/// One sample as the engine takes it. Time is unix milliseconds on the way in and seconds on the
|
|
26
|
+
/// way through: the engine counts in seconds, and a host that hands over milliseconds - which is
|
|
27
|
+
/// what `Date.now()` and `presentation.timeText` both deal in - should not have to know that.
|
|
28
|
+
function toSeries(points) {
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const point of [...points].sort((a, b) => a.time - b.time)) {
|
|
32
|
+
if (!Number.isFinite(point.time) || !Number.isFinite(point.value))
|
|
33
|
+
continue;
|
|
34
|
+
// The engine holds one value per moment. Two fills in the same second are two samples of
|
|
35
|
+
// one instant, and the later one is where the run actually stood.
|
|
36
|
+
const second = Math.floor(point.time / 1000);
|
|
37
|
+
if (seen.has(second))
|
|
38
|
+
out[out.length - 1] = { time: second, value: point.value };
|
|
39
|
+
else
|
|
40
|
+
out.push({ time: second, value: point.value });
|
|
41
|
+
seen.add(second);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
export class EquityWidget {
|
|
46
|
+
static create(hostEl, state, deps) {
|
|
47
|
+
const host = assertHost(deps?.host, 'EquityWidget');
|
|
48
|
+
const root = EquityWidget._buildRoot(host);
|
|
49
|
+
root.id = makePanelId(EquityWidget.TYPE);
|
|
50
|
+
hostEl.appendChild(root);
|
|
51
|
+
return new EquityWidget(root, state || {}, deps);
|
|
52
|
+
}
|
|
53
|
+
static _buildRoot(host) {
|
|
54
|
+
const title = host.t('Equity');
|
|
55
|
+
return makePanelRoot('equity-panel', title, [
|
|
56
|
+
makeElement('div', 'panel-header', {}, [
|
|
57
|
+
makeElement('span', '', {}, [title]),
|
|
58
|
+
makeElement('span', 'equity-last', {}, []),
|
|
59
|
+
makeIconButton('bt-icon-btn equity-reset-btn', host.t('ResetView'), 'bi-arrow-clockwise', { type: 'button' }),
|
|
60
|
+
makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),
|
|
61
|
+
]),
|
|
62
|
+
makeElement('div', 'panel-body', {}, [
|
|
63
|
+
makeElement('div', 'equity-chart', { role: 'img', 'aria-label': host.t('PnLChart') }, [
|
|
64
|
+
// Over the curve rather than in the panel header, for the same reason the
|
|
65
|
+
// smile's is: a reading about a moment belongs beside that moment, and a host
|
|
66
|
+
// that lifts panel headers into a tab strip would put it in a tab title.
|
|
67
|
+
makeElement('div', 'equity-hover', {}, []),
|
|
68
|
+
makeElement('div', 'equity-empty', {}, [host.t('NoEquity')]),
|
|
69
|
+
]),
|
|
70
|
+
]),
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
constructor(rootEl, _state, deps) {
|
|
74
|
+
this._host = assertHost(deps?.host, 'EquityWidget');
|
|
75
|
+
this.rootEl = rootEl;
|
|
76
|
+
this._closeBtn = this.rootEl.querySelector('.panel-close-btn');
|
|
77
|
+
this._resetBtn = this.rootEl.querySelector('.equity-reset-btn');
|
|
78
|
+
this._lastEl = this.rootEl.querySelector('.equity-last');
|
|
79
|
+
this._hoverEl = this.rootEl.querySelector('.equity-hover');
|
|
80
|
+
this._emptyEl = this.rootEl.querySelector('.equity-empty');
|
|
81
|
+
this._chartEl = this.rootEl.querySelector('.equity-chart');
|
|
82
|
+
this._chart = null;
|
|
83
|
+
this._series = null;
|
|
84
|
+
this._points = [];
|
|
85
|
+
this._resizeObserver = null;
|
|
86
|
+
this._closeBtn?.addEventListener('click', (e) => { e.preventDefault(); this._host.close(); });
|
|
87
|
+
this._resetBtn?.addEventListener('click', (e) => { e.preventDefault(); this.resetZoom(); });
|
|
88
|
+
// The panel has no size until it is laid out, and a docked one is resized afterwards.
|
|
89
|
+
// The engine sizes its canvases only through resize(), so it is told rather than left to
|
|
90
|
+
// notice.
|
|
91
|
+
if (this._chartEl !== null && typeof ResizeObserver !== 'undefined') {
|
|
92
|
+
this._resizeObserver = new ResizeObserver(() => this._fit());
|
|
93
|
+
this._resizeObserver.observe(this._chartEl);
|
|
94
|
+
}
|
|
95
|
+
this._render();
|
|
96
|
+
this._host.register(this);
|
|
97
|
+
}
|
|
98
|
+
dispose() {
|
|
99
|
+
try {
|
|
100
|
+
this._resizeObserver?.disconnect();
|
|
101
|
+
}
|
|
102
|
+
catch { /* already torn down */ }
|
|
103
|
+
this._resizeObserver = null;
|
|
104
|
+
try {
|
|
105
|
+
this._chart?.remove();
|
|
106
|
+
}
|
|
107
|
+
catch { /* already gone */ }
|
|
108
|
+
this._chart = null;
|
|
109
|
+
this._series = null;
|
|
110
|
+
this._host.unregister(this);
|
|
111
|
+
try {
|
|
112
|
+
this.rootEl.remove();
|
|
113
|
+
}
|
|
114
|
+
catch { /* already detached */ }
|
|
115
|
+
}
|
|
116
|
+
/// Show this run. The whole curve at once: it is a cumulative figure, so a set of points that
|
|
117
|
+
/// no longer contains an earlier sample is a different run rather than a longer one.
|
|
118
|
+
///
|
|
119
|
+
/// `time` is unix milliseconds.
|
|
120
|
+
update(points) {
|
|
121
|
+
this._points = points || [];
|
|
122
|
+
this._render();
|
|
123
|
+
}
|
|
124
|
+
/// Show the whole run again, after a zoom.
|
|
125
|
+
resetZoom() {
|
|
126
|
+
try {
|
|
127
|
+
this._chart?.timeScale().fitContent();
|
|
128
|
+
}
|
|
129
|
+
catch { /* no chart yet */ }
|
|
130
|
+
}
|
|
131
|
+
/// The engine, for a host that wants to add to this chart - a benchmark line, a marker at a
|
|
132
|
+
/// drawdown. Null until there is something to draw.
|
|
133
|
+
chart() {
|
|
134
|
+
return this._chart;
|
|
135
|
+
}
|
|
136
|
+
// Built on first use rather than in the constructor: the engine measures its container, and
|
|
137
|
+
// a panel that has not been laid out yet has none.
|
|
138
|
+
_ensureChart() {
|
|
139
|
+
if (this._chart !== null)
|
|
140
|
+
return true;
|
|
141
|
+
if (this._chartEl === null)
|
|
142
|
+
return false;
|
|
143
|
+
const palette = this._host.presentation.canvasPalette();
|
|
144
|
+
this._chart = createChart(this._chartEl, {
|
|
145
|
+
layout: {
|
|
146
|
+
background: { type: 'solid', color: 'transparent' },
|
|
147
|
+
textColor: palette.grid,
|
|
148
|
+
fontFamily: palette.font,
|
|
149
|
+
fontSize: 11,
|
|
150
|
+
attributionLogo: false,
|
|
151
|
+
},
|
|
152
|
+
grid: { vertLines: { color: palette.grid }, horzLines: { color: palette.grid } },
|
|
153
|
+
crosshair: { mode: CrosshairMode.Normal },
|
|
154
|
+
rightPriceScale: { borderColor: palette.grid },
|
|
155
|
+
timeScale: {
|
|
156
|
+
borderColor: palette.grid,
|
|
157
|
+
timeVisible: true,
|
|
158
|
+
secondsVisible: true,
|
|
159
|
+
// The engine dates an axis in UTC unless told otherwise, and everything else on
|
|
160
|
+
// the page - the readout beside this one included - reads in the browser's zone.
|
|
161
|
+
// Two labels for one instant, three hours apart, is worse than either.
|
|
162
|
+
timeZone: browserTimeZone(),
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
this._series = this._chart.addSeries(AreaSeries, {
|
|
166
|
+
lineWidth: LINE_WIDTH,
|
|
167
|
+
lineColor: palette.up,
|
|
168
|
+
topColor: withAlpha(palette.up, FILL_TOP_ALPHA),
|
|
169
|
+
bottomColor: withAlpha(palette.up, FILL_BOTTOM_ALPHA),
|
|
170
|
+
});
|
|
171
|
+
// The readout. The engine says which point the pointer is over; wording it is the host's,
|
|
172
|
+
// which is why this is here rather than in the engine's own crosshair label.
|
|
173
|
+
this._chart.subscribeCrosshairMove((param) => this._renderHover(param));
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
_render() {
|
|
177
|
+
const series = toSeries(this._points);
|
|
178
|
+
const empty = series.length < 2;
|
|
179
|
+
if (this._emptyEl !== null)
|
|
180
|
+
this._emptyEl.hidden = !empty;
|
|
181
|
+
this._renderLast(empty ? null : series[series.length - 1].value);
|
|
182
|
+
if (empty)
|
|
183
|
+
return;
|
|
184
|
+
if (!this._ensureChart() || this._series === null)
|
|
185
|
+
return;
|
|
186
|
+
// Coloured by where the run ended, the way the sparkline is: a run that peaked and gave
|
|
187
|
+
// it all back is a loss, and the colour says so.
|
|
188
|
+
const palette = this._host.presentation.canvasPalette();
|
|
189
|
+
const last = series[series.length - 1].value;
|
|
190
|
+
const colour = last >= 0 ? palette.up : palette.down;
|
|
191
|
+
this._series.applyOptions({
|
|
192
|
+
lineColor: colour,
|
|
193
|
+
topColor: withAlpha(colour, FILL_TOP_ALPHA),
|
|
194
|
+
bottomColor: withAlpha(colour, FILL_BOTTOM_ALPHA),
|
|
195
|
+
});
|
|
196
|
+
this._series.setData(series);
|
|
197
|
+
this._fit();
|
|
198
|
+
}
|
|
199
|
+
_fit() {
|
|
200
|
+
if (this._chart === null || this._chartEl === null)
|
|
201
|
+
return;
|
|
202
|
+
const rect = this._chartEl.getBoundingClientRect();
|
|
203
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
204
|
+
return;
|
|
205
|
+
try {
|
|
206
|
+
this._chart.resize(rect.width, rect.height);
|
|
207
|
+
}
|
|
208
|
+
catch { /* the engine is between frames */ }
|
|
209
|
+
}
|
|
210
|
+
// Where the run stands, in the header.
|
|
211
|
+
_renderLast(value) {
|
|
212
|
+
const el = this._lastEl;
|
|
213
|
+
if (el === null)
|
|
214
|
+
return;
|
|
215
|
+
el.textContent = value === null ? '' : formatPnl(value);
|
|
216
|
+
el.className = value === null
|
|
217
|
+
? 'equity-last'
|
|
218
|
+
: `equity-last ${this._host.presentation.pnlClass(value)}`;
|
|
219
|
+
}
|
|
220
|
+
// What the pointer is over. The engine hands over the moment and the value at it; the moment
|
|
221
|
+
// is worded by the host, because what a time reads as is the page's business and not this
|
|
222
|
+
// package's.
|
|
223
|
+
_renderHover(param) {
|
|
224
|
+
const el = this._hoverEl;
|
|
225
|
+
if (el === null)
|
|
226
|
+
return;
|
|
227
|
+
const point = this._series === null ? undefined : param.seriesData.get(this._series);
|
|
228
|
+
const value = point?.value;
|
|
229
|
+
// Off the series entirely: the engine reports a crosshair with no time when the pointer
|
|
230
|
+
// has left the plot, and a stale readout is worse than none.
|
|
231
|
+
if (param.time === null || value === undefined || !Number.isFinite(value)) {
|
|
232
|
+
el.textContent = '';
|
|
233
|
+
el.className = 'equity-hover';
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// Back to milliseconds for the host: the engine counts in seconds, and every other moment
|
|
237
|
+
// this package hands over is a Date.
|
|
238
|
+
const moment = new Date(Number(param.time) * 1000);
|
|
239
|
+
el.textContent = `${this._host.presentation.timeText(moment)} ${formatPnl(value)}`;
|
|
240
|
+
el.className = `equity-hover ${this._host.presentation.pnlClass(value)}`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
EquityWidget.TYPE = ControlTypes.Equity;
|
|
244
|
+
/// The zone a moment reads in: the browser's own, which is the zone
|
|
245
|
+
/// `presentation.timeText` words one in. UTC when the environment cannot say.
|
|
246
|
+
function browserTimeZone() {
|
|
247
|
+
try {
|
|
248
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return 'UTC';
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/// A colour at a given opacity.
|
|
255
|
+
///
|
|
256
|
+
/// The host answers with whatever CSS understands, so this cannot take it apart - it wraps it
|
|
257
|
+
/// instead, which `color-mix` accepts for every form a token can be written in.
|
|
258
|
+
function withAlpha(colour, alpha) {
|
|
259
|
+
return `color-mix(in srgb, ${colour} ${Math.round(alpha * 100)}%, transparent)`;
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=equity-widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equity-widget.js","sourceRoot":"","sources":["../../src/equity-widget.ts"],"names":[],"mappings":"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"}
|