@stocksharp/trading-controls 1.2.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.
Files changed (99) hide show
  1. package/README.md +204 -17
  2. package/dist/esm/black-scholes.js +147 -0
  3. package/dist/esm/black-scholes.js.map +1 -0
  4. package/dist/esm/chart-engine.js +15 -0
  5. package/dist/esm/chart-engine.js.map +1 -0
  6. package/dist/esm/control-types.js +8 -0
  7. package/dist/esm/control-types.js.map +1 -1
  8. package/dist/esm/equity-widget.js +261 -0
  9. package/dist/esm/equity-widget.js.map +1 -0
  10. package/dist/esm/heatmap-grid.js +271 -0
  11. package/dist/esm/heatmap-grid.js.map +1 -0
  12. package/dist/esm/index.js +37 -0
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/log-monitor-widget.js +265 -0
  15. package/dist/esm/log-monitor-widget.js.map +1 -0
  16. package/dist/esm/log-tree.js +96 -0
  17. package/dist/esm/log-tree.js.map +1 -0
  18. package/dist/esm/optimization-heatmap-widget.js +303 -0
  19. package/dist/esm/optimization-heatmap-widget.js.map +1 -0
  20. package/dist/esm/option-desk-widget.js +322 -0
  21. package/dist/esm/option-desk-widget.js.map +1 -0
  22. package/dist/esm/option-smile-widget.js +252 -0
  23. package/dist/esm/option-smile-widget.js.map +1 -0
  24. package/dist/esm/pnl-curve.js +133 -0
  25. package/dist/esm/pnl-curve.js.map +1 -0
  26. package/dist/esm/statistics-widget.js +194 -0
  27. package/dist/esm/statistics-widget.js.map +1 -0
  28. package/dist/esm/strategies-widget.js +348 -0
  29. package/dist/esm/strategies-widget.js.map +1 -0
  30. package/dist/esm/surface-grid.js +282 -0
  31. package/dist/esm/surface-grid.js.map +1 -0
  32. package/dist/esm/surface-widget.js +379 -0
  33. package/dist/esm/surface-widget.js.map +1 -0
  34. package/dist/sstradingcontrols.js +2982 -295
  35. package/dist/sstradingcontrols.js.map +4 -4
  36. package/dist/types/black-scholes.d.ts +28 -0
  37. package/dist/types/black-scholes.d.ts.map +1 -0
  38. package/dist/types/chart-engine.d.ts +3 -0
  39. package/dist/types/chart-engine.d.ts.map +1 -0
  40. package/dist/types/control-types.d.ts +8 -0
  41. package/dist/types/control-types.d.ts.map +1 -1
  42. package/dist/types/equity-widget.d.ts +34 -0
  43. package/dist/types/equity-widget.d.ts.map +1 -0
  44. package/dist/types/heatmap-grid.d.ts +88 -0
  45. package/dist/types/heatmap-grid.d.ts.map +1 -0
  46. package/dist/types/index.d.ts +31 -1
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/log-monitor-widget.d.ts +42 -0
  49. package/dist/types/log-monitor-widget.d.ts.map +1 -0
  50. package/dist/types/log-tree.d.ts +34 -0
  51. package/dist/types/log-tree.d.ts.map +1 -0
  52. package/dist/types/optimization-heatmap-widget.d.ts +45 -0
  53. package/dist/types/optimization-heatmap-widget.d.ts.map +1 -0
  54. package/dist/types/option-desk-widget.d.ts +68 -0
  55. package/dist/types/option-desk-widget.d.ts.map +1 -0
  56. package/dist/types/option-smile-widget.d.ts +39 -0
  57. package/dist/types/option-smile-widget.d.ts.map +1 -0
  58. package/dist/types/pnl-curve.d.ts +46 -0
  59. package/dist/types/pnl-curve.d.ts.map +1 -0
  60. package/dist/types/statistics-widget.d.ts +29 -0
  61. package/dist/types/statistics-widget.d.ts.map +1 -0
  62. package/dist/types/strategies-widget.d.ts +63 -0
  63. package/dist/types/strategies-widget.d.ts.map +1 -0
  64. package/dist/types/surface-grid.d.ts +64 -0
  65. package/dist/types/surface-grid.d.ts.map +1 -0
  66. package/dist/types/surface-widget.d.ts +64 -0
  67. package/dist/types/surface-widget.d.ts.map +1 -0
  68. package/dist/types/trading-data.d.ts +9 -0
  69. package/dist/types/trading-data.d.ts.map +1 -1
  70. package/package.json +54 -3
  71. package/screenshots/equity.png +0 -0
  72. package/screenshots/log-monitor.png +0 -0
  73. package/screenshots/optimization-surface.png +0 -0
  74. package/screenshots/optimization.png +0 -0
  75. package/screenshots/option-desk.png +0 -0
  76. package/screenshots/option-smile.png +0 -0
  77. package/screenshots/panels.jpg +0 -0
  78. package/screenshots/statistics.png +0 -0
  79. package/screenshots/strategies.png +0 -0
  80. package/src/black-scholes.ts +199 -0
  81. package/src/chart-engine.global.ts +64 -0
  82. package/src/chart-engine.ts +29 -0
  83. package/src/control-types.ts +8 -0
  84. package/src/equity-widget.ts +289 -0
  85. package/src/heatmap-grid.ts +405 -0
  86. package/src/index.ts +87 -0
  87. package/src/log-monitor-widget.ts +312 -0
  88. package/src/log-tree.ts +131 -0
  89. package/src/optimization-heatmap-widget.ts +347 -0
  90. package/src/option-desk-widget.ts +422 -0
  91. package/src/option-smile-widget.ts +281 -0
  92. package/src/pnl-curve.ts +216 -0
  93. package/src/statistics-widget.ts +226 -0
  94. package/src/strategies-widget.ts +435 -0
  95. package/src/surface-grid.ts +410 -0
  96. package/src/surface-widget.ts +457 -0
  97. package/src/trading-data.ts +22 -0
  98. package/styles/trading-controls.css +637 -0
  99. package/translation-keys.json +83 -1
