@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.
- package/README.md +204 -17
- package/dist/esm/black-scholes.js +147 -0
- package/dist/esm/black-scholes.js.map +1 -0
- package/dist/esm/chart-engine.js +15 -0
- package/dist/esm/chart-engine.js.map +1 -0
- package/dist/esm/control-types.js +8 -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 +37 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/log-monitor-widget.js +265 -0
- package/dist/esm/log-monitor-widget.js.map +1 -0
- package/dist/esm/log-tree.js +96 -0
- package/dist/esm/log-tree.js.map +1 -0
- package/dist/esm/optimization-heatmap-widget.js +303 -0
- package/dist/esm/optimization-heatmap-widget.js.map +1 -0
- package/dist/esm/option-desk-widget.js +322 -0
- package/dist/esm/option-desk-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 +133 -0
- package/dist/esm/pnl-curve.js.map +1 -0
- package/dist/esm/statistics-widget.js +194 -0
- package/dist/esm/statistics-widget.js.map +1 -0
- package/dist/esm/strategies-widget.js +348 -0
- package/dist/esm/strategies-widget.js.map +1 -0
- 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 +2982 -295
- package/dist/sstradingcontrols.js.map +4 -4
- package/dist/types/black-scholes.d.ts +28 -0
- package/dist/types/black-scholes.d.ts.map +1 -0
- 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 +8 -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 +31 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/log-monitor-widget.d.ts +42 -0
- package/dist/types/log-monitor-widget.d.ts.map +1 -0
- package/dist/types/log-tree.d.ts +34 -0
- package/dist/types/log-tree.d.ts.map +1 -0
- 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-desk-widget.d.ts +68 -0
- package/dist/types/option-desk-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 +46 -0
- package/dist/types/pnl-curve.d.ts.map +1 -0
- package/dist/types/statistics-widget.d.ts +29 -0
- package/dist/types/statistics-widget.d.ts.map +1 -0
- package/dist/types/strategies-widget.d.ts +63 -0
- package/dist/types/strategies-widget.d.ts.map +1 -0
- 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/dist/types/trading-data.d.ts +9 -0
- package/dist/types/trading-data.d.ts.map +1 -1
- package/package.json +54 -3
- package/screenshots/equity.png +0 -0
- package/screenshots/log-monitor.png +0 -0
- package/screenshots/optimization-surface.png +0 -0
- package/screenshots/optimization.png +0 -0
- package/screenshots/option-desk.png +0 -0
- package/screenshots/option-smile.png +0 -0
- package/screenshots/panels.jpg +0 -0
- package/screenshots/statistics.png +0 -0
- package/screenshots/strategies.png +0 -0
- package/src/black-scholes.ts +199 -0
- package/src/chart-engine.global.ts +64 -0
- package/src/chart-engine.ts +29 -0
- package/src/control-types.ts +8 -0
- package/src/equity-widget.ts +289 -0
- package/src/heatmap-grid.ts +405 -0
- package/src/index.ts +87 -0
- package/src/log-monitor-widget.ts +312 -0
- package/src/log-tree.ts +131 -0
- package/src/optimization-heatmap-widget.ts +347 -0
- package/src/option-desk-widget.ts +422 -0
- package/src/option-smile-widget.ts +281 -0
- package/src/pnl-curve.ts +216 -0
- package/src/statistics-widget.ts +226 -0
- package/src/strategies-widget.ts +435 -0
- package/src/surface-grid.ts +410 -0
- package/src/surface-widget.ts +457 -0
- package/src/trading-data.ts +22 -0
- package/styles/trading-controls.css +637 -0
- package/translation-keys.json +83 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"option-desk-widget.js","sourceRoot":"","sources":["../../src/option-desk-widget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// Option smile — multi-instance.
|
|
2
|
+
//
|
|
3
|
+
// One expiry's implied volatility against strike, call side and put side as two curves on one
|
|
4
|
+
// scale, drawn by @stocksharp/chart.
|
|
5
|
+
//
|
|
6
|
+
// The engine is a time-series engine and this axis is a ladder of strikes, which looked at first
|
|
7
|
+
// like a reason to draw the chart by hand. It is not: the axis carries numbers, and what turns
|
|
8
|
+
// them into dates is `timeScale.formatter` - one function shared by the tick labels and the
|
|
9
|
+
// crosshair label. Given a formatter that words a number as a strike, the axis is a strike axis,
|
|
10
|
+
// and the crosshair, the legend readout, the zoom and the pan come with it rather than being
|
|
11
|
+
// written again here.
|
|
12
|
+
//
|
|
13
|
+
// `mode: 'ordinal'` on top of that, because a listed chain is evenly spaced by listing rather
|
|
14
|
+
// than by the gaps between its numbers: a venue that lists 67000, 67250 and then 68000 means
|
|
15
|
+
// three rungs, not a hole.
|
|
16
|
+
import { formatPrice } from './formatters.js';
|
|
17
|
+
import { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';
|
|
18
|
+
import { ControlTypes } from './control-types.js';
|
|
19
|
+
import { assertHost } from './trading-host.js';
|
|
20
|
+
import { CrosshairMode, LineSeries, createChart, } from './chart-engine.js';
|
|
21
|
+
const LINE_WIDTH = 2;
|
|
22
|
+
/// The volatility a side is drawn at: what it last traded at, or the middle of its quote when it
|
|
23
|
+
/// has not traded. Null when neither is known - a strike nobody has quoted is a gap in the curve,
|
|
24
|
+
/// and a line drawn through it invents a quote nobody made.
|
|
25
|
+
export function sideVolatility(side) {
|
|
26
|
+
if (side === undefined || side === null)
|
|
27
|
+
return null;
|
|
28
|
+
const last = side.ivLast;
|
|
29
|
+
if (typeof last === 'number' && isFinite(last) && last > 0)
|
|
30
|
+
return last;
|
|
31
|
+
const bid = side.ivBid;
|
|
32
|
+
const ask = side.ivAsk;
|
|
33
|
+
const quoted = [bid, ask].filter((v) => typeof v === 'number' && isFinite(v) && v > 0);
|
|
34
|
+
if (quoted.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
return quoted.reduce((sum, v) => sum + v, 0) / quoted.length;
|
|
37
|
+
}
|
|
38
|
+
/// The chain in the order it is drawn: by strike, lowest first, whatever order it arrived in.
|
|
39
|
+
export function sortedChain(strikes) {
|
|
40
|
+
return [...strikes]
|
|
41
|
+
.filter(row => row !== null && row !== undefined && isFinite(row.strike))
|
|
42
|
+
.sort((a, b) => a.strike - b.strike);
|
|
43
|
+
}
|
|
44
|
+
/// One side as a series, and the strikes it was drawn at.
|
|
45
|
+
///
|
|
46
|
+
/// The x value is the strike itself rather than an index: the axis words it back through the
|
|
47
|
+
/// formatter, so the number on the wire and the number on the label are the same one and no
|
|
48
|
+
/// lookup can put them out of step.
|
|
49
|
+
export function toSmileSeries(chain, put) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const row of chain) {
|
|
52
|
+
const volatility = sideVolatility(put ? row.put : row.call);
|
|
53
|
+
// Whitespace rather than a point: the engine keeps the rung on the axis and leaves the
|
|
54
|
+
// line broken across it, which is what a strike quoted on one side only looks like.
|
|
55
|
+
if (volatility === null)
|
|
56
|
+
out.push({ time: row.strike });
|
|
57
|
+
else
|
|
58
|
+
out.push({ time: row.strike, value: volatility * 100 });
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
export class OptionSmileWidget {
|
|
63
|
+
static create(hostEl, state, deps) {
|
|
64
|
+
const host = assertHost(deps?.host, 'OptionSmileWidget');
|
|
65
|
+
const root = OptionSmileWidget._buildRoot(host);
|
|
66
|
+
root.id = makePanelId(OptionSmileWidget.TYPE);
|
|
67
|
+
hostEl.appendChild(root);
|
|
68
|
+
return new OptionSmileWidget(root, state || {}, deps);
|
|
69
|
+
}
|
|
70
|
+
static _buildRoot(host) {
|
|
71
|
+
const title = host.t('OptionSmile');
|
|
72
|
+
return makePanelRoot('option-smile-panel', title, [
|
|
73
|
+
makeElement('div', 'panel-header', {}, [
|
|
74
|
+
makeElement('span', '', {}, [title]),
|
|
75
|
+
makeIconButton('bt-icon-btn option-smile-reset-btn', host.t('ResetView'), 'bi-arrow-clockwise', { type: 'button' }),
|
|
76
|
+
makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),
|
|
77
|
+
]),
|
|
78
|
+
makeElement('div', 'panel-body', {}, [
|
|
79
|
+
makeElement('div', 'option-smile-legend', {}, [
|
|
80
|
+
makeElement('span', 'option-smile-axis', {}, [host.t('ImpliedVolatility')]),
|
|
81
|
+
makeElement('span', 'option-smile-key', {}, [
|
|
82
|
+
makeElement('span', 'option-smile-dot option-smile-dot-call', {}, []),
|
|
83
|
+
host.t('Call'),
|
|
84
|
+
]),
|
|
85
|
+
makeElement('span', 'option-smile-key', {}, [
|
|
86
|
+
makeElement('span', 'option-smile-dot option-smile-dot-put', {}, []),
|
|
87
|
+
host.t('Put'),
|
|
88
|
+
]),
|
|
89
|
+
makeElement('span', 'option-smile-spot', {}, []),
|
|
90
|
+
]),
|
|
91
|
+
makeElement('div', 'option-smile-chart', { role: 'img', 'aria-label': host.t('OptionChain') }, [
|
|
92
|
+
// Over the chart, not in the panel header: a reading about the point under the
|
|
93
|
+
// pointer belongs beside that point, and a host that lifts panel headers into a
|
|
94
|
+
// tab strip would otherwise write it into a tab title.
|
|
95
|
+
makeElement('div', 'option-smile-hover', {}, []),
|
|
96
|
+
makeElement('div', 'option-smile-empty', {}, [host.t('NoOptions')]),
|
|
97
|
+
]),
|
|
98
|
+
]),
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
constructor(rootEl, _state, deps) {
|
|
102
|
+
this._host = assertHost(deps?.host, 'OptionSmileWidget');
|
|
103
|
+
this.rootEl = rootEl;
|
|
104
|
+
this._closeBtn = this.rootEl.querySelector('.panel-close-btn');
|
|
105
|
+
this._resetBtn = this.rootEl.querySelector('.option-smile-reset-btn');
|
|
106
|
+
this._emptyEl = this.rootEl.querySelector('.option-smile-empty');
|
|
107
|
+
this._chartEl = this.rootEl.querySelector('.option-smile-chart');
|
|
108
|
+
this._spotEl = this.rootEl.querySelector('.option-smile-spot');
|
|
109
|
+
this._hoverEl = this.rootEl.querySelector('.option-smile-hover');
|
|
110
|
+
this._chart = null;
|
|
111
|
+
this._call = null;
|
|
112
|
+
this._put = null;
|
|
113
|
+
this._strikes = [];
|
|
114
|
+
this._context = {};
|
|
115
|
+
this._resizeObserver = null;
|
|
116
|
+
this._closeBtn?.addEventListener('click', (e) => { e.preventDefault(); this._host.close(); });
|
|
117
|
+
this._resetBtn?.addEventListener('click', (e) => { e.preventDefault(); this.resetZoom(); });
|
|
118
|
+
if (this._chartEl !== null && typeof ResizeObserver !== 'undefined') {
|
|
119
|
+
this._resizeObserver = new ResizeObserver(() => this._fit());
|
|
120
|
+
this._resizeObserver.observe(this._chartEl);
|
|
121
|
+
}
|
|
122
|
+
this._render();
|
|
123
|
+
this._host.register(this);
|
|
124
|
+
}
|
|
125
|
+
dispose() {
|
|
126
|
+
try {
|
|
127
|
+
this._resizeObserver?.disconnect();
|
|
128
|
+
}
|
|
129
|
+
catch { /* already torn down */ }
|
|
130
|
+
this._resizeObserver = null;
|
|
131
|
+
try {
|
|
132
|
+
this._chart?.remove();
|
|
133
|
+
}
|
|
134
|
+
catch { /* already gone */ }
|
|
135
|
+
this._chart = null;
|
|
136
|
+
this._call = null;
|
|
137
|
+
this._put = null;
|
|
138
|
+
this._host.unregister(this);
|
|
139
|
+
try {
|
|
140
|
+
this.rootEl.remove();
|
|
141
|
+
}
|
|
142
|
+
catch { /* already detached */ }
|
|
143
|
+
}
|
|
144
|
+
/// Show this chain, priced against this context.
|
|
145
|
+
///
|
|
146
|
+
/// The desk's two arguments unchanged, and for the same reason it takes them together: they
|
|
147
|
+
/// are one observation. A smile marked with a spot the curves never saw is two moments.
|
|
148
|
+
update(strikes, context = {}) {
|
|
149
|
+
this._strikes = strikes || [];
|
|
150
|
+
this._context = context || {};
|
|
151
|
+
this._render();
|
|
152
|
+
}
|
|
153
|
+
/// Show the whole chain again, after a zoom.
|
|
154
|
+
resetZoom() {
|
|
155
|
+
try {
|
|
156
|
+
this._chart?.timeScale().fitContent();
|
|
157
|
+
}
|
|
158
|
+
catch { /* no chart yet */ }
|
|
159
|
+
}
|
|
160
|
+
/// The engine, for a host that wants to add to this chart - a second expiry, a marker.
|
|
161
|
+
chart() {
|
|
162
|
+
return this._chart;
|
|
163
|
+
}
|
|
164
|
+
_ensureChart() {
|
|
165
|
+
if (this._chart !== null)
|
|
166
|
+
return true;
|
|
167
|
+
if (this._chartEl === null)
|
|
168
|
+
return false;
|
|
169
|
+
const palette = this._host.presentation.canvasPalette();
|
|
170
|
+
this._chart = createChart(this._chartEl, {
|
|
171
|
+
layout: {
|
|
172
|
+
background: { type: 'solid', color: 'transparent' },
|
|
173
|
+
textColor: palette.grid,
|
|
174
|
+
fontFamily: palette.font,
|
|
175
|
+
fontSize: 11,
|
|
176
|
+
attributionLogo: false,
|
|
177
|
+
},
|
|
178
|
+
grid: { vertLines: { color: palette.grid }, horzLines: { color: palette.grid } },
|
|
179
|
+
crosshair: { mode: CrosshairMode.Normal },
|
|
180
|
+
rightPriceScale: { borderColor: palette.grid },
|
|
181
|
+
timeScale: {
|
|
182
|
+
borderColor: palette.grid,
|
|
183
|
+
// Evenly spaced by listing, and worded as strikes: the axis carries the strike
|
|
184
|
+
// itself, so the label and the value can never drift apart.
|
|
185
|
+
mode: 'ordinal',
|
|
186
|
+
formatter: (value) => formatPrice(Number(value)),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
this._call = this._chart.addSeries(LineSeries, { lineWidth: LINE_WIDTH, color: palette.up });
|
|
190
|
+
this._put = this._chart.addSeries(LineSeries, { lineWidth: LINE_WIDTH, color: palette.down });
|
|
191
|
+
this._chart.subscribeCrosshairMove((param) => this._renderHover(param));
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
_render() {
|
|
195
|
+
const chain = sortedChain(this._strikes);
|
|
196
|
+
const quoted = chain.some(row => sideVolatility(row.call) !== null || sideVolatility(row.put) !== null);
|
|
197
|
+
if (this._emptyEl !== null)
|
|
198
|
+
this._emptyEl.hidden = quoted;
|
|
199
|
+
this._renderSpot();
|
|
200
|
+
if (!quoted)
|
|
201
|
+
return;
|
|
202
|
+
if (!this._ensureChart() || this._call === null || this._put === null)
|
|
203
|
+
return;
|
|
204
|
+
this._call.setData(toSmileSeries(chain, false));
|
|
205
|
+
this._put.setData(toSmileSeries(chain, true));
|
|
206
|
+
this._fit();
|
|
207
|
+
}
|
|
208
|
+
_fit() {
|
|
209
|
+
if (this._chart === null || this._chartEl === null)
|
|
210
|
+
return;
|
|
211
|
+
const rect = this._chartEl.getBoundingClientRect();
|
|
212
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
213
|
+
return;
|
|
214
|
+
try {
|
|
215
|
+
this._chart.resize(rect.width, rect.height);
|
|
216
|
+
}
|
|
217
|
+
catch { /* the engine is between frames */ }
|
|
218
|
+
}
|
|
219
|
+
// Where the money is. Not a line on the chart: an ordinal axis has a rung per listed strike
|
|
220
|
+
// and the spot is between two of them, so it is stated rather than drawn at a place the
|
|
221
|
+
// ladder does not have.
|
|
222
|
+
_renderSpot() {
|
|
223
|
+
const el = this._spotEl;
|
|
224
|
+
if (el === null)
|
|
225
|
+
return;
|
|
226
|
+
const spot = this._context.assetPrice;
|
|
227
|
+
if (typeof spot !== 'number' || !isFinite(spot)) {
|
|
228
|
+
el.textContent = '';
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
el.textContent = `${this._host.t('Underlying')} ${formatPrice(spot)}`;
|
|
232
|
+
}
|
|
233
|
+
// What the pointer is over: the strike and both sides at it. Both, because a smile is read by
|
|
234
|
+
// the distance between the two curves - a readout naming one of them answers half of it.
|
|
235
|
+
_renderHover(param) {
|
|
236
|
+
const el = this._hoverEl;
|
|
237
|
+
if (el === null)
|
|
238
|
+
return;
|
|
239
|
+
if (param.time === null) {
|
|
240
|
+
el.textContent = '';
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const at = (series) => {
|
|
244
|
+
const point = series === null ? undefined : param.seriesData.get(series);
|
|
245
|
+
const value = point?.value;
|
|
246
|
+
return typeof value === 'number' && isFinite(value) ? `${value.toFixed(2)}%` : '--';
|
|
247
|
+
};
|
|
248
|
+
el.textContent = `${formatPrice(Number(param.time))} ${at(this._call)} / ${at(this._put)}`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
OptionSmileWidget.TYPE = ControlTypes.OptionSmile;
|
|
252
|
+
//# sourceMappingURL=option-smile-widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"option-smile-widget.js","sourceRoot":"","sources":["../../src/option-smile-widget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// The geometry of a cumulative P&L curve, as pure functions over numbers.
|
|
2
|
+
//
|
|
3
|
+
// The same curve is read in three places and drawn at three sizes: a strategy row's sparkline,
|
|
4
|
+
// a statistics card, and a backtest's own chart. Only the last of those is a price chart - it
|
|
5
|
+
// shares the price axis and belongs to the chart engine. The other two are a curve in a box,
|
|
6
|
+
// and this is that curve: where each point lands, where the baseline sits, and which way the
|
|
7
|
+
// run ended. No canvas, so it can be checked against arithmetic rather than against pixels.
|
|
8
|
+
/// A run reduced to at most `count` samples, spanning the same stretch of time.
|
|
9
|
+
///
|
|
10
|
+
/// A sparkline that keeps only the newest N samples marches sideways: each new sample pushes the
|
|
11
|
+
/// oldest off the left edge, so the shape slides and the run's beginning is lost. Compressing
|
|
12
|
+
/// instead keeps the whole run in the box and re-buckets it, so a new sample changes the curve's
|
|
13
|
+
/// shape rather than its position - which is what makes two readings of the same strategy
|
|
14
|
+
/// comparable.
|
|
15
|
+
///
|
|
16
|
+
/// A bucket is summarised by its LAST sample, not by an average. This is a cumulative figure:
|
|
17
|
+
/// the last value in a bucket is where the run actually stood at that moment, while an average
|
|
18
|
+
/// would draw a rising run below the line it was on. Every point returned is therefore a real
|
|
19
|
+
/// sample, never a computed one, and the first and last are kept exactly - they are where the
|
|
20
|
+
/// run started and where it stands now.
|
|
21
|
+
export function compressPnl(points, count) {
|
|
22
|
+
if (points.length <= count || count < 2)
|
|
23
|
+
return points.slice();
|
|
24
|
+
const first = points[0];
|
|
25
|
+
const last = points[points.length - 1];
|
|
26
|
+
const span = last.time - first.time;
|
|
27
|
+
// Every sample at the same instant: there is no time to bucket by, so fall back to position.
|
|
28
|
+
if (!(span > 0)) {
|
|
29
|
+
const step = (points.length - 1) / (count - 1);
|
|
30
|
+
return Array.from({ length: count }, (_, i) => points[Math.round(i * step)]);
|
|
31
|
+
}
|
|
32
|
+
// One bucket per output point, the last sample in each standing for it. Buckets that caught
|
|
33
|
+
// nothing are skipped rather than repeated, so a quiet stretch reads as a long flat segment
|
|
34
|
+
// instead of as a row of identical points.
|
|
35
|
+
// `count - 1` buckets, not `count`: the first sample is seeded and the last is appended, so
|
|
36
|
+
// the buckets between them may contribute at most `count - 2` points. Sized any wider the
|
|
37
|
+
// result can overrun `count`, and trimming it afterwards would drop the run's start - the
|
|
38
|
+
// one point this function exists to keep.
|
|
39
|
+
const buckets = count - 1;
|
|
40
|
+
const out = [first];
|
|
41
|
+
let bucket = 0;
|
|
42
|
+
for (let i = 1; i < points.length; i++) {
|
|
43
|
+
const index = Math.min(buckets - 1, Math.floor(((points[i].time - first.time) / span) * buckets));
|
|
44
|
+
if (index > bucket) {
|
|
45
|
+
out.push(points[i - 1]);
|
|
46
|
+
bucket = index;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (out[out.length - 1] !== last)
|
|
50
|
+
out.push(last);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/// Lay a run out in a box, or null when there is no curve to draw.
|
|
54
|
+
///
|
|
55
|
+
/// Two rules that are not arbitrary. Zero is always in the range, so a run that only ever won
|
|
56
|
+
/// still shows the line it started from and the height of the curve means something. And the
|
|
57
|
+
/// horizontal axis is time, not sample index: a strategy that traded twice in the morning and
|
|
58
|
+
/// forty times after lunch should look like that, not like a curve with evenly spaced steps.
|
|
59
|
+
export function pnlCurve(points, box) {
|
|
60
|
+
const clean = points
|
|
61
|
+
.filter(p => p !== null && p !== undefined && Number.isFinite(p.time) && Number.isFinite(p.value))
|
|
62
|
+
.slice()
|
|
63
|
+
.sort((a, b) => a.time - b.time);
|
|
64
|
+
if (clean.length < 2)
|
|
65
|
+
return null;
|
|
66
|
+
// One sample per drawable pixel is as much as a box can show; past that the extra points are
|
|
67
|
+
// strokes on top of strokes. Compressing here rather than asking the caller to do it is what
|
|
68
|
+
// lets a host keep the whole run and still get a curve that does not slide.
|
|
69
|
+
const drawable = Math.max(2, Math.round(box.width - box.padX * 2));
|
|
70
|
+
const shown = compressPnl(clean, drawable);
|
|
71
|
+
const values = shown.map(p => p.value);
|
|
72
|
+
const min = Math.min(0, ...values);
|
|
73
|
+
const max = Math.max(0, ...values);
|
|
74
|
+
const left = box.padX;
|
|
75
|
+
const right = box.width - box.padX;
|
|
76
|
+
const top = box.padY;
|
|
77
|
+
const bottom = box.height - box.padY;
|
|
78
|
+
const firstTime = shown[0].time;
|
|
79
|
+
const lastTime = shown[shown.length - 1].time;
|
|
80
|
+
const timeSpan = lastTime - firstTime;
|
|
81
|
+
const valueSpan = max - min;
|
|
82
|
+
// A run with no spread in one axis collapses onto a line rather than dividing by nothing:
|
|
83
|
+
// every sample at the same instant stacks at the left edge, every sample at the same value
|
|
84
|
+
// rests on the top edge, which for a flat run is also its baseline.
|
|
85
|
+
const x = (time) => (timeSpan === 0 ? left : left + ((time - firstTime) / timeSpan) * (right - left));
|
|
86
|
+
const y = (value) => (valueSpan === 0 ? top : bottom - ((value - min) / valueSpan) * (bottom - top));
|
|
87
|
+
const curve = shown.map(p => [x(p.time), y(p.value)]);
|
|
88
|
+
const zeroY = y(0);
|
|
89
|
+
const last = shown[shown.length - 1].value;
|
|
90
|
+
return {
|
|
91
|
+
points: curve,
|
|
92
|
+
// Closed along the baseline rather than along the foot of the box: a run entirely above
|
|
93
|
+
// water fills the gap between itself and zero, and nothing below it.
|
|
94
|
+
area: [...curve, [curve[curve.length - 1][0], zeroY], [curve[0][0], zeroY]],
|
|
95
|
+
zeroY,
|
|
96
|
+
min,
|
|
97
|
+
max,
|
|
98
|
+
from: firstTime,
|
|
99
|
+
to: lastTime,
|
|
100
|
+
last,
|
|
101
|
+
positive: last >= 0,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/// Paint a laid-out curve: the baseline, the filled area under it, then the line itself.
|
|
105
|
+
export function drawPnlCurve(ctx, curve, box, style) {
|
|
106
|
+
const colour = curve.positive ? style.up : style.down;
|
|
107
|
+
ctx.clearRect(0, 0, box.width, box.height);
|
|
108
|
+
ctx.setLineDash([2, 3]);
|
|
109
|
+
ctx.strokeStyle = style.baseline;
|
|
110
|
+
ctx.lineWidth = 1;
|
|
111
|
+
ctx.beginPath();
|
|
112
|
+
ctx.moveTo(box.padX, curve.zeroY);
|
|
113
|
+
ctx.lineTo(box.width - box.padX, curve.zeroY);
|
|
114
|
+
ctx.stroke();
|
|
115
|
+
ctx.setLineDash([]);
|
|
116
|
+
ctx.globalAlpha = style.fillOpacity;
|
|
117
|
+
ctx.fillStyle = colour;
|
|
118
|
+
ctx.beginPath();
|
|
119
|
+
ctx.moveTo(curve.area[0][0], curve.area[0][1]);
|
|
120
|
+
for (const [px, py] of curve.area.slice(1))
|
|
121
|
+
ctx.lineTo(px, py);
|
|
122
|
+
ctx.closePath();
|
|
123
|
+
ctx.fill();
|
|
124
|
+
ctx.globalAlpha = 1;
|
|
125
|
+
ctx.strokeStyle = colour;
|
|
126
|
+
ctx.lineWidth = style.lineWidth;
|
|
127
|
+
ctx.beginPath();
|
|
128
|
+
ctx.moveTo(curve.points[0][0], curve.points[0][1]);
|
|
129
|
+
for (const [px, py] of curve.points.slice(1))
|
|
130
|
+
ctx.lineTo(px, py);
|
|
131
|
+
ctx.stroke();
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=pnl-curve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pnl-curve.js","sourceRoot":"","sources":["../../src/pnl-curve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// Strategy statistics — multi-instance.
|
|
2
|
+
//
|
|
3
|
+
// A read-only table of everything a strategy reports about itself: net profit, drawdown, trade
|
|
4
|
+
// counts, latencies. One row per parameter, grouped by the area it belongs to.
|
|
5
|
+
//
|
|
6
|
+
// The desktop grid derives its rows by reflecting over the strategy's statistic parameters, and
|
|
7
|
+
// a browser has no equivalent - so the rows arrive from the consumer already resolved, name and
|
|
8
|
+
// description translated, with the two things that decide where a row sits: a stable category
|
|
9
|
+
// key and the registry order. Both matter. Grouping on the translated category would regroup
|
|
10
|
+
// the table when the language changes, and sorting by name or by value would scatter parameters
|
|
11
|
+
// that are read together.
|
|
12
|
+
import { makeElement, makeIconButton, makePanelId, makePanelRoot } from './dom.js';
|
|
13
|
+
import { ControlTypes } from './control-types.js';
|
|
14
|
+
import { makeGridMenu } from './grid-menu.js';
|
|
15
|
+
import { assertHost } from './trading-host.js';
|
|
16
|
+
import { DataGrid } from '@stocksharp/grids/data-grid';
|
|
17
|
+
/// Give every category the place of its first parameter.
|
|
18
|
+
///
|
|
19
|
+
/// Taken from the rows rather than assumed: the registry bands its orders by category today
|
|
20
|
+
/// - profit below a hundred, trades from a hundred - but a consumer numbering them some
|
|
21
|
+
/// other way still gets its groups in the order it asked for.
|
|
22
|
+
function rank(rows) {
|
|
23
|
+
const first = new Map();
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
const seen = first.get(row.category);
|
|
26
|
+
if (seen === undefined || row.order < seen)
|
|
27
|
+
first.set(row.category, row.order);
|
|
28
|
+
}
|
|
29
|
+
return rows.map(row => ({ ...row, categoryRank: first.get(row.category) ?? row.order }));
|
|
30
|
+
}
|
|
31
|
+
export class StatisticsWidget {
|
|
32
|
+
static create(hostEl, state, deps) {
|
|
33
|
+
const host = assertHost(deps?.host, 'StatisticsWidget');
|
|
34
|
+
const root = StatisticsWidget._buildRoot(host);
|
|
35
|
+
root.id = makePanelId(StatisticsWidget.TYPE);
|
|
36
|
+
hostEl.appendChild(root);
|
|
37
|
+
return new StatisticsWidget(root, state || {}, deps);
|
|
38
|
+
}
|
|
39
|
+
// The panel's markup. No rail action but the export: a statistic is produced by the
|
|
40
|
+
// strategy and there is nothing here to cancel, reload or edit.
|
|
41
|
+
static _buildRoot(host) {
|
|
42
|
+
const title = host.t('Statistics');
|
|
43
|
+
return makePanelRoot('statistics-panel', title, [
|
|
44
|
+
makeElement('div', 'panel-header', {}, [
|
|
45
|
+
makeElement('span', '', {}, [title]),
|
|
46
|
+
makeIconButton('bt-icon-btn bt-icon-cancel panel-close-btn', host.t('ClosePanel'), 'bi-x', { type: 'button' }),
|
|
47
|
+
]),
|
|
48
|
+
makeElement('div', 'panel-body panel-body-with-rail', {}, [
|
|
49
|
+
makeElement('div', 'panel-body-content', {}, [
|
|
50
|
+
makeElement('table', 'terminal-table statistics-table', { role: 'table', 'aria-label': host.t('StatisticsList') }, [
|
|
51
|
+
makeElement('thead', '', {}, []),
|
|
52
|
+
makeElement('tbody', 'statistics-body', {}, []),
|
|
53
|
+
]),
|
|
54
|
+
]),
|
|
55
|
+
makeElement('div', 'panel-rail', { role: 'toolbar', 'aria-label': host.t('StatisticsActions') }, [
|
|
56
|
+
makeIconButton('bt-icon-btn panel-export-btn', host.t('ExportToExcel'), 'bi-file-earmark-spreadsheet', {}),
|
|
57
|
+
]),
|
|
58
|
+
]),
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
constructor(rootEl, _state, deps) {
|
|
62
|
+
this._host = assertHost(deps?.host, 'StatisticsWidget');
|
|
63
|
+
this.rootEl = rootEl;
|
|
64
|
+
this.el = this.rootEl.querySelector('.statistics-body');
|
|
65
|
+
this._closeBtn = this.rootEl.querySelector('.panel-close-btn');
|
|
66
|
+
this._exportBtn = this.rootEl.querySelector('.panel-export-btn');
|
|
67
|
+
this._rows = [];
|
|
68
|
+
this._closeBtn?.addEventListener('click', (e) => {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
this._host.close();
|
|
71
|
+
});
|
|
72
|
+
this._exportBtn?.addEventListener('click', (e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
this._export();
|
|
75
|
+
});
|
|
76
|
+
const head = this.rootEl.querySelector('.statistics-table thead');
|
|
77
|
+
this._grid = head && this.el
|
|
78
|
+
? new DataGrid({
|
|
79
|
+
head: head,
|
|
80
|
+
body: this.el,
|
|
81
|
+
columns: this._columns(),
|
|
82
|
+
// Not a sort the reader chose: the registry hands every parameter an order, and
|
|
83
|
+
// the bands it assigns are what puts profit above drawdown above trade counts.
|
|
84
|
+
defaultSort: { col: 'order', dir: 'asc' },
|
|
85
|
+
rowKey: (r) => String(r.key),
|
|
86
|
+
emptyText: this._host.t('NoStatistics'),
|
|
87
|
+
contextMenu: makeGridMenu(this._host),
|
|
88
|
+
// The groups sit where the registry order puts them, not where the
|
|
89
|
+
// alphabet would: profit, then trades, then positions, then orders.
|
|
90
|
+
groupOrder: 'rows',
|
|
91
|
+
selection: 'multi',
|
|
92
|
+
})
|
|
93
|
+
: null;
|
|
94
|
+
// Grouped on the key, never on the caption: the caption is translated and would
|
|
95
|
+
// regroup the table under the reader's language. Neither the key nor the order is a
|
|
96
|
+
// column anyone reads, so both are hidden - declared, so the grid can group and sort
|
|
97
|
+
// on them, and out of the way.
|
|
98
|
+
this._grid?.setState({ group: 'category', hidden: ['category', 'order'] });
|
|
99
|
+
this._host.register(this);
|
|
100
|
+
}
|
|
101
|
+
dispose() {
|
|
102
|
+
this._grid?.destroy();
|
|
103
|
+
this._host.unregister(this);
|
|
104
|
+
try {
|
|
105
|
+
this.rootEl.remove();
|
|
106
|
+
}
|
|
107
|
+
catch { /* already detached */ }
|
|
108
|
+
}
|
|
109
|
+
/// Show these statistics. The whole set at once: a strategy publishes its parameters as one
|
|
110
|
+
/// table and a row that vanished from it has stopped existing, not stopped changing.
|
|
111
|
+
update(rows) {
|
|
112
|
+
this._rows = rank(rows || []);
|
|
113
|
+
this._grid?.setRows(this._rows);
|
|
114
|
+
}
|
|
115
|
+
_export() {
|
|
116
|
+
this._grid?.download('statistics', this._host.t('Statistics'));
|
|
117
|
+
}
|
|
118
|
+
// Two visible columns and one that only groups. The order column is hidden as well: it
|
|
119
|
+
// decides the sort and means nothing to a reader.
|
|
120
|
+
_columns() {
|
|
121
|
+
const label = (key) => this._host.t(key);
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
key: 'category',
|
|
125
|
+
header: label('Category'),
|
|
126
|
+
exportable: false,
|
|
127
|
+
// The group's place, not its name: groups are laid out in the order the
|
|
128
|
+
// registry gives their parameters, so profit comes before trade counts.
|
|
129
|
+
// Grouping on the name would order them alphabetically, and on the
|
|
130
|
+
// translated name would reorder them with the reader's language.
|
|
131
|
+
value: (r) => r.categoryRank,
|
|
132
|
+
text: (r) => r.categoryText || r.category,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
key: 'order',
|
|
136
|
+
header: label('Order'),
|
|
137
|
+
exportable: false,
|
|
138
|
+
value: (r) => r.order,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
key: 'name',
|
|
142
|
+
header: label('Name'),
|
|
143
|
+
exportable: true,
|
|
144
|
+
value: (r) => r.name,
|
|
145
|
+
bindCell: (td, r) => {
|
|
146
|
+
if (r.description)
|
|
147
|
+
td.setAttribute('title', r.description);
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: 'value',
|
|
152
|
+
header: label('Value'),
|
|
153
|
+
exportable: true,
|
|
154
|
+
cellClass: () => 'statistic-value',
|
|
155
|
+
// Sorts on the raw value so a number sorts as a number, reads as the text below.
|
|
156
|
+
value: (r) => r.value,
|
|
157
|
+
render: (r) => formatStatistic(r.value),
|
|
158
|
+
exportValue: (r) => r.value ?? '',
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
StatisticsWidget.TYPE = ControlTypes.Statistics;
|
|
164
|
+
/// A statistic as text.
|
|
165
|
+
///
|
|
166
|
+
/// Three shapes reach this: a number, a moment, and everything else. A number is rounded to two
|
|
167
|
+
/// places and shown without trailing zeros - a profit of 11055.75 and a count of 1340 both read
|
|
168
|
+
/// the way they are meant to. A moment is a date: these are run-level facts (the day of the
|
|
169
|
+
/// maximum drawdown), and the hour would be noise. Anything with no value yet is a blank cell
|
|
170
|
+
/// rather than a zero, which would read as a measured nothing.
|
|
171
|
+
export function formatStatistic(value) {
|
|
172
|
+
if (value === null || value === undefined || value === '')
|
|
173
|
+
return '';
|
|
174
|
+
if (typeof value === 'number') {
|
|
175
|
+
if (!isFinite(value))
|
|
176
|
+
return '';
|
|
177
|
+
return String(Math.round(value * 100) / 100);
|
|
178
|
+
}
|
|
179
|
+
if (value instanceof Date)
|
|
180
|
+
return isoDate(value);
|
|
181
|
+
if (typeof value === 'string') {
|
|
182
|
+
// A date only when it is unambiguously one: a bare number in a string stays a number,
|
|
183
|
+
// and a name stays a name.
|
|
184
|
+
const at = /^\d{4}-\d{2}-\d{2}([T ]|$)/.test(value) ? new Date(value) : null;
|
|
185
|
+
if (at !== null && !isNaN(at.getTime()))
|
|
186
|
+
return isoDate(at);
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
return String(value);
|
|
190
|
+
}
|
|
191
|
+
function isoDate(at) {
|
|
192
|
+
return at.toISOString().slice(0, 10);
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=statistics-widget.js.map
|