@tony01/astroneum 0.4.1-beta.2 → 0.4.1-beta.24

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 (57) hide show
  1. package/CHANGELOG.md +280 -237
  2. package/LICENSE +21 -21
  3. package/README.md +250 -198
  4. package/dist/{Indicator-BBI3L_lx.d.ts → Indicator-BQQcVGGT.d.ts} +51 -227
  5. package/dist/astroneum.css +1 -1
  6. package/dist/chunk-47YGQEL5.js +399 -0
  7. package/dist/chunk-67XM5LOK.js +2 -0
  8. package/dist/{chunk-2YYGUSVX.js → chunk-DFJMDDL5.js} +1 -1
  9. package/dist/chunk-EFAS6F4C.js +59 -0
  10. package/dist/chunk-EHZN52CY.js +2 -0
  11. package/dist/chunk-HKLWNPMP.js +35 -0
  12. package/dist/chunk-ONPPQW65.js +2 -0
  13. package/dist/chunk-SCTHEM27.js +3 -0
  14. package/dist/chunk-UQXYMB6X.js +2 -0
  15. package/dist/chunk-WSIR5T2M.js +3 -0
  16. package/dist/entries/alerts.d.ts +36 -10
  17. package/dist/entries/alerts.js +1 -1
  18. package/dist/entries/datafeeds/crypto.d.ts +5 -1
  19. package/dist/entries/datafeeds/crypto.js +1 -1
  20. package/dist/entries/datafeeds/polygon.d.ts +3 -1
  21. package/dist/entries/datafeeds/polygon.js +1 -1
  22. package/dist/entries/headless.d.ts +78 -0
  23. package/dist/entries/headless.js +2 -0
  24. package/dist/entries/multichart.d.ts +12 -1
  25. package/dist/entries/multichart.js +1 -1
  26. package/dist/entries/replay.d.ts +3 -1
  27. package/dist/entries/script.d.ts +2 -68
  28. package/dist/entries/script.js +1 -1
  29. package/dist/entries/watchlist.d.ts +32 -2
  30. package/dist/entries/watchlist.js +1 -1
  31. package/dist/entries/workspace.d.ts +61 -0
  32. package/dist/entries/workspace.js +2 -0
  33. package/dist/index-BiXdqLA4.d.ts +45 -0
  34. package/dist/index-_JgHTy8X.d.ts +19 -0
  35. package/dist/index.d.ts +188 -27
  36. package/dist/index.js +1 -1
  37. package/dist/script-BU9StbN-.d.ts +120 -0
  38. package/dist/types-W6SIYQ9m.d.ts +232 -0
  39. package/dist/types-yETgeYsE.d.ts +28 -0
  40. package/dist/zh-CN-NDTUDZGZ.js +3 -0
  41. package/docs/MIRROR-PLAN-SYMBOL-SEARCH-WATCHLIST.md +255 -0
  42. package/docs/STRUCTURE.md +51 -37
  43. package/docs/TODO-DESIGN.md +111 -37
  44. package/docs/TODO.md +26 -16
  45. package/docs/WORKSTREAMS.md +55 -0
  46. package/docs/api.md +655 -528
  47. package/docs/datafeed-guide.md +329 -311
  48. package/docs/design-astroneum.md +204 -65
  49. package/docs/plugin-development.md +569 -565
  50. package/docs/tv-functions-skill.md +88 -64
  51. package/package.json +158 -147
  52. package/dist/chunk-4YOLQ5DC.js +0 -2
  53. package/dist/chunk-I4YQBP2M.js +0 -3
  54. package/dist/chunk-OOMZRONH.js +0 -35
  55. package/dist/chunk-PV2GAINM.js +0 -2
  56. package/dist/chunk-YW3STIPY.js +0 -653
  57. package/dist/zh-CN-Y2C6RSOI.js +0 -3