package/README.md CHANGED
@@ -4,10 +4,13 @@
4
4
  [![npm version](https://img.shields.io/npm/v/%40stocksharp%2Ftrading-controls.svg)](https://www.npmjs.com/package/@stocksharp/trading-controls)
5
5
  [![License](https://img.shields.io/badge/license-StockSharp%20EULA-c8202f.svg)](LICENSE)
6
6
 
7
- **StockSharp JS Trading Controls** are the browser panels a trading screen is
8
- made of: an **active orders** blotter with inline edit, a **positions** blotter
9
- with a pinned cash balance, a **trade history** blotter, and a **watchlist**
10
- with live quotes, favourites and category tabs.
7
+ **StockSharp JS Trading Controls** are the browser panels a trading screen is made
8
+ of. Fifteen of them: the **active orders**, **positions** and **trade history**
9
+ blotters, a **watchlist** with live quotes and category tabs, an **order entry**
10
+ pad, a **trade feed**, an **order book** ladder, a **statistics** table, a
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**.
11
14
 
12
15
  Each control builds its own DOM, renders its own table through
13
16
  [`@stocksharp/grids`](https://www.npmjs.com/package/@stocksharp/grids), and reaches
@@ -18,7 +21,7 @@ the outside world through exactly one object — a `TradingHost`.
18
21
  [GitHub repository](https://github.com/StockSharp/JS-TradingControls) ·
19
22
  [Issue tracker](https://github.com/StockSharp/JS-TradingControls/issues)
20
23
 
21
- ![The demo board: a candlestick chart, the trade feed, the order book, the watchlist, the order entry pad and the tabbed blotters, docked the way the terminal docks them](screenshots/panels.jpg)
24
+ ![The demo board: a candlestick chart with two moving averages and a legend, the trade feed, the order book, the watchlist, the order entry pad and the tabbed blotters, docked the way the terminal docks them](screenshots/panels.jpg)
22
25
 
23
26
  The page above is `demo/` — the published bundle over a demo `TradingHost`, no
24
27
  server and no network, laid out by the same dockview-core the StockSharp web
@@ -27,12 +30,28 @@ candlestick panel fed by the same simulated prices. The **Host port traffic**
27
30
  tab records every call the controls made into that host, which is the whole of
28
31
  what they can reach.
29
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
+
30
41
  ## Quick start
31
42
 
32
43
  ```sh
33
44
  npm install @stocksharp/trading-controls
34
45
  ```
35
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
+
36
55
  ```ts
37
56
  import { PositionsWidget } from '@stocksharp/trading-controls';
38
57
  import '@stocksharp/trading-controls/styles.css';
@@ -60,6 +79,17 @@ The package also ships a ready-to-use browser bundle exposed as
60
79
  </script>
61
80
  ```
62
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
+
63
93
  ## The host port is the whole API surface
64
94
 
65
95
  A control imports no translator, no settings singleton, no panel registry and no
@@ -86,15 +116,18 @@ clicking something that does nothing. So a missing member throws at construction
86
116
  naming the path — `WatchlistWidget: host.trading.api.getExecutions is required` —
87
117
  nested members included.
88
118
 
89
- **Seven of them no shipped control calls**: `spawn`, `persistState`,
90
- `saveLayout`, `log`, `trading.pickInstrument`, `marketData.resubscribe` and
91
- `marketData.getOrders`. They are required anyway, and not on speculation — the
92
- terminal this was extracted from has seven controls, and the three still on its
93
- side of the boundary (order book, order entry, trade feed) are already written
94
- against this same interface and call every one of those members. Narrowing the
95
- port now and widening it again as each moves in would break every host twice.
96
- If you are adopting only the four controls here, stubs are a correct answer: a
97
- no-op `spawn`, a `log` that forwards to the console.
119
+ **Every member is now reached by some shipped control.** When four of them were
120
+ here the port was already this wide, and the seven members nothing yet called —
121
+ `spawn`, `persistState`, `saveLayout`, `log`, `trading.pickInstrument`,
122
+ `marketData.resubscribe`, `marketData.getOrders` were required anyway, because
123
+ the controls still on the terminal's side of the boundary were written against
124
+ this same interface and called them. Those controls have since moved in and do
125
+ call them, which is the argument for not having narrowed the port and widened it
126
+ again seven times.
127
+
128
+ A host adopting a subset still has to answer for all of it. Stubs are a correct
129
+ answer where a capability genuinely does not exist on that page: a no-op `spawn`,
130
+ a `log` that forwards to the console.
98
131
 
99
132
  Two pairs are deliberately separate rather than merged:
100
133
 
@@ -121,7 +154,7 @@ Two pairs are deliberately separate rather than merged:
121
154
  reads the port names (`getExecutions`, `searchInstruments`), which the host
122
155
  implements.
123
156
 
124
- ### The 114 keys a host has to answer
157
+ ### The 235 keys a host has to answer
125
158
 
126
159
  `t()` cannot fail. A key the host does not know is rendered to the user as
127
160
  itself, so `NoActiveOrders` appears in the empty blotter and `ClosePanel`
@@ -130,11 +163,20 @@ error. The complete list ships with the package:
130
163
 
131
164
  ```ts
132
165
  import keys from '@stocksharp/trading-controls/translation-keys.json';
133
- // { count: 114, keys: ['Actions', 'ActiveOrders', …] }
166
+ // { count: 235, keys: ['Actions', 'ActiveOrders', …] }
134
167
  ```
135
168
 
136
169
  It is **generated from the sources** (`npm run i18n:update`) and re-checked by
137
- `npm test`, so it cannot drift from what the controls actually ask for.
170
+ `npm test`, so it cannot drift from what the controls actually ask for. Because it
171
+ is generated by scanning for `t('literal')`, no control may assemble a key from a
172
+ value: a computed key would be invisible to the scan, absent from the list, and
173
+ would reach a user as itself. A test renders each control over data that takes
174
+ every branch of its wording and asserts that everything it asked for is on the
175
+ list.
176
+
177
+ One set of keys is deliberately not: the trading modes a host hands
178
+ `StrategiesWidget`. The host says what a run may be put in and therefore words
179
+ them, so those strings are its vocabulary and answering for them is its job.
138
180
 
139
181
  The keys are not derivable, which is why the list is shipped rather than
140
182
  described. Most are resource identifiers (`ClosePanel`, `ExportToExcel`,
@@ -304,6 +346,151 @@ is class names: the volume bar's share of its side and the heat behind a level
304
346
  reach the stylesheet as measured custom properties (`--t-ob-bar`, `--t-ob-heat`,
305
347
  `--t-ob-sent`), so no width and no colour is decided in TypeScript.
306
348
 
349
+ ### `StatisticsWidget`
350
+
351
+ What a run made, one parameter per row, grouped by category and left in the order
352
+ the host sent them — the registry's order, which puts profit before trades before
353
+ positions before orders, and is not alphabetical. The panel measures nothing: a
354
+ statistic is produced by whatever ran the strategy, so the whole set arrives at
355
+ once through `update` and a row that stopped being sent has stopped existing.
356
+ There are no deps beyond the host, because there is nothing on the table to act
357
+ on. A value is a number, a moment or nothing at all, and nothing is a blank cell
358
+ rather than a zero: a figure that has not been measured yet is not a measured
359
+ zero.
360
+
361
+ ![The statistics table: profit, drawdown and trade counts grouped by category, derived from the demo's own fills and moving with them](screenshots/statistics.png)
362
+
363
+ ### `StrategiesWidget`
364
+
365
+ A row per run: its state, whether it is online, the mode it trades in, its
366
+ instrument, position, order and trade counts, what it has made, and a sparkline of
367
+ how it got there. Every action is a dep and a row only offers what the host can
368
+ carry out — start appears on a stopped run and stop on a started one, the
369
+ trading-mode cell is a plain caption unless `setTradingMode` was supplied. The
370
+ modes themselves come from the host and are worded through `t()`, which makes them
371
+ the one set of keys in this package that the host answers for rather than the
372
+ shipped list.
373
+
374
+ The sparkline is a canvas sized in device pixels and drawn from
375
+ `presentation.canvasPalette()`, so it is as sharp as the text beside it and green
376
+ and red mean there what they mean everywhere else on the page.
377
+
378
+ ![The strategies dashboard: three runs with their state, trading mode, P&L and a sparkline each, one of them stopped and reporting why](screenshots/strategies.png)
379
+
380
+ ### `LogMonitorWidget`
381
+
382
+ A source tree beside the messages, which is how a log with more than one writer is
383
+ read: pick a node and the table shows that source and everything under it. Sources
384
+ arrive whole through `setSources` and messages accumulate through `append`, capped
385
+ (`maxMessages`, five thousand by default) so a session left running overnight
386
+ cannot grow without bound. The five levels toggle independently and the filter is
387
+ a substring over the message and its source together.
388
+
389
+ A message names its source by id; what that id is *called* comes from the tree, so
390
+ renaming a source — or switching the page's language — re-captions the messages
391
+ that were already in the table.
392
+
393
+ ![The log monitor: the demo board as a source tree, with every host-port call the controls made filed under the panel that made it](screenshots/log-monitor.png)
394
+
395
+ ### `OptionDeskWidget`
396
+
397
+ One expiry of a chain, the call side mirrored against the put side around the
398
+ strike. Volume and open interest are scaled per side, because calls and puts trade
399
+ in different sizes and comparing one against the busiest of the other says
400
+ nothing; volatility is scaled across both at once, because a skew is exactly the
401
+ comparison between them.
402
+
403
+ Greeks arrive one of two ways and the desk does not care which: a host that prices
404
+ its own sends them on the contract, and a host that has quotes and an expiry sends
405
+ the volatility instead and the desk prices delta through rho itself, from the
406
+ Black-Scholes that ships with the package (`premium`, `greeks`,
407
+ `impliedVolatility` — exported, because a host that wants the arithmetic without
408
+ the table should not have to reimplement it). The number of decimal places in a
409
+ greek column comes from the numbers in it: gamma on an underlying at sixty
410
+ thousand is about 0.00003, and the four places that suit a delta would show every
411
+ strike as nothing.
412
+
413
+ ![The option desk: a BTC chain mirrored around the strike, its smile, per-side volume bars and a green-to-red split at the money](screenshots/option-desk.png)
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
+ ![The option smile: call and put volatility by strike on one scale, the crosshair naming the strike and both sides at it, the underlying marked](screenshots/option-smile.png)
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
+ ![The equity panel: a run's cumulative P&L filled to the water line, the crosshair naming the moment and the figure under the pointer](screenshots/equity.png)
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
+ ![The optimisation heatmap: net profit over a moving-average pair, gaps where no run exists and the best cell marked](screenshots/optimization.png)
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
+ ![The optimisation surface: the same sweep as a landscape, turned by hand, with the losing corner in red](screenshots/optimization-surface.png)
493
+
307
494
  ## Exported sheets say what the screen says
308
495
 
309
496
  Every blotter exports to `.xlsx` through the grid, and a column states both
@@ -0,0 +1,147 @@
1
+ // Option pricing and the greeks, as pure functions.
2
+ //
3
+ // The desktop desk gets these from StockSharp.Algo.Derivatives, which a browser cannot call. A
4
+ // host could compute them and send them down, and one that already does should - the desk takes
5
+ // them either way. But a host that has bid, ask and an expiry has everything the arithmetic
6
+ // needs, and making it stand up a pricing service before it can draw a chain is a poor trade.
7
+ //
8
+ // So the maths ships, and it ships here, apart from the control: a wrong greek is not visible
9
+ // on screen the way a wrong column is, and the only defence is checking the numbers against
10
+ // values that are known.
11
+ //
12
+ // Conventions follow the desk that reads them, not the textbook: vega is per one point of
13
+ // volatility, theta is per calendar day, and rho is per one point of rate - the units a trader
14
+ // works in, rather than per 1.0 of each, which nobody quotes.
15
+ export const OptionTypes = {
16
+ Call: 'call',
17
+ Put: 'put',
18
+ };
19
+ const SQRT_2PI = Math.sqrt(2 * Math.PI);
20
+ const DAYS_IN_YEAR = 365;
21
+ /// The standard normal CDF.
22
+ ///
23
+ /// Hart's rational approximation, which is what pricing libraries use and what a desk's numbers
24
+ /// are expected to agree with: accurate to about 1e-15 across the range, and exactly a half at
25
+ /// the mean rather than nearly so. The cheaper Abramowitz-and-Stegun fit is good to 1e-7, which
26
+ /// sounds like enough until a greek that should be symmetric is not.
27
+ export function normalCdf(x) {
28
+ const z = Math.abs(x);
29
+ // Beyond this the tail is smaller than double precision can carry anyway.
30
+ if (z > 37)
31
+ return x > 0 ? 1 : 0;
32
+ const e = Math.exp(-(z * z) / 2);
33
+ let tail;
34
+ if (z < 7.07106781186547) {
35
+ let b = 3.52624965998911e-02 * z + 0.700383064443688;
36
+ b = b * z + 6.37396220353165;
37
+ b = b * z + 33.912866078383;
38
+ b = b * z + 112.079291497871;
39
+ b = b * z + 221.213596169931;
40
+ b = b * z + 220.206867912376;
41
+ let d = 8.83883476483184e-02 * z + 1.75566716318264;
42
+ d = d * z + 16.064177579207;
43
+ d = d * z + 86.7807322029461;
44
+ d = d * z + 296.564248779674;
45
+ d = d * z + 637.333633378831;
46
+ d = d * z + 793.826512519948;
47
+ d = d * z + 440.413735824752;
48
+ tail = (e * b) / d;
49
+ }
50
+ else {
51
+ // A continued fraction, which is what stays accurate once the polynomial ratio above
52
+ // starts losing digits to cancellation.
53
+ let b = z + 0.65;
54
+ b = z + 4 / b;
55
+ b = z + 3 / b;
56
+ b = z + 2 / b;
57
+ b = z + 1 / b;
58
+ tail = e / (b * 2.506628274631);
59
+ }
60
+ return x > 0 ? 1 - tail : tail;
61
+ }
62
+ /// The standard normal density.
63
+ export function normalPdf(x) {
64
+ return Math.exp(-0.5 * x * x) / SQRT_2PI;
65
+ }
66
+ /// d1, or zero when there is no time or no volatility for the division to mean anything.
67
+ export function d1(inputs) {
68
+ const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;
69
+ const spread = deviation * Math.sqrt(timeToExpiry);
70
+ if (spread === 0 || assetPrice <= 0 || strike <= 0)
71
+ return 0;
72
+ return (Math.log(assetPrice / strike) + (riskFree - dividend + (deviation * deviation) / 2) * timeToExpiry) / spread;
73
+ }
74
+ /// d2, which is d1 less one standard deviation of the remaining time.
75
+ export function d2(inputs) {
76
+ const spread = inputs.deviation * Math.sqrt(inputs.timeToExpiry);
77
+ return spread === 0 ? 0 : d1(inputs) - spread;
78
+ }
79
+ /// What the option is worth.
80
+ ///
81
+ /// At expiry, or with no volatility, that is its intrinsic value - the formula degenerates to
82
+ /// exactly that, and saying so explicitly keeps a zero denominator out of the general case.
83
+ export function premium(type, inputs) {
84
+ const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;
85
+ if (timeToExpiry <= 0 || deviation <= 0)
86
+ return Math.max(0, type === OptionTypes.Call ? assetPrice - strike : strike - assetPrice);
87
+ const a = d1(inputs);
88
+ const b = d2(inputs);
89
+ const carried = assetPrice * Math.exp(-dividend * timeToExpiry);
90
+ const discounted = strike * Math.exp(-riskFree * timeToExpiry);
91
+ return type === OptionTypes.Call
92
+ ? carried * normalCdf(a) - discounted * normalCdf(b)
93
+ : discounted * normalCdf(-b) - carried * normalCdf(-a);
94
+ }
95
+ /// Every greek at once: they share d1, and computing them together is both cheaper and the only
96
+ /// way they are guaranteed to describe the same moment.
97
+ export function greeks(type, inputs) {
98
+ const { assetPrice, strike, timeToExpiry, riskFree, dividend, deviation } = inputs;
99
+ const sign = type === OptionTypes.Call ? 1 : -1;
100
+ if (timeToExpiry <= 0 || deviation <= 0 || assetPrice <= 0) {
101
+ // An expired option still has a delta - it is one or nothing, depending on which side of
102
+ // the strike it finished. Everything else has stopped moving.
103
+ const inTheMoney = type === OptionTypes.Call ? assetPrice > strike : assetPrice < strike;
104
+ return { delta: inTheMoney ? sign : 0, gamma: 0, vega: 0, theta: 0, rho: 0 };
105
+ }
106
+ const a = d1(inputs);
107
+ const b = d2(inputs);
108
+ const sqrtT = Math.sqrt(timeToExpiry);
109
+ const density = normalPdf(a);
110
+ const carry = Math.exp(-dividend * timeToExpiry);
111
+ const discount = Math.exp(-riskFree * timeToExpiry);
112
+ const delta = sign * carry * normalCdf(sign * a);
113
+ const gamma = (carry * density) / (assetPrice * deviation * sqrtT);
114
+ // Scaled the way a desk quotes them: vega per one volatility point, rho per one rate point,
115
+ // theta per calendar day.
116
+ const vega = assetPrice * carry * density * sqrtT * 0.01;
117
+ const rho = sign * strike * timeToExpiry * discount * normalCdf(sign * b) * 0.01;
118
+ const theta = (-(assetPrice * carry * density * deviation) / (2 * sqrtT)
119
+ - sign * riskFree * strike * discount * normalCdf(sign * b)
120
+ + sign * dividend * assetPrice * carry * normalCdf(sign * a)) / DAYS_IN_YEAR;
121
+ return { delta, gamma, vega, theta, rho };
122
+ }
123
+ /// The volatility that would produce this price, or null when none would.
124
+ ///
125
+ /// Bisection rather than Newton: vega collapses far from the money and deep in time, and a
126
+ /// Newton step divided by a vanishing vega walks off to nonsense. Halving cannot, and forty
127
+ /// steps over a range this wide is finer than any quote.
128
+ export function impliedVolatility(type, inputs, price) {
129
+ if (!(price > 0) || inputs.timeToExpiry <= 0)
130
+ return null;
131
+ const at = (deviation) => premium(type, { ...inputs, deviation });
132
+ let low = 1e-6;
133
+ let high = 5;
134
+ // A price outside what any volatility in that range can produce has no answer, and
135
+ // returning the nearest bound would be a number that means nothing.
136
+ if (price < at(low) || price > at(high))
137
+ return null;
138
+ for (let step = 0; step < 60; step++) {
139
+ const mid = (low + high) / 2;
140
+ if (at(mid) < price)
141
+ low = mid;
142
+ else
143
+ high = mid;
144
+ }
145
+ return (low + high) / 2;
146
+ }
147
+ //# sourceMappingURL=black-scholes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"black-scholes.js","sourceRoot":"","sources":["../../src/black-scholes.ts"],"names":[],"mappings":"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"}
@@ -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"}
@@ -22,5 +22,13 @@ export const ControlTypes = {
22
22
  OrderBook: 'orderbook',
23
23
  TradeFeed: 'tradefeed',
24
24
  OrderEntry: 'orderEntry',
25
+ Statistics: 'statistics',
26
+ LogMonitor: 'logMonitor',
27
+ Strategies: 'strategies',
28
+ OptionDesk: 'optionDesk',
29
+ OptionSmile: 'optionSmile',
30
+ Equity: 'equity',
31
+ OptimizationHeatmap: 'optimizationHeatmap',
32
+ OptimizationSurface: 'optimizationSurface',
25
33
  };
26
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;CAClB,CAAC"}
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"}