package/docs/api.md CHANGED
@@ -1,528 +1,655 @@
1
- # API Reference
2
-
3
- ## Constructor
4
-
5
- ```typescript
6
- <AstroneumChart ref={ref} {...options} />
7
- ```
8
-
9
- ### `AstroneumOptions`
10
-
11
- | Property | Type | Required | Default | Description |
12
- |---|---|---|---|---|
13
- | `symbol` | `SymbolInfo` | ✓ | — | Symbol to display on load |
14
- | `period` | `Period` | ✓ | — | Initial timeframe |
15
- | `datafeed` | `Datafeed` | ✓ | — | Data source implementation |
16
- | `theme` | `'light' \| 'dark'` | | `getSystemTheme()` | UI theme. Omit to auto-detect OS color scheme. |
17
- | `locale` | `string` | | `'en-US'` | UI locale key |
18
- | `timezone` | `string` | | `'Asia/Shanghai'` | IANA timezone identifier for the X-axis |
19
- | `watermark` | `string \| Node` | | Astroneum logo SVG | Watermark rendered in the chart center |
20
- | `styles` | `DeepPartial<Styles>` | | `{}` | Deep-partial engine style override |
21
- | `drawingBarVisible` | `boolean` | | `true` | Whether the left-side drawing toolbar is shown |
22
- | `periods` | `Period[]` | | `[1m,5m,15m,1H,2H,4H,D,W,M,Y]` | Timeframes available in the period bar |
23
- | `mainIndicators` | `IndicatorDef[]` | | `[{ name: 'EMA', calcParams: [7,25,99] }]` | Indicators overlaid on the main candle pane |
24
- | `subIndicators` | `string[]` | | `['VOL']` | Indicator names rendered in sub-panes below the chart |
25
- | `plugins` | `ChartPlugin[]` | | `[]` | Plugins mounted with chart lifecycle hooks (`onInit` / disposer) |
26
-
27
- ---
28
-
29
- ## Instance methods
30
-
31
- ### `setTheme(theme)`
32
- ```typescript
33
- setTheme(theme: 'light' | 'dark'): void
34
- ```
35
- Sets the UI theme. Updates both the `data-theme` attribute and the internal chart styles.
36
-
37
- ### `getTheme()`
38
- ```typescript
39
- getTheme(): string
40
- ```
41
- Returns the current theme name.
42
-
43
- ### `setStyles(styles)`
44
- ```typescript
45
- setStyles(styles: DeepPartial<Styles>): void
46
- ```
47
- Merges the given partial styles into the engine's current style tree.
48
-
49
- ### `getStyles()`
50
- ```typescript
51
- getStyles(): Styles
52
- ```
53
- Returns the full resolved engine style object.
54
-
55
- ### `setLocale(locale)`
56
- ```typescript
57
- setLocale(locale: string): void
58
- ```
59
- Switches the UI and engine locale. The locale key must have been registered with `loadLocales()` (for UI strings) and the engine's `registerLocale()` (for OHLC/indicator labels).
60
-
61
- ### `getLocale()`
62
- ```typescript
63
- getLocale(): string
64
- ```
65
- Returns the current locale key.
66
-
67
- ### `setTimezone(timezone)`
68
- ```typescript
69
- setTimezone(timezone: string): void
70
- ```
71
- Changes the IANA timezone used for the X-axis date labels.
72
-
73
- ### `getTimezone()`
74
- ```typescript
75
- getTimezone(): string
76
- ```
77
- Returns the current timezone.
78
-
79
- ### `setSymbol(symbol)`
80
- ```typescript
81
- setSymbol(symbol: SymbolInfo): void
82
- ```
83
- Loads a new symbol. Triggers a full data reload via the datafeed.
84
-
85
- ### `getSymbol()`
86
- ```typescript
87
- getSymbol(): SymbolInfo
88
- ```
89
- Returns the currently active `SymbolInfo`.
90
-
91
- ### `setPeriod(period)`
92
- ```typescript
93
- setPeriod(period: Period): void
94
- ```
95
- Switches the active timeframe. Triggers a full data reload.
96
-
97
- ### `getPeriod()`
98
- ```typescript
99
- getPeriod(): Period
100
- ```
101
- Returns the currently active `Period`.
102
-
103
- ### `serializeState()` / `loadState()`
104
-
105
- ```typescript
106
- serializeState(): SerializedChartState
107
- loadState(state: SerializedChartState): void
108
- ```
109
-
110
- Capture and restore the chart's user-visible state — theme, locale,
111
- timezone, symbol, period, styles, indicators, and drawing overlays —
112
- as a JSON-safe object.
113
-
114
- ```ts
115
- // save
116
- localStorage.setItem('chart-state', JSON.stringify(chartRef.current!.serializeState()))
117
-
118
- // restore on mount
119
- const saved = localStorage.getItem('chart-state')
120
- if (saved) chartRef.current!.loadState(JSON.parse(saved))
121
- ```
122
-
123
- `loadState()` is a best-effort restore: missing or unknown indicator /
124
- overlay names are silently skipped (matching the engine's behaviour for
125
- unregistered names). The `version` field lets the format evolve without
126
- breaking older payloads; payloads from a newer format than the current
127
- library understands are ignored.
128
-
129
- ---
130
-
131
- ## Accessibility
132
-
133
- `<AstroneumChart>` accepts an `accessible` prop (default `false`) that
134
- opts the canvas into screen-reader and keyboard-focus support without
135
- changing visuals:
136
-
137
- - The container gets `tabindex=0`, `role="img"`, and an `aria-label`
138
- (defaults to `"<ticker> <period text> chart"`; override via the
139
- `ariaLabel` prop).
140
- - A visually-hidden `aria-live="polite"` region announces OHLCV on
141
- crosshair changes, throttled to ~10 updates / second to avoid
142
- flooding screen-reader queues.
143
-
144
- ```tsx
145
- <AstroneumChart
146
- symbol={{ ticker: 'BTC-USD' }}
147
- period={{ multiplier: 1, timespan: 'minute', text: '1m' }}
148
- datafeed={datafeed}
149
- accessible
150
- ariaLabel='Bitcoin / USD 1-minute candles'
151
- />
152
- ```
153
-
154
- For users who require a high-contrast UI, set `theme="high-contrast"`
155
- (or call `setTheme('high-contrast')`). The bundled theme uses a
156
- black background, white candles/grid, and yellow accents with strong
157
- focus rings.
158
-
159
- ---
160
-
161
- ## Lazy locales
162
-
163
- Only `en-US` is bundled with the main entry. Load any other built-in
164
- locale on demand:
165
-
166
- ```ts
167
- import { loadLocale, BUILTIN_LOCALES } from '@tony01/astroneum'
168
-
169
- await loadLocale('ja-JP')
170
- chartRef.current?.setLocale('ja-JP')
171
-
172
- console.log(BUILTIN_LOCALES) // ['en-US', 'zh-CN', 'ja-JP', ...]
173
- ```
174
-
175
- `loadLocale(key)` resolves with the dictionary on success, or `null`
176
- when `key` is not a built-in locale. Concurrent calls for the same
177
- locale are de-duplicated. To register a fully custom locale,
178
- keep using `loadLocales(key, dictionary)`.
179
-
180
- ---
181
-
182
- ## Subpath entry points
183
-
184
- Optional features are reachable through dedicated subpath imports so a
185
- consumer can pull in just what they need.
186
-
187
- | Subpath | Exports |
188
- | --- | --- |
189
- | `astroneum/replay` | `BarReplay`, types `BarReplayOptions`, `BarReplayState` |
190
- | `astroneum/multichart` | `MultiChartLayout`, types `MultiChartCount`, `MultiChartSlot`, `MultiChartLayoutOptions` |
191
- | `astroneum/watchlist` | `WatchlistManager`, types `Watchlist`, `WatchSymbol` |
192
- | `astroneum/portfolio` | `PortfolioTracker`, types `Position`, `PositionSide`, `PnLResult` |
193
- | `astroneum/alerts` | `AlertManager`, full alert type surface |
194
- | `astroneum/script` | `ScriptEngine`, types `CompiledIndicator`, `StudyOptions`, `PlotOptions`, `InputOptions` |
195
- | `astroneum/datafeeds/polygon` | `DefaultDatafeed`, `WebSocketDatafeed`, `WebSocketDatafeedOptions` |
196
- | `astroneum/datafeeds/crypto` | `createStandardCryptoDatafeed`, `StandardCryptoDatafeed`, `STANDARD_CRYPTO_SYMBOLS`, `DATAFEED_ERROR_EVENT`, `BinanceAdapter`, `BitgetAdapter`, `OkxAdapter`, plus types |
197
-
198
- All listed symbols also re-export from the root `astroneum` entry today,
199
- but the root re-exports for these modules will be removed in **v1.0**
200
- (see the [Roadmap](../README.md#v10--stability)). Migrate to the subpath
201
- import to be forward-compatible.
202
-
203
- ```ts
204
- // recommended — forward-compatible with v1.0
205
- import { BarReplay } from '@tony01/astroneum/replay'
206
- import { createStandardCryptoDatafeed } from '@tony01/astroneum/datafeeds/crypto'
207
-
208
- // legacy — works today, will be removed in v1.0
209
- import { BarReplay, createStandardCryptoDatafeed } from '@tony01/astroneum'
210
- ```
211
-
212
- ---
213
-
214
- ## Exported utilities
215
-
216
- ### Brand cast helpers
217
-
218
- ```typescript
219
- import { asPrice, asVolume, asTimestamp } from '@tony01/astroneum'
220
-
221
- const p: Price = asPrice(100.50)
222
- const v: Volume = asVolume(1_250_000)
223
- const t: Timestamp = asTimestamp(Date.now())
224
- ```
225
-
226
- These are type-safe cast functions. They assert that a plain `number` belongs to a specific financial domain without any runtime overhead.
227
-
228
- ### `rafCoalesce`
229
-
230
- ```typescript
231
- import { rafCoalesce } from '@tony01/astroneum'
232
-
233
- const dispatch = rafCoalesce((tick: CandleData) => chart.updateBar(tick))
234
- webSocket.onmessage = e => dispatch(parse(e.data)) // safe to call 100×/s
235
- ```
236
-
237
- Returns a dispatcher that schedules `fn` on the next `requestAnimationFrame`, coalescing rapid calls so only the **last** value is delivered per frame. Use this when you only care about the latest state (e.g. price display). For financial accuracy across bursts, use `rafMergeTick` instead.
238
-
239
- ### `rafMergeTick`
240
-
241
- ```typescript
242
- import { rafMergeTick } from '@tony01/astroneum'
243
-
244
- const cancel = rafMergeTick(incomingTick, (merged) => {
245
- chart.updateBar(merged)
246
- })
247
- // Call cancel() to stop
248
- ```
249
-
250
- OHLCV-aware frame coalescer for WebSocket burst handling. Merges all ticks arriving within the same `requestAnimationFrame` frame into one financially accurate bar:
251
- - `open` = first tick
252
- - `high` = max across ticks
253
- - `low` = min across ticks
254
- - `close` = last tick
255
- - `volume` = sum across ticks
256
-
257
- ### `EventBus`
258
-
259
- ```typescript
260
- import { EventBus } from '@tony01/astroneum'
261
- import type { ChartEventMap } from '@tony01/astroneum'
262
-
263
- const bus = new EventBus<ChartEventMap>()
264
-
265
- bus.on('tick', (candle) => console.log(candle.close))
266
- bus.emit('tick', bar)
267
- bus.off('tick', handler)
268
- ```
269
-
270
- Zero-dependency, type-safe synchronous event bus.
271
-
272
- ### `TickAnimator`
273
-
274
- ```typescript
275
- import { TickAnimator } from '@tony01/astroneum'
276
- import type { TickAnimatorOptions } from '@tony01/astroneum'
277
-
278
- const opts: TickAnimatorOptions = { duration: 120 } // ms
279
- const animator = new TickAnimator(opts, (frame) => {
280
- chart.updateLastBar(frame)
281
- })
282
-
283
- animator.push(newTick)
284
- animator.cancel()
285
- ```
286
-
287
- Smooth ease-out cubic interpolation of the last bar's `close`/`high`/`low` over `duration` ms (default 120 ms). Zero allocations in the rAF loop.
288
-
289
- ### `RingBuffer`
290
-
291
- ```typescript
292
- import { RingBuffer } from '@tony01/astroneum'
293
-
294
- const buf = new RingBuffer<CandleData>(1000) // fixed capacity
295
- buf.push(candle)
296
- const arr = buf.toArray() // oldest → newest
297
- ```
298
-
299
- Fixed-capacity O(1) circular buffer designed for streaming time-series data.
300
-
301
- ---
302
-
303
- ## Exported types
304
-
305
- ```typescript
306
- import type {
307
- // Core
308
- AstroneumOptions,
309
- AstroneumHandle,
310
- SerializedChartState,
311
- Datafeed,
312
- SymbolInfo,
313
- Period,
314
- CandleData,
315
- DatafeedSubscribeCallback,
316
-
317
- // Branded primitives
318
- Price,
319
- Volume,
320
- Timestamp,
321
-
322
- // Indicator
323
- IndicatorDef,
324
- IndicatorPlugin,
325
- ChartPlugin,
326
- ChartPluginContext,
327
-
328
- // Events
329
- ChartEventMap,
330
-
331
- // Coordinate space
332
- Viewport,
333
-
334
- // Engine types
335
- Styles,
336
- DeepPartial,
337
- Coordinate,
338
- Bounding,
339
-
340
- // Utility types
341
- TickAnimatorOptions,
342
- TimeframeText,
343
- Timespan,
344
-
345
- // MultiChartLayout
346
- MultiChartCount,
347
- MultiChartSlot,
348
- MultiChartLayoutOptions,
349
-
350
- // BarReplay
351
- BarReplayOptions,
352
- BarReplayState,
353
-
354
- // Drawing templates
355
- OverlayStylePreset,
356
- DrawingStyleTemplate,
357
-
358
- // AlertManager
359
- Alert,
360
- AlertCondition,
361
- AlertStatus,
362
- AlertFrequency,
363
- AlertCreate,
364
- AlertCheckInput,
365
- AlertTriggeredCallback,
366
-
367
- // Format helpers
368
- FormatPriceOptions,
369
- DateFormatStyle,
370
- TimeFormatStyle,
371
-
372
- // ScriptEngine
373
- CompiledIndicator,
374
- StudyOptions,
375
- PlotOptions,
376
- InputOptions,
377
-
378
- // WatchlistManager
379
- Watchlist,
380
- WatchSymbol,
381
-
382
- // PortfolioTracker
383
- Position,
384
- PositionSide,
385
- PnLResult,
386
-
387
- // PerformanceMode
388
- PerformanceBar,
389
- } from '@tony01/astroneum'
390
- ```
391
-
392
- ### `IndicatorDef`
393
-
394
- ```typescript
395
- interface IndicatorDef {
396
- /** Registered indicator name, e.g. 'EMA', 'MA', 'RSI' */
397
- name: string
398
- /** Calculation parameters passed to the indicator engine */
399
- calcParams?: number[]
400
- }
401
- ```
402
-
403
- ### `IndicatorPlugin<TOutput>`
404
-
405
- ```typescript
406
- interface IndicatorPlugin<TOutput> {
407
- name: string
408
- shortName?: string
409
- calcParams?: number[]
410
- calc(data: CandleData[], params: number[]): TOutput[]
411
- render2D?(ctx: CanvasRenderingContext2D, output: TOutput[], viewport: Viewport): void
412
- renderGL?(gl: WebGL2RenderingContext, output: TOutput[], viewport: Viewport, vbo: WebGLBuffer): void
413
- }
414
- ```
415
-
416
- Implement this interface to register a custom typed indicator.
417
- Use `render2D` for lightweight overlays (< 10k points) and `renderGL` for high-density or GPU-accelerated rendering.
418
- When `renderGL` is present, Astroneum executes it on a dedicated WebGL2 layer and reuses a per-indicator `vbo`.
419
- If WebGL2 is unavailable, `render2D` is used as fallback when available.
420
-
421
- ### `registerIndicatorPlugin(plugin)`
422
-
423
- ```typescript
424
- registerIndicatorPlugin<TOutput>(plugin: IndicatorPlugin<TOutput>): void
425
- ```
426
-
427
- Adapts `IndicatorPlugin` to the engine `IndicatorTemplate` format and registers it.
428
-
429
- ### `registerIndicatorPlugins(plugins)`
430
-
431
- ```typescript
432
- registerIndicatorPlugins(plugins: IndicatorPlugin<unknown>[]): void
433
- ```
434
-
435
- Batch helper for registering multiple indicator plugins.
436
-
437
- ### `createIndicatorTemplateFromPlugin(plugin)`
438
-
439
- ```typescript
440
- createIndicatorTemplateFromPlugin<TOutput>(plugin: IndicatorPlugin<TOutput>): IndicatorTemplate
441
- ```
442
-
443
- Returns the converted engine template without registering it. Useful when you want manual control over registration.
444
-
445
- ### `ChartPlugin`
446
-
447
- ```typescript
448
- interface ChartPlugin {
449
- name?: string
450
- indicators?: IndicatorPlugin<unknown>[]
451
- onInit?: (context: ChartPluginContext) => void | (() => void)
452
- }
453
- ```
454
-
455
- `indicators` are registered before chart indicator creation begins. `onInit` runs once after chart initialization and may return a disposer called during chart teardown.
456
-
457
- ### `ChartPluginContext`
458
-
459
- ```typescript
460
- interface ChartPluginContext {
461
- chart: Chart
462
- registerIndicatorPlugin: (plugin: IndicatorPlugin<unknown>) => void
463
- registerIndicatorPlugins: (plugins: IndicatorPlugin<unknown>[]) => void
464
- }
465
- ```
466
-
467
- ### `ChartEventMap`
468
-
469
- ```typescript
470
- interface ChartEventMap {
471
- 'symbol-change': SymbolInfo
472
- 'period-change': Period
473
- 'crosshair-move': Coordinate | null
474
- 'zoom': { scale: number; anchor: ZoomAnchor }
475
- 'data-load': { type: 'init' | 'forward' | 'backward'; count: number }
476
- 'tick': CandleData
477
- 'drawing-start': string
478
- 'drawing-end': string
479
- 'theme-change': string
480
- }
481
- ```
482
-
483
- ### `Viewport`
484
-
485
- ```typescript
486
- interface Viewport {
487
- priceMin: Price
488
- priceMax: Price
489
- timeMin: Timestamp
490
- timeMax: Timestamp
491
- resolution: readonly [width: number, height: number]
492
- }
493
- ```
494
-
495
- Represents the currently visible region of the chart canvas. Used for coordinate-space transforms in custom `IndicatorPlugin` renderers.
496
-
497
- ---
498
-
499
- ## Additional exports
500
-
501
- | Export | Description |
502
- |---|---|
503
- | `DefaultDatafeed` | Polygon.io REST + WebSocket datafeed |
504
- | `MultiChartLayout` | Multi-chart layout manager |
505
- | `BarReplay` | Historical bar replay engine |
506
- | `DrawingTemplates` | Save/load drawing style templates |
507
- | `AlertManager` | Price alert creation and monitoring |
508
- | `ScriptEngine` | Pine-like indicator scripting sandbox (`compile()` auto-registers indicators) |
509
- | `registerIndicator` | Register a raw engine `IndicatorTemplate` |
510
- | `getSupportedIndicators` | List all registered indicator names |
511
- | `registerOverlay` | Register a raw engine `OverlayTemplate` |
512
- | `getSupportedOverlays` | List all registered overlay names |
513
- | `registerXAxis` / `registerYAxis` | Register custom axis templates |
514
- | `registerIndicatorPlugin` | Register a typed `IndicatorPlugin` via adapter |
515
- | `registerIndicatorPlugins` | Batch register typed indicator plugins |
516
- | `createIndicatorTemplateFromPlugin` | Convert `IndicatorPlugin` to engine template |
517
- | `WatchlistManager` | Symbol watchlist with live prices |
518
- | `PortfolioTracker` | Position tracking and P&L calculation |
519
- | `PerformanceMode` | Reduced-render performance optimization |
520
- | `loadLocales` | Register UI locale strings |
521
- | `formatPrice` | Format a price number using symbol precision |
522
- | `formatVolume` | Format a volume number |
523
- | `formatPercent` | Format a percentage value |
524
- | `formatDate` / `formatTime` / `formatDateTime` | Date/time formatting helpers |
525
- | `formatDuration` | Format a duration in milliseconds as human-readable text |
526
- | `formatPeriod` | Format a `Period` as a human-readable string (e.g. `'1H'`) |
527
- | `detectPricePrecision` | Auto-detect decimal precision from price data |
528
-
1
+ # API Reference
2
+
3
+ ## Strategy backtesting
4
+
5
+ `runBacktest(bars, signals, config)` is a deterministic, bar-close simulation
6
+ API. It returns `BacktestResult`, containing normalized trades, equity and
7
+ drawdown points, and summary metrics. It does not place orders, persist data,
8
+ or connect to brokers.
9
+
10
+ `ScriptEngine.compileStrategy(source)` is intentionally bounded: scripts define
11
+ `strategySignals({ open, high, low, close, volume, ta })` and return an array
12
+ of `{ index, action: 'enter' | 'exit', side?: 'long' | 'short' }`. Its runner
13
+ uses the same `runBacktest` core. It is not Pine-compatible `strategy.*`.
14
+
15
+ ## Headless engine
16
+
17
+ The supported `@tony01/astroneum/headless` entry exposes the chart renderer
18
+ without React, native Astroneum UI, stylesheets, datafeeds, keyboard shortcuts,
19
+ or implicit network behavior.
20
+
21
+ ```typescript
22
+ import {
23
+ asPrice,
24
+ asTimestamp,
25
+ createChart,
26
+ destroyChart,
27
+ registerBuiltInExtensions,
28
+ } from '@tony01/astroneum/headless'
29
+
30
+ registerBuiltInExtensions()
31
+ const chart = createChart(container, { timezone: 'UTC', styles: 'dark' })
32
+
33
+ chart.replaceBars([{
34
+ timestamp: asTimestamp(1_788_000_000_000),
35
+ open: asPrice(100),
36
+ high: asPrice(105),
37
+ low: asPrice(98),
38
+ close: asPrice(103),
39
+ }])
40
+
41
+ chart.updateBar({
42
+ timestamp: asTimestamp(1_788_000_060_000),
43
+ open: asPrice(103),
44
+ high: asPrice(106),
45
+ low: asPrice(102),
46
+ close: asPrice(104),
47
+ })
48
+
49
+ destroyChart(chart)
50
+ ```
51
+
52
+ `createChart(container, options?)` owns one chart per supplied element. A second
53
+ call for the same element is idempotent and returns the existing instance;
54
+ options from the duplicate call are ignored. A container marked by another
55
+ Astroneum module instance fails visibly rather than mounting duplicate DOM.
56
+ `destroyChart(chartOrContainer)` is idempotent and releases all registry ownership.
57
+
58
+ `replaceBars(bars)` validates the full input before mutation and requires finite,
59
+ internally consistent OHLCV values with unique, strictly ascending safe-integer
60
+ UTC-millisecond timestamps. `updateBar(bar)` accepts only the current last
61
+ timestamp or a newer timestamp. Both methods copy accepted data, reject rather
62
+ than repair invalid input, and switch the chart to host-owned data by detaching
63
+ any explicitly configured engine loader. `getBars()` returns a copy.
64
+
65
+ `HeadlessChart` also exposes viewport, coordinate conversion, pane, indicator,
66
+ overlay, event, and resize primitives. `getViewportState()` returns the JSON-safe
67
+ version-1 shape `{ version, barSpace, rightOffsetBarCount }`.
68
+ `setViewportState()` accepts only version 1, finite bar spacing from 1 through 50,
69
+ and a finite right-offset bar count; the offset is clamped to the current bars and
70
+ container geometry. Apply recovery state after bars load. It is engine recovery
71
+ data, not semantic chart state.
72
+
73
+ Optional built-in indicators and drawings use process-wide registries and are
74
+ installed only after `registerBuiltInExtensions()` is called. The headless entry
75
+ also exports typed `registerIndicatorPlugin()` helpers plus raw
76
+ `registerIndicator()` and `registerOverlay()` primitives for host-owned outputs.
77
+ Browser coverage carries 16 study series and 16 overlay instances. Screenshot
78
+ export composites ordered Canvas2D and accelerated canvas layers; browser pixel
79
+ coverage verifies output dimensions and candle pixels from the accelerated layer.
80
+
81
+ `HEADLESS_CAPABILITIES` is the immutable version-1 runtime capability manifest.
82
+ The entry imports safely when browser globals are absent and does not require the
83
+ package stylesheet. Destroy cancels active chart animations, unsubscribes an
84
+ explicit loader, and rejects late loader callbacks before they mutate state.
85
+
86
+ ## Constructor
87
+
88
+ ```typescript
89
+ <AstroneumChart ref={ref} {...options} />
90
+ ```
91
+
92
+ ### `AstroneumOptions`
93
+
94
+ | Property | Type | Required | Default | Description |
95
+ |---|---|---|---|---|
96
+ | `symbol` | `SymbolInfo` | ✓ | — | Symbol to display on load |
97
+ | `period` | `Period` | ✓ | — | Initial timeframe |
98
+ | `datafeed` | `Datafeed` | ✓ | — | Data source implementation |
99
+ | `theme` | `'light' \| 'dark'` | | `getSystemTheme()` | UI theme. Omit to auto-detect OS color scheme. |
100
+ | `locale` | `string` | | `'en-US'` | UI locale key |
101
+ | `timezone` | `string` | | `'Asia/Shanghai'` | IANA timezone identifier for the X-axis |
102
+ | `watermark` | `string \| Node` | | Astroneum logo SVG | Watermark rendered in the chart center |
103
+ | `styles` | `DeepPartial<Styles>` | | `{}` | Deep-partial engine style override |
104
+ | `drawingBarVisible` | `boolean` | | `true` | Whether the left-side drawing toolbar is shown |
105
+ | `periods` | `Period[]` | | `[1m,5m,15m,1H,2H,4H,D,W,M,Y]` | Timeframes available in the period bar |
106
+ | `mainIndicators` | `IndicatorDef[]` | | `[{ name: 'EMA', calcParams: [7,25,99] }]` | Indicators overlaid on the main candle pane |
107
+ | `subIndicators` | `string[]` | | `['VOL']` | Indicator names rendered in sub-panes below the chart |
108
+ | `plugins` | `ChartPlugin[]` | | `[]` | Plugins mounted with chart lifecycle hooks (`onInit` / disposer) |
109
+
110
+ ---
111
+
112
+ ## Instance methods
113
+
114
+ ### `setTheme(theme)`
115
+ ```typescript
116
+ setTheme(theme: 'light' | 'dark'): void
117
+ ```
118
+ Sets the UI theme. Updates both the `data-theme` attribute and the internal chart styles.
119
+
120
+ ### `getTheme()`
121
+ ```typescript
122
+ getTheme(): string
123
+ ```
124
+ Returns the current theme name.
125
+
126
+ ### `setStyles(styles)`
127
+ ```typescript
128
+ setStyles(styles: DeepPartial<Styles>): void
129
+ ```
130
+ Merges the given partial styles into the engine's current style tree.
131
+
132
+ ### `getStyles()`
133
+ ```typescript
134
+ getStyles(): Styles
135
+ ```
136
+ Returns the full resolved engine style object.
137
+
138
+ ### `setLocale(locale)`
139
+ ```typescript
140
+ setLocale(locale: string): void
141
+ ```
142
+ Switches the UI and engine locale. The locale key must have been registered with `loadLocales()` (for UI strings) and the engine's `registerLocale()` (for OHLC/indicator labels).
143
+
144
+ ### `getLocale()`
145
+ ```typescript
146
+ getLocale(): string
147
+ ```
148
+ Returns the current locale key.
149
+
150
+ ### `setTimezone(timezone)`
151
+ ```typescript
152
+ setTimezone(timezone: string): void
153
+ ```
154
+ Changes the IANA timezone used for the X-axis date labels.
155
+
156
+ ### `getTimezone()`
157
+ ```typescript
158
+ getTimezone(): string
159
+ ```
160
+ Returns the current timezone.
161
+
162
+ ### `setSymbol(symbol)`
163
+ ```typescript
164
+ setSymbol(symbol: SymbolInfo): void
165
+ ```
166
+ Loads a new symbol. Triggers a full data reload via the datafeed.
167
+
168
+ ### `getSymbol()`
169
+ ```typescript
170
+ getSymbol(): SymbolInfo
171
+ ```
172
+ Returns the currently active `SymbolInfo`.
173
+
174
+ ### `setPeriod(period)`
175
+ ```typescript
176
+ setPeriod(period: Period): void
177
+ ```
178
+ Switches the active timeframe. Triggers a full data reload.
179
+
180
+ ### `getPeriod()`
181
+ ```typescript
182
+ getPeriod(): Period
183
+ ```
184
+ Returns the currently active `Period`.
185
+
186
+ ### `serializeState()` / `loadState()`
187
+
188
+ ```typescript
189
+ serializeState(): SerializedChartState
190
+ loadState(state: SerializedChartState): void
191
+ ```
192
+
193
+ Capture and restore the chart's user-visible state — theme, locale,
194
+ timezone, symbol, period, styles, indicators, and drawing overlays —
195
+ as a JSON-safe object.
196
+ Indicator entries retain their calculation parameters, visibility, and styles.
197
+
198
+ ```ts
199
+ // save
200
+ localStorage.setItem('chart-state', JSON.stringify(chartRef.current!.serializeState()))
201
+
202
+ // restore on mount
203
+ const saved = localStorage.getItem('chart-state')
204
+ if (saved) chartRef.current!.loadState(JSON.parse(saved))
205
+ ```
206
+
207
+ `loadState()` is a best-effort restore: missing or unknown indicator /
208
+ overlay names are silently skipped (matching the engine's behaviour for
209
+ unregistered names). The `version` field lets the format evolve without
210
+ breaking older payloads; payloads from a newer format than the current
211
+ library understands are ignored.
212
+
213
+ ---
214
+
215
+ ## Accessibility
216
+
217
+ `<AstroneumChart>` accepts an `accessible` prop (default `false`) that
218
+ opts the canvas into screen-reader and keyboard-focus support without
219
+ changing visuals:
220
+
221
+ - The container gets `tabindex=0`, `role="img"`, and an `aria-label`
222
+ (defaults to `"<ticker> <period text> chart"`; override via the
223
+ `ariaLabel` prop).
224
+ - A visually-hidden `aria-live="polite"` region announces OHLCV on
225
+ crosshair changes, throttled to ~10 updates / second to avoid
226
+ flooding screen-reader queues.
227
+
228
+ ```tsx
229
+ <AstroneumChart
230
+ symbol={{ ticker: 'BTC-USD' }}
231
+ period={{ multiplier: 1, timespan: 'minute', text: '1m' }}
232
+ datafeed={datafeed}
233
+ accessible
234
+ ariaLabel='Bitcoin / USD 1-minute candles'
235
+ />
236
+ ```
237
+
238
+ For users who require a high-contrast UI, set `theme="high-contrast"`
239
+ (or call `setTheme('high-contrast')`). The bundled theme uses a
240
+ black background, white candles/grid, and yellow accents with strong
241
+ focus rings.
242
+
243
+ ---
244
+
245
+ ## Lazy locales
246
+
247
+ Only `en-US` is bundled with the main entry. Load any other built-in
248
+ locale on demand:
249
+
250
+ ```ts
251
+ import { loadLocale, BUILTIN_LOCALES } from '@tony01/astroneum'
252
+
253
+ await loadLocale('ja-JP')
254
+ chartRef.current?.setLocale('ja-JP')
255
+
256
+ console.log(BUILTIN_LOCALES) // ['en-US', 'zh-CN', 'ja-JP', ...]
257
+ ```
258
+
259
+ `loadLocale(key)` resolves with the dictionary on success, or `null`
260
+ when `key` is not a built-in locale. Concurrent calls for the same
261
+ locale are de-duplicated. To register a fully custom locale,
262
+ keep using `loadLocales(key, dictionary)`.
263
+
264
+ ---
265
+
266
+ ## Subpath entry points
267
+
268
+ Optional features are reachable through dedicated subpath imports so a
269
+ consumer can pull in just what they need.
270
+
271
+ | Subpath | Exports |
272
+ | --- | --- |
273
+ | `@tony01/astroneum/headless` | `createChart`, `destroyChart`, `registerBuiltInExtensions`, `HEADLESS_CAPABILITIES`, registration/query helpers, branded bar types, engine host types |
274
+ | `@tony01/astroneum/replay` | `BarReplay`, types `BarReplayOptions`, `BarReplayState` |
275
+ | `@tony01/astroneum/multichart` | `MultiChartLayout`, types `MultiChartCount`, `MultiChartSlot`, `MultiChartLayoutOptions` |
276
+ | `@tony01/astroneum/watchlist` | `WatchlistManager`, types `Watchlist`, `WatchSymbol`, `WatchlistColumn`, `WatchlistSort`, `WatchlistSortDirection` |
277
+ | `@tony01/astroneum/portfolio` | `PortfolioTracker`, types `Position`, `PositionSide`, `PnLResult` |
278
+ | `@tony01/astroneum/alerts` | `AlertManager`, full alert type surface |
279
+ | `@tony01/astroneum/script` | `ScriptEngine`, types `CompiledIndicator`, `StudyOptions`, `PlotOptions`, `InputOptions` |
280
+ | `@tony01/astroneum/datafeeds/polygon` | `DefaultDatafeed`, `WebSocketDatafeed`, `WebSocketDatafeedOptions` |
281
+ | `@tony01/astroneum/datafeeds/crypto` | `createStandardCryptoDatafeed`, `StandardCryptoDatafeed`, `STANDARD_CRYPTO_SYMBOLS`, `DATAFEED_ERROR_EVENT`, `BinanceAdapter`, `BitgetAdapter`, `OkxAdapter`, plus types |
282
+
283
+ All listed symbols also re-export from the root `@tony01/astroneum` entry today,
284
+ but the root re-exports for these modules will be removed in **v1.0**
285
+ (see the [Roadmap](../README.md#v10--stability)). Migrate to the subpath
286
+ import to be forward-compatible.
287
+
288
+ `MultiChartLayout` persists the grid, active pane, and complete serialized state
289
+ for each pane when given a `storageKey`; an explicit `count` takes precedence
290
+ over a stored grid size.
291
+
292
+ ```ts
293
+ // recommended — forward-compatible with v1.0
294
+ import { BarReplay } from '@tony01/astroneum/replay'
295
+ import { createStandardCryptoDatafeed } from '@tony01/astroneum/datafeeds/crypto'
296
+
297
+ // legacy — works today, will be removed in v1.0
298
+ import { BarReplay, createStandardCryptoDatafeed } from '@tony01/astroneum'
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Exported utilities
304
+
305
+ ### Brand cast helpers
306
+
307
+ ```typescript
308
+ import { asPrice, asVolume, asTimestamp } from '@tony01/astroneum'
309
+
310
+ const p: Price = asPrice(100.50)
311
+ const v: Volume = asVolume(1_250_000)
312
+ const t: Timestamp = asTimestamp(Date.now())
313
+ ```
314
+
315
+ These are type-safe cast functions. They assert that a plain `number` belongs to a specific financial domain without any runtime overhead.
316
+
317
+ ### `rafCoalesce`
318
+
319
+ ```typescript
320
+ import { rafCoalesce } from '@tony01/astroneum'
321
+
322
+ const dispatch = rafCoalesce((tick: CandleData) => chart.updateBar(tick))
323
+ webSocket.onmessage = e => dispatch(parse(e.data)) // safe to call 100×/s
324
+ ```
325
+
326
+ Returns a dispatcher that schedules `fn` on the next `requestAnimationFrame`, coalescing rapid calls so only the **last** value is delivered per frame. Use this when you only care about the latest state (e.g. price display). For financial accuracy across bursts, use `rafMergeTick` instead.
327
+
328
+ ### `rafMergeTick`
329
+
330
+ ```typescript
331
+ import { rafMergeTick } from '@tony01/astroneum'
332
+
333
+ const cancel = rafMergeTick(incomingTick, (merged) => {
334
+ chart.updateBar(merged)
335
+ })
336
+ // Call cancel() to stop
337
+ ```
338
+
339
+ OHLCV-aware frame coalescer for WebSocket burst handling. Merges all ticks arriving within the same `requestAnimationFrame` frame into one financially accurate bar:
340
+ - `open` = first tick
341
+ - `high` = max across ticks
342
+ - `low` = min across ticks
343
+ - `close` = last tick
344
+ - `volume` = sum across ticks
345
+
346
+ ### `EventBus`
347
+
348
+ ```typescript
349
+ import { EventBus } from '@tony01/astroneum'
350
+ import type { ChartEventMap } from '@tony01/astroneum'
351
+
352
+ const bus = new EventBus<ChartEventMap>()
353
+
354
+ bus.on('tick', (candle) => console.log(candle.close))
355
+ bus.emit('tick', bar)
356
+ bus.off('tick', handler)
357
+ ```
358
+
359
+ Zero-dependency, type-safe synchronous event bus.
360
+
361
+ ### `TickAnimator`
362
+
363
+ ```typescript
364
+ import { TickAnimator } from '@tony01/astroneum'
365
+ import type { TickAnimatorOptions } from '@tony01/astroneum'
366
+
367
+ const opts: TickAnimatorOptions = { duration: 120 } // ms
368
+ const animator = new TickAnimator(opts, (frame) => {
369
+ chart.updateLastBar(frame)
370
+ })
371
+
372
+ animator.push(newTick)
373
+ animator.cancel()
374
+ ```
375
+
376
+ Smooth ease-out cubic interpolation of the last bar's `close`/`high`/`low` over `duration` ms (default 120 ms). Zero allocations in the rAF loop.
377
+
378
+ ### `RingBuffer`
379
+
380
+ ```typescript
381
+ import { RingBuffer } from '@tony01/astroneum'
382
+
383
+ const buf = new RingBuffer<CandleData>(1000) // fixed capacity
384
+ buf.push(candle)
385
+ const arr = buf.toArray() // oldest → newest
386
+ ```
387
+
388
+ Fixed-capacity O(1) circular buffer designed for streaming time-series data.
389
+
390
+ ---
391
+
392
+ ## Exported types
393
+
394
+ ```typescript
395
+ import type {
396
+ // Core
397
+ AstroneumOptions,
398
+ AstroneumHandle,
399
+ SerializedChartState,
400
+ Datafeed,
401
+ SymbolInfo,
402
+ Period,
403
+ CandleData,
404
+ DatafeedSubscribeCallback,
405
+ QuoteSnapshot,
406
+
407
+ // Branded primitives
408
+ Price,
409
+ Volume,
410
+ Timestamp,
411
+
412
+ // Indicator
413
+ IndicatorDef,
414
+ IndicatorPlugin,
415
+ ChartPlugin,
416
+ ChartPluginContext,
417
+
418
+ // Events
419
+ ChartEventMap,
420
+
421
+ // Coordinate space
422
+ Viewport,
423
+
424
+ // Engine types
425
+ Styles,
426
+ DeepPartial,
427
+ Coordinate,
428
+ Bounding,
429
+
430
+ // Utility types
431
+ TickAnimatorOptions,
432
+ TimeframeText,
433
+ Timespan,
434
+
435
+ // MultiChartLayout
436
+ MultiChartCount,
437
+ MultiChartSlot,
438
+ MultiChartLayoutOptions,
439
+
440
+ // BarReplay
441
+ BarReplayOptions,
442
+ BarReplayState,
443
+
444
+ // Drawing templates
445
+ OverlayStylePreset,
446
+ DrawingStyleTemplate,
447
+
448
+ // AlertManager
449
+ Alert,
450
+ AlertCondition,
451
+ AlertStatus,
452
+ AlertFrequency,
453
+ AlertCreate,
454
+ AlertCheckInput,
455
+ AlertTriggeredCallback,
456
+
457
+ // Format helpers
458
+ FormatPriceOptions,
459
+ DateFormatStyle,
460
+ TimeFormatStyle,
461
+
462
+ // ScriptEngine
463
+ CompiledIndicator,
464
+ StudyOptions,
465
+ PlotOptions,
466
+ InputOptions,
467
+
468
+ // WatchlistManager
469
+ Watchlist,
470
+ WatchSymbol,
471
+ WatchlistColumn,
472
+ WatchlistSort,
473
+ WatchlistSortDirection,
474
+
475
+ // PortfolioTracker
476
+ Position,
477
+ PositionSide,
478
+ PnLResult,
479
+
480
+ // PerformanceMode
481
+ PerformanceBar,
482
+ } from '@tony01/astroneum'
483
+ ```
484
+
485
+ ### `Datafeed.getQuotes` and `QuoteSnapshot`
486
+
487
+ `getQuotes` is optional. Watchlist consumers call it with resolved `SymbolInfo` records and keep displaying `—` when a datafeed does not provide snapshots.
488
+
489
+ ```typescript
490
+ interface Datafeed {
491
+ getQuotes?(symbols: SymbolInfo[]): Promise<QuoteSnapshot[]>
492
+ }
493
+
494
+ interface QuoteSnapshot {
495
+ ticker: string
496
+ last: Price
497
+ change?: number
498
+ changePercent?: number
499
+ volume?: Volume
500
+ open?: Price
501
+ high?: Price
502
+ low?: Price
503
+ timestamp?: Timestamp
504
+ }
505
+ ```
506
+
507
+ ### `WatchlistManager`
508
+
509
+ List contents and configuration persist to `localStorage`. Quote fields are runtime-only and deliberately excluded from persistence.
510
+
511
+ ```typescript
512
+ manager.addSymbolFromInfo(listId, symbol)
513
+ manager.moveSymbol(fromListId, toListId, ticker, toIndex?)
514
+ manager.setSort(listId, column, direction?)
515
+ manager.setColumns(listId, columns)
516
+ manager.setColor(listId, color)
517
+ manager.updateQuotes(snapshots)
518
+ ```
519
+
520
+ ### `IndicatorDef`
521
+
522
+ ```typescript
523
+ interface IndicatorDef {
524
+ /** Registered indicator name, e.g. 'EMA', 'MA', 'RSI' */
525
+ name: string
526
+ /** Calculation parameters passed to the indicator engine */
527
+ calcParams?: number[]
528
+ }
529
+ ```
530
+
531
+ ### `IndicatorPlugin<TOutput>`
532
+
533
+ ```typescript
534
+ interface IndicatorPlugin<TOutput> {
535
+ name: string
536
+ shortName?: string
537
+ calcParams?: number[]
538
+ calc(data: CandleData[], params: number[]): TOutput[]
539
+ render2D?(ctx: CanvasRenderingContext2D, output: TOutput[], viewport: Viewport): void
540
+ renderGL?(gl: WebGL2RenderingContext, output: TOutput[], viewport: Viewport, vbo: WebGLBuffer): void
541
+ }
542
+ ```
543
+
544
+ Implement this interface to register a custom typed indicator.
545
+ Use `render2D` for lightweight overlays (< 10k points) and `renderGL` for high-density or GPU-accelerated rendering.
546
+ When `renderGL` is present, Astroneum executes it on a dedicated WebGL2 layer and reuses a per-indicator `vbo`.
547
+ If WebGL2 is unavailable, `render2D` is used as fallback when available.
548
+
549
+ ### `registerIndicatorPlugin(plugin)`
550
+
551
+ ```typescript
552
+ registerIndicatorPlugin<TOutput>(plugin: IndicatorPlugin<TOutput>): void
553
+ ```
554
+
555
+ Adapts `IndicatorPlugin` to the engine `IndicatorTemplate` format and registers it.
556
+
557
+ ### `registerIndicatorPlugins(plugins)`
558
+
559
+ ```typescript
560
+ registerIndicatorPlugins(plugins: IndicatorPlugin<unknown>[]): void
561
+ ```
562
+
563
+ Batch helper for registering multiple indicator plugins.
564
+
565
+ ### `createIndicatorTemplateFromPlugin(plugin)`
566
+
567
+ ```typescript
568
+ createIndicatorTemplateFromPlugin<TOutput>(plugin: IndicatorPlugin<TOutput>): IndicatorTemplate
569
+ ```
570
+
571
+ Returns the converted engine template without registering it. Useful when you want manual control over registration.
572
+
573
+ ### `ChartPlugin`
574
+
575
+ ```typescript
576
+ interface ChartPlugin {
577
+ name?: string
578
+ indicators?: IndicatorPlugin<unknown>[]
579
+ onInit?: (context: ChartPluginContext) => void | (() => void)
580
+ }
581
+ ```
582
+
583
+ `indicators` are registered before chart indicator creation begins. `onInit` runs once after chart initialization and may return a disposer called during chart teardown.
584
+
585
+ ### `ChartPluginContext`
586
+
587
+ ```typescript
588
+ interface ChartPluginContext {
589
+ chart: Chart
590
+ registerIndicatorPlugin: (plugin: IndicatorPlugin<unknown>) => void
591
+ registerIndicatorPlugins: (plugins: IndicatorPlugin<unknown>[]) => void
592
+ }
593
+ ```
594
+
595
+ ### `ChartEventMap`
596
+
597
+ ```typescript
598
+ interface ChartEventMap {
599
+ 'symbol-change': SymbolInfo
600
+ 'period-change': Period
601
+ 'crosshair-move': Coordinate | null
602
+ 'zoom': { scale: number; anchor: ZoomAnchor }
603
+ 'data-load': { type: 'init' | 'forward' | 'backward'; count: number }
604
+ 'tick': CandleData
605
+ 'drawing-start': string
606
+ 'drawing-end': string
607
+ 'theme-change': string
608
+ }
609
+ ```
610
+
611
+ ### `Viewport`
612
+
613
+ ```typescript
614
+ interface Viewport {
615
+ priceMin: Price
616
+ priceMax: Price
617
+ timeMin: Timestamp
618
+ timeMax: Timestamp
619
+ resolution: readonly [width: number, height: number]
620
+ }
621
+ ```
622
+
623
+ Represents the currently visible region of the chart canvas. Used for coordinate-space transforms in custom `IndicatorPlugin` renderers.
624
+
625
+ ---
626
+
627
+ ## Additional exports
628
+
629
+ | Export | Description |
630
+ |---|---|
631
+ | `DefaultDatafeed` | Polygon.io REST + WebSocket datafeed |
632
+ | `MultiChartLayout` | Multi-chart layout manager |
633
+ | `BarReplay` | Historical bar replay engine |
634
+ | `DrawingTemplates` | Save/load drawing style templates |
635
+ | `AlertManager` | Price alert creation and monitoring |
636
+ | `ScriptEngine` | Pine-like indicator scripting sandbox (`compile()` auto-registers indicators) |
637
+ | `registerIndicator` | Register a raw engine `IndicatorTemplate` |
638
+ | `getSupportedIndicators` | List all registered indicator names |
639
+ | `registerOverlay` | Register a raw engine `OverlayTemplate` |
640
+ | `getSupportedOverlays` | List all registered overlay names |
641
+ | `registerXAxis` / `registerYAxis` | Register custom axis templates |
642
+ | `registerIndicatorPlugin` | Register a typed `IndicatorPlugin` via adapter |
643
+ | `registerIndicatorPlugins` | Batch register typed indicator plugins |
644
+ | `createIndicatorTemplateFromPlugin` | Convert `IndicatorPlugin` to engine template |
645
+ | `WatchlistManager` | Symbol watchlist with live prices |
646
+ | `PortfolioTracker` | Position tracking and P&L calculation |
647
+ | `PerformanceMode` | Reduced-render performance optimization |
648
+ | `loadLocales` | Register UI locale strings |
649
+ | `formatPrice` | Format a price number using symbol precision |
650
+ | `formatVolume` | Format a volume number |
651
+ | `formatPercent` | Format a percentage value |
652
+ | `formatDate` / `formatTime` / `formatDateTime` | Date/time formatting helpers |
653
+ | `formatDuration` | Format a duration in milliseconds as human-readable text |
654
+ | `formatPeriod` | Format a `Period` as a human-readable string (e.g. `'1H'`) |
655
+ | `detectPricePrecision` | Auto-detect decimal precision from price data |