@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.
- package/CHANGELOG.md +280 -237
- package/LICENSE +21 -21
- package/README.md +250 -198
- package/dist/{Indicator-BBI3L_lx.d.ts → Indicator-BQQcVGGT.d.ts} +51 -227
- package/dist/astroneum.css +1 -1
- package/dist/chunk-47YGQEL5.js +399 -0
- package/dist/chunk-67XM5LOK.js +2 -0
- package/dist/{chunk-2YYGUSVX.js → chunk-DFJMDDL5.js} +1 -1
- package/dist/chunk-EFAS6F4C.js +59 -0
- package/dist/chunk-EHZN52CY.js +2 -0
- package/dist/chunk-HKLWNPMP.js +35 -0
- package/dist/chunk-ONPPQW65.js +2 -0
- package/dist/chunk-SCTHEM27.js +3 -0
- package/dist/chunk-UQXYMB6X.js +2 -0
- package/dist/chunk-WSIR5T2M.js +3 -0
- package/dist/entries/alerts.d.ts +36 -10
- package/dist/entries/alerts.js +1 -1
- package/dist/entries/datafeeds/crypto.d.ts +5 -1
- package/dist/entries/datafeeds/crypto.js +1 -1
- package/dist/entries/datafeeds/polygon.d.ts +3 -1
- package/dist/entries/datafeeds/polygon.js +1 -1
- package/dist/entries/headless.d.ts +78 -0
- package/dist/entries/headless.js +2 -0
- package/dist/entries/multichart.d.ts +12 -1
- package/dist/entries/multichart.js +1 -1
- package/dist/entries/replay.d.ts +3 -1
- package/dist/entries/script.d.ts +2 -68
- package/dist/entries/script.js +1 -1
- package/dist/entries/watchlist.d.ts +32 -2
- package/dist/entries/watchlist.js +1 -1
- package/dist/entries/workspace.d.ts +61 -0
- package/dist/entries/workspace.js +2 -0
- package/dist/index-BiXdqLA4.d.ts +45 -0
- package/dist/index-_JgHTy8X.d.ts +19 -0
- package/dist/index.d.ts +188 -27
- package/dist/index.js +1 -1
- package/dist/script-BU9StbN-.d.ts +120 -0
- package/dist/types-W6SIYQ9m.d.ts +232 -0
- package/dist/types-yETgeYsE.d.ts +28 -0
- package/dist/zh-CN-NDTUDZGZ.js +3 -0
- package/docs/MIRROR-PLAN-SYMBOL-SEARCH-WATCHLIST.md +255 -0
- package/docs/STRUCTURE.md +51 -37
- package/docs/TODO-DESIGN.md +111 -37
- package/docs/TODO.md +26 -16
- package/docs/WORKSTREAMS.md +55 -0
- package/docs/api.md +655 -528
- package/docs/datafeed-guide.md +329 -311
- package/docs/design-astroneum.md +204 -65
- package/docs/plugin-development.md +569 -565
- package/docs/tv-functions-skill.md +88 -64
- package/package.json +158 -147
- package/dist/chunk-4YOLQ5DC.js +0 -2
- package/dist/chunk-I4YQBP2M.js +0 -3
- package/dist/chunk-OOMZRONH.js +0 -35
- package/dist/chunk-PV2GAINM.js +0 -2
- package/dist/chunk-YW3STIPY.js +0 -653
- package/dist/zh-CN-Y2C6RSOI.js +0 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { a as CandleData, f as IndicatorTemplate } from './Indicator-BQQcVGGT.js';
|
|
2
|
+
|
|
3
|
+
type StrategySide = 'long' | 'short';
|
|
4
|
+
interface BacktestConfig {
|
|
5
|
+
initialCapital: number;
|
|
6
|
+
commissionPercent: number;
|
|
7
|
+
slippagePercent: number;
|
|
8
|
+
positionSizePercent: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
}
|
|
11
|
+
interface BacktestTrade {
|
|
12
|
+
id: string;
|
|
13
|
+
side: StrategySide;
|
|
14
|
+
entryTime: number;
|
|
15
|
+
entryPrice: number;
|
|
16
|
+
exitTime: number;
|
|
17
|
+
exitPrice: number;
|
|
18
|
+
quantity: number;
|
|
19
|
+
grossProfit: number;
|
|
20
|
+
commission: number;
|
|
21
|
+
netProfit: number;
|
|
22
|
+
barsHeld: number;
|
|
23
|
+
}
|
|
24
|
+
interface EquityPoint {
|
|
25
|
+
timestamp: number;
|
|
26
|
+
equity: number;
|
|
27
|
+
drawdownPercent: number;
|
|
28
|
+
}
|
|
29
|
+
interface BacktestMetrics {
|
|
30
|
+
netProfit: number;
|
|
31
|
+
netProfitPercent: number;
|
|
32
|
+
grossProfit: number;
|
|
33
|
+
grossLoss: number;
|
|
34
|
+
profitFactor: number | null;
|
|
35
|
+
totalTrades: number;
|
|
36
|
+
percentProfitable: number;
|
|
37
|
+
maxDrawdown: number;
|
|
38
|
+
maxDrawdownPercent: number;
|
|
39
|
+
buyAndHoldPercent: number;
|
|
40
|
+
}
|
|
41
|
+
interface BacktestResult {
|
|
42
|
+
config: BacktestConfig;
|
|
43
|
+
metrics: BacktestMetrics;
|
|
44
|
+
trades: BacktestTrade[];
|
|
45
|
+
equity: EquityPoint[];
|
|
46
|
+
bars: CandleData[];
|
|
47
|
+
mode: 'fixture' | 'runtime';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Client-side JavaScript indicator scripting engine.
|
|
52
|
+
*
|
|
53
|
+
* Lets users write Pine-Script–inspired indicator logic in plain JS
|
|
54
|
+
* and compile it into an IndicatorTemplate that is registered
|
|
55
|
+
* with the chart engine during compilation.
|
|
56
|
+
*
|
|
57
|
+
* Security: scripts run inside a Function scope with a curated API
|
|
58
|
+
* surface (forbidden identifiers shadowed, Object/Array prototypes frozen).
|
|
59
|
+
* This is a best-effort sandbox suitable for trusted user scripts only —
|
|
60
|
+
* constructor-chain escapes (e.g. (0).constructor.constructor) are not
|
|
61
|
+
* fully blocked. Do NOT run untrusted third-party scripts without a
|
|
62
|
+
* real isolate (sandboxed iframe or Web Worker).
|
|
63
|
+
*
|
|
64
|
+
* Usage:
|
|
65
|
+
* const engine = ScriptEngine.getInstance()
|
|
66
|
+
* const template = engine.compile(`
|
|
67
|
+
* study('My MA', { overlay: true })
|
|
68
|
+
* const len = input('Length', 14)
|
|
69
|
+
* plot(ta.sma(close, len), { title: 'SMA', color: '#2962ff' })
|
|
70
|
+
* `)
|
|
71
|
+
* chart.createIndicator(template.name)
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
interface StudyOptions {
|
|
75
|
+
/** Display name shown in tooltip */
|
|
76
|
+
shortName?: string;
|
|
77
|
+
/** Whether to overlay on the price pane (default false) */
|
|
78
|
+
overlay?: boolean;
|
|
79
|
+
/** Precision of displayed values */
|
|
80
|
+
precision?: number;
|
|
81
|
+
}
|
|
82
|
+
interface PlotOptions {
|
|
83
|
+
title?: string;
|
|
84
|
+
color?: string;
|
|
85
|
+
lineWidth?: number;
|
|
86
|
+
visible?: boolean;
|
|
87
|
+
}
|
|
88
|
+
interface InputOptions {
|
|
89
|
+
type?: 'int' | 'float' | 'bool' | 'string';
|
|
90
|
+
options?: (number | string)[];
|
|
91
|
+
}
|
|
92
|
+
interface CompiledIndicator extends IndicatorTemplate {
|
|
93
|
+
/** Original source for editing */
|
|
94
|
+
_source: string;
|
|
95
|
+
}
|
|
96
|
+
interface CompiledStrategy {
|
|
97
|
+
name: string;
|
|
98
|
+
source: string;
|
|
99
|
+
run: (bars: CandleData[], config?: Partial<BacktestConfig>) => BacktestResult;
|
|
100
|
+
}
|
|
101
|
+
declare class ScriptEngine {
|
|
102
|
+
private static _instance;
|
|
103
|
+
private _registry;
|
|
104
|
+
private constructor();
|
|
105
|
+
static getInstance(): ScriptEngine;
|
|
106
|
+
/**
|
|
107
|
+
* Compile a script string into an IndicatorTemplate.
|
|
108
|
+
* Throws a descriptive Error if compilation or type validation fails.
|
|
109
|
+
*/
|
|
110
|
+
compile(source: string, name?: string): CompiledIndicator;
|
|
111
|
+
compileStrategy(source: string, name?: string): CompiledStrategy;
|
|
112
|
+
/** Get a previously compiled template by name */
|
|
113
|
+
get(name: string): CompiledIndicator | undefined;
|
|
114
|
+
/** All registered script names */
|
|
115
|
+
list(): string[];
|
|
116
|
+
/** Remove a compiled indicator */
|
|
117
|
+
remove(name: string): void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { type BacktestConfig as B, type CompiledIndicator as C, type EquityPoint as E, type InputOptions as I, type PlotOptions as P, type StrategySide as S, type BacktestResult as a, type BacktestMetrics as b, type BacktestTrade as c, type CompiledStrategy as d, ScriptEngine as e, type StudyOptions as f };
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
import { D as DeepPartial, S as Styles, C as Chart, a as CandleData, I as IndicatorCreate, P as PaneOptions, N as Nullable, b as IndicatorFilter, c as Indicator, O as OverlayCreate, d as OverlayFilter, e as Coordinate, Z as ZoomAnchor } from './Indicator-BQQcVGGT.js';
|
|
3
|
+
import { I as IndicatorPlugin, P as Price, V as Volume, T as Timestamp } from './types-yETgeYsE.js';
|
|
4
|
+
|
|
5
|
+
interface IndicatorDef {
|
|
6
|
+
name: string;
|
|
7
|
+
calcParams?: number[];
|
|
8
|
+
}
|
|
9
|
+
interface SerializedIndicator extends IndicatorDef {
|
|
10
|
+
visible: boolean;
|
|
11
|
+
styles?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** Canonical timespan identifiers — aligned with engine PeriodType */
|
|
14
|
+
type Timespan = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
|
|
15
|
+
interface SymbolInfo {
|
|
16
|
+
ticker: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
shortName?: string;
|
|
19
|
+
exchange?: string;
|
|
20
|
+
market?: string;
|
|
21
|
+
pricePrecision?: number;
|
|
22
|
+
volumePrecision?: number;
|
|
23
|
+
priceCurrency?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
logo?: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
interface Period {
|
|
29
|
+
multiplier: number;
|
|
30
|
+
/** Canonical timespan — maps directly to engine PeriodType */
|
|
31
|
+
timespan: Timespan;
|
|
32
|
+
text: string;
|
|
33
|
+
}
|
|
34
|
+
type DatafeedSubscribeCallback = (data: CandleData) => void;
|
|
35
|
+
interface QuoteSnapshot {
|
|
36
|
+
ticker: string;
|
|
37
|
+
last: Price;
|
|
38
|
+
change?: number;
|
|
39
|
+
changePercent?: number;
|
|
40
|
+
volume?: Volume;
|
|
41
|
+
open?: Price;
|
|
42
|
+
high?: Price;
|
|
43
|
+
low?: Price;
|
|
44
|
+
timestamp?: Timestamp;
|
|
45
|
+
}
|
|
46
|
+
interface Datafeed {
|
|
47
|
+
searchSymbols(search?: string): Promise<SymbolInfo[]>;
|
|
48
|
+
getHistoryData(symbol: SymbolInfo, period: Period, from: number, to: number): Promise<CandleData[]>;
|
|
49
|
+
subscribe(symbol: SymbolInfo, period: Period, callback: DatafeedSubscribeCallback): void;
|
|
50
|
+
unsubscribe(symbol: SymbolInfo, period: Period): void;
|
|
51
|
+
getQuotes?(symbols: SymbolInfo[]): Promise<QuoteSnapshot[]>;
|
|
52
|
+
}
|
|
53
|
+
interface ChartToolbarActions {
|
|
54
|
+
toggleDrawingBar: () => void;
|
|
55
|
+
openSymbolSearch: () => void;
|
|
56
|
+
openIndicators: () => void;
|
|
57
|
+
openTimezone: () => void;
|
|
58
|
+
openSettings: () => void;
|
|
59
|
+
openAlert: () => void;
|
|
60
|
+
captureScreenshot: () => void;
|
|
61
|
+
}
|
|
62
|
+
interface AstroneumOptions {
|
|
63
|
+
styles?: DeepPartial<Styles>;
|
|
64
|
+
watermark?: string | Node;
|
|
65
|
+
theme?: string;
|
|
66
|
+
locale?: string;
|
|
67
|
+
drawingBarVisible?: boolean;
|
|
68
|
+
toolbarVisible?: boolean;
|
|
69
|
+
toolbarActionsRef?: react__default.MutableRefObject<ChartToolbarActions | null>;
|
|
70
|
+
symbol: SymbolInfo;
|
|
71
|
+
period: Period;
|
|
72
|
+
periods?: Period[];
|
|
73
|
+
timezone?: string;
|
|
74
|
+
mainIndicators?: IndicatorDef[];
|
|
75
|
+
subIndicators?: string[];
|
|
76
|
+
plugins?: ChartPlugin[];
|
|
77
|
+
datafeed: Datafeed;
|
|
78
|
+
/** Inline styles applied to the root `.astroneum` container element. */
|
|
79
|
+
style?: react__default.CSSProperties;
|
|
80
|
+
/** Additional class names applied to the root `.astroneum` container element. */
|
|
81
|
+
className?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Make the chart canvas focusable + emit an `aria-live` summary of the
|
|
84
|
+
* crosshair OHLCV. Off by default to avoid surprising existing apps.
|
|
85
|
+
* When `true`, the chart container gets `tabindex=0`, `role="img"`,
|
|
86
|
+
* and an `aria-label` describing the current symbol. Cursor moves
|
|
87
|
+
* announce OHLCV through a visually-hidden polite live region.
|
|
88
|
+
*/
|
|
89
|
+
accessible?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Optional ARIA label override for the chart canvas. Only used when
|
|
92
|
+
* `accessible` is true. Defaults to a string built from the symbol
|
|
93
|
+
* ticker and current period.
|
|
94
|
+
*/
|
|
95
|
+
ariaLabel?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Bar rendering style. Defaults to 'candle' (standard candlesticks).
|
|
98
|
+
* 'heikin_ashi' transforms OHLC data with the Heikin-Ashi formula
|
|
99
|
+
* before rendering — no datafeed changes required.
|
|
100
|
+
*/
|
|
101
|
+
barStyle?: 'candle' | 'heikin_ashi';
|
|
102
|
+
/**
|
|
103
|
+
* Price axis scale mode. Defaults to 'linear' (absolute price).
|
|
104
|
+
* 'log' uses logarithmic scale (useful for long-term charts).
|
|
105
|
+
* 'percent' shows percentage change from the first visible bar.
|
|
106
|
+
*/
|
|
107
|
+
priceScale?: 'linear' | 'log' | 'percent';
|
|
108
|
+
initialHistory?: 'all' | 'none';
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Serialised chart state — drawings, indicators, and the user-facing
|
|
112
|
+
* surface settings (theme, locale, timezone, symbol, period, styles).
|
|
113
|
+
*
|
|
114
|
+
* The shape is intentionally simple JSON so it can be persisted to
|
|
115
|
+
* `localStorage`, a URL, or a backend. The `version` field lets the
|
|
116
|
+
* library evolve the format without breaking older payloads.
|
|
117
|
+
*/
|
|
118
|
+
interface SerializedChartState {
|
|
119
|
+
version: 1;
|
|
120
|
+
theme: string;
|
|
121
|
+
locale: string;
|
|
122
|
+
timezone: string;
|
|
123
|
+
symbol: SymbolInfo;
|
|
124
|
+
period: Period;
|
|
125
|
+
styles: DeepPartial<Styles>;
|
|
126
|
+
mainIndicators: SerializedIndicator[];
|
|
127
|
+
subIndicators: SerializedIndicator[];
|
|
128
|
+
overlays: Array<{
|
|
129
|
+
name: string;
|
|
130
|
+
points?: ReadonlyArray<{
|
|
131
|
+
timestamp?: number;
|
|
132
|
+
value?: number;
|
|
133
|
+
dataIndex?: number;
|
|
134
|
+
}>;
|
|
135
|
+
styles?: unknown;
|
|
136
|
+
lock?: boolean;
|
|
137
|
+
visible?: boolean;
|
|
138
|
+
}>;
|
|
139
|
+
}
|
|
140
|
+
interface AstroneumHandle {
|
|
141
|
+
setTheme(theme: string): void;
|
|
142
|
+
getTheme(): string;
|
|
143
|
+
setStyles(styles: DeepPartial<Styles>): void;
|
|
144
|
+
getStyles(): Styles;
|
|
145
|
+
setLocale(locale: string): void;
|
|
146
|
+
getLocale(): string;
|
|
147
|
+
setTimezone(timezone: string): void;
|
|
148
|
+
getTimezone(): string;
|
|
149
|
+
setSymbol(symbol: SymbolInfo): void;
|
|
150
|
+
getSymbol(): SymbolInfo;
|
|
151
|
+
setPeriod(period: Period): void;
|
|
152
|
+
getPeriod(): Period;
|
|
153
|
+
getDataListLength(): number;
|
|
154
|
+
getLastDataTimestamp(): number | null;
|
|
155
|
+
/**
|
|
156
|
+
* Capture the chart's current user-visible state as a JSON-safe object.
|
|
157
|
+
* Includes theme, locale, timezone, symbol, period, styles, indicators,
|
|
158
|
+
* and drawing overlays. Pair with `loadState()` to restore.
|
|
159
|
+
*/
|
|
160
|
+
serializeState(): SerializedChartState;
|
|
161
|
+
/**
|
|
162
|
+
* Restore a previously-captured state. Best-effort: missing or unknown
|
|
163
|
+
* indicator / overlay names are silently skipped, matching the engine's
|
|
164
|
+
* behaviour for unregistered names.
|
|
165
|
+
*/
|
|
166
|
+
loadState(state: SerializedChartState): void;
|
|
167
|
+
/**
|
|
168
|
+
* Subscribe to crosshair moves. Returns an unsubscribe function.
|
|
169
|
+
* Callback receives the crosshair data (or null when it leaves the chart).
|
|
170
|
+
* Returns a Coordinate-like object with the timestamp + price at cursor.
|
|
171
|
+
*/
|
|
172
|
+
onCrosshairMove(callback: (data: unknown) => void): () => void;
|
|
173
|
+
/**
|
|
174
|
+
* Programmatically set the crosshair position on this chart.
|
|
175
|
+
* Used internally by MultiChartLayout for crosshair sync.
|
|
176
|
+
*/
|
|
177
|
+
setCrosshair(data: unknown): void;
|
|
178
|
+
/** Lock or unlock ALL drawing overlays at once. */
|
|
179
|
+
lockAllDrawings(locked: boolean): void;
|
|
180
|
+
createIndicator(value: string | IndicatorCreate, isStack?: boolean, paneOptions?: PaneOptions): Nullable<string>;
|
|
181
|
+
removeIndicator(filter?: IndicatorFilter): boolean;
|
|
182
|
+
getIndicators(filter?: IndicatorFilter): Indicator[];
|
|
183
|
+
createOverlay(value: string | OverlayCreate | Array<string | OverlayCreate>): Nullable<string> | Array<Nullable<string>>;
|
|
184
|
+
removeOverlay(filter?: OverlayFilter): boolean;
|
|
185
|
+
getDataList(): CandleData[];
|
|
186
|
+
resetData(): void;
|
|
187
|
+
resize(): void;
|
|
188
|
+
}
|
|
189
|
+
interface ChartPluginContext {
|
|
190
|
+
/** The chart instance mounted by Astroneum. */
|
|
191
|
+
chart: Chart;
|
|
192
|
+
/** Register a typed indicator plugin at runtime. */
|
|
193
|
+
registerIndicatorPlugin: (plugin: IndicatorPlugin<unknown>) => void;
|
|
194
|
+
/** Batch register typed indicator plugins at runtime. */
|
|
195
|
+
registerIndicatorPlugins: (plugins: ReadonlyArray<IndicatorPlugin<unknown>>) => void;
|
|
196
|
+
}
|
|
197
|
+
interface ChartPlugin {
|
|
198
|
+
/** Optional plugin label for diagnostics and debugging. */
|
|
199
|
+
name?: string;
|
|
200
|
+
/** Indicator plugins to register before this chart starts creating indicators. */
|
|
201
|
+
indicators?: ReadonlyArray<IndicatorPlugin<unknown>>;
|
|
202
|
+
/** Optional lifecycle hook called once after the chart is initialized. */
|
|
203
|
+
onInit?: (context: ChartPluginContext) => void | (() => void);
|
|
204
|
+
}
|
|
205
|
+
interface ChartEventMap {
|
|
206
|
+
/** Fired when the active symbol changes. */
|
|
207
|
+
'symbol-change': SymbolInfo;
|
|
208
|
+
/** Fired when the active period / timeframe changes. */
|
|
209
|
+
'period-change': Period;
|
|
210
|
+
/** Fired on every crosshair move; null when the crosshair leaves the chart. */
|
|
211
|
+
'crosshair-move': Coordinate | null;
|
|
212
|
+
/** Fired when the user zooms the chart. */
|
|
213
|
+
'zoom': {
|
|
214
|
+
scale: number;
|
|
215
|
+
anchor: ZoomAnchor;
|
|
216
|
+
};
|
|
217
|
+
/** Fired when a historical data batch is loaded. */
|
|
218
|
+
'data-load': {
|
|
219
|
+
type: 'init' | 'forward' | 'backward';
|
|
220
|
+
count: number;
|
|
221
|
+
};
|
|
222
|
+
/** Fired when a real-time tick arrives from the datafeed subscription. */
|
|
223
|
+
'tick': CandleData;
|
|
224
|
+
/** Fired when the user starts drawing an overlay (value = overlay type name). */
|
|
225
|
+
'drawing-start': string;
|
|
226
|
+
/** Fired when the user finishes drawing (value = overlay id). */
|
|
227
|
+
'drawing-end': string;
|
|
228
|
+
/** Fired when the active theme changes (value = theme name). */
|
|
229
|
+
'theme-change': string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export type { AstroneumOptions as A, ChartEventMap as C, Datafeed as D, Period as P, QuoteSnapshot as Q, SymbolInfo as S, AstroneumHandle as a, SerializedChartState as b, ChartPlugin as c, ChartPluginContext as d, ChartToolbarActions as e, DatafeedSubscribeCallback as f, SerializedIndicator as g };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { a as CandleData } from './Indicator-BQQcVGGT.js';
|
|
2
|
+
|
|
3
|
+
type Price = number & {
|
|
4
|
+
readonly _brand: 'Price';
|
|
5
|
+
};
|
|
6
|
+
type Volume = number & {
|
|
7
|
+
readonly _brand: 'Volume';
|
|
8
|
+
};
|
|
9
|
+
type Timestamp = number & {
|
|
10
|
+
readonly _brand: 'Timestamp';
|
|
11
|
+
};
|
|
12
|
+
interface Viewport {
|
|
13
|
+
priceMin: Price;
|
|
14
|
+
priceMax: Price;
|
|
15
|
+
timeMin: Timestamp;
|
|
16
|
+
timeMax: Timestamp;
|
|
17
|
+
resolution: readonly [width: number, height: number];
|
|
18
|
+
}
|
|
19
|
+
interface IndicatorPlugin<TOutput> {
|
|
20
|
+
name: string;
|
|
21
|
+
shortName?: string;
|
|
22
|
+
calcParams?: number[];
|
|
23
|
+
calc(data: CandleData[], params: number[]): TOutput[];
|
|
24
|
+
render2D?(ctx: CanvasRenderingContext2D, output: TOutput[], viewport: Viewport): void;
|
|
25
|
+
renderGL?(gl: WebGL2RenderingContext, output: TOutput[], viewport: Viewport, vbo: WebGLBuffer): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type { IndicatorPlugin as I, Price as P, Timestamp as T, Volume as V, Viewport as a };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var t="Indicator",r="Main Indicator",o="Sub Indicator",i="Setting",l="Timezone",n="Screenshot",_="Full Screen",c="Exit",s="Save",d="Confirm",h="Color",g="Show",m="\u53D6\u6D88",p="MA(Moving Average)",b="EMA(Exponential Moving Average)",v="SMA",u="BOLL(Bolinger Bands)",w="BBI(Bull And Bear Index)",f="SAR(Stop and Reverse)",C="VOL(Volume)",k="MACD(Moving Average Convergence / Divergence)",y="KDJ(KDJ Index)",S="RSI(Relative Strength Index)",T="BIAS(Bias Ratio)",x="BRAR(Bull and Bear Sentiment)",A="CCI(Commodity Channel Index)",P="DMI(Directional Movement Index)",L="CR(Energy Index)",R="PSY(Psychological Line)",U="DMA(Different of Moving Average)",B="TRIX(Triple Exponentially Smoothed Moving Average)",M="OBV(On Balance Volume)",I="VR(Volatility Volume Ratio)",V="WR(Williams %R)",D="MTM(Momentum Index)",W="EMV(Ease of Movement Value)",E="ROC(Price Rate of Change)",F="PVT(Price and Volume Trend)",O="AO(Awesome Oscillator)",z="UTC",H="(UTC-10) Honolulu",J="(UTC-8) Juneau",K="(UTC-7) Los Angeles",G="(UTC-5) Chicago",j="(UTC-4) Toronto",N="(UTC-3) Sao Paulo",X="(UTC+1) London",q="(UTC+2) Berlin",Y="(UTC+3) Bahrain",Q="(UTC+4) Dubai",Z="(UTC+5) Ashkhabad",$="(UTC+6) Almaty",ee="(UTC+7) Bangkok",ae="(UTC+8) Shanghai",te="(UTC+9) Tokyo",re="(UTC+10) Sydney",oe="(UTC+12) Norfolk",ie="Horizontal Line",le="Horizontal Ray",ne="Horizontal Segment",_e="Vertical Line",ce="Vertical Ray",se="Vertical Segment",de="Trend Line",he="Ray",ge="Segment",me="Arrow",pe="Price Line",be="Price Channel Line",ve="Parallel Line",ue="Fibonacci Line",we="Fibonacci Segment",fe="Fibonacci Circle",Ce="Fibonacci Spiral",ke="Fibonacci Sector",ye="Fibonacci Extension",Se="Gann Box",Te="Gann Fan",xe="Andrews Pitchfork",Ae="Rect",Pe="Parallelogram",Le="Circle",Re="Triangle",Ue="Three Waves",Be="Five Waves",Me="Eight Waves",Ie="Any Waves",Ve="ABCD Pattern",De="XABCD Pattern",We="Weak Magnet",Ee="Strong Magnet",Fe="Symbol Search",Oe="Symbol Code",ze="Parameter 1",He="Parameter 2",Je="Parameter 3",Ke="Parameter 4",Ge="Parameter 5",je="Line 1",Ne="Line 2",Xe="Line 3",qe="Line 4",Ye="Line 5",Qe="Period",Ze="Standard Deviation",$e="Candle Type",ea="Candle Solid",aa="Candle Stroke",ta="Candle Up Stroke",ra="Candle Down Stroke",oa="OHLC",ia="Area",la="Show Last Price",na="Show Highest Price",_a="Show Lowest Price",ca="Show indicator's last value",sa="Price Axis Type",da="Normal",ha="Percentage",ga="Log",ma="Reverse Coordinate",pa="Show Grids",ba="\u6062\u590D\u9ED8\u8BA4",va="\u4EF7\u683C\u63D0\u9192",ua="\u521B\u5EFA",wa="\u521B\u5EFA\u63D0\u9192",fa="\u521B\u5EFA\u63D0\u9192\u4E8E",Ca="\u4FDD\u5B58",ka="\u5386\u53F2",ya="\u6761\u4EF6",Sa="\u4EF7\u683C",Ta="\u6570\u636E\u6E90",xa="\u4EF7\u683C",Aa="\u8FD0\u7B97\u7B26",Pa="\u503C",La="\u4EA4\u53C9",Ra="\u4ECB\u4E8E",Ua="\u6DFB\u52A0\u6761\u4EF6",Ba="\u5907\u6CE8",Ma="\u53EF\u9009\u6807\u7B7E\u2026",Ia="\u6DFB\u52A0\u63D0\u9192",Va="\u521B\u5EFA",Da="\u521B\u5EFA\u4E2D\u2026",Wa="\u4FDD\u5B58\u4E2D\u2026",Ea="\u8BF7\u8F93\u5165\u6709\u6548\u6570\u5B57",Fa="\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6709\u6548\u6761\u4EF6",Oa="\u63D0\u9192\u6570\u91CF\u5DF2\u8FBE\u4E0A\u9650",za="\u91CD\u8BD5",Ha="\u6D3B\u8DC3",Ja="\u6682\u65E0\u6D3B\u8DC3\u63D0\u9192",Ka="\u6682\u65E0\u89E6\u53D1\u8BB0\u5F55",Ga="\u91CD\u65B0\u6FC0\u6D3B",ja="\u6682\u505C",Na="\u6062\u590D",Xa="\u7F16\u8F91",qa="\u5220\u9664",Ya="\u641C\u7D22\u63D0\u9192\u2026",Qa="\u6240\u6709\u54C1\u79CD",Za="\u5F53\u524D\u54C1\u79CD",$a="\u5DF2\u6682\u505C",et="\u5DF2\u8FC7\u671F",at="\u5DF2\u5FFD\u7565",tt="\u9AD8\u4E8E",rt="\u4F4E\u4E8E",ot="\u4E0A\u7A7F \u2191",it="\u4E0B\u7A7F \u2193",lt="\u89E6\u53D1\u65B9\u5F0F",nt="\u4EC5\u4E00\u6B21",_t="\u6BCF\u6839K\u7EBF",ct="K\u7EBF\u6536\u76D8\u65F6\u4E00\u6B21",st="\u5230\u671F",dt="\u65E0\u9650\u671F",ht="1\u5929\u540E",gt="1\u5468\u540E",mt="1\u4E2A\u6708\u540E",pt="2\u4E2A\u6708\u540E",bt="\u6D88\u606F",vt="\u8BBE\u7F6E Webhook URL \u540E\u5C06\u4F5C\u4E3A\u8BF7\u6C42\u4F53\u53D1\u9001\u3002",ut="Webhook \u8BF7\u6C42\u4F53",wt="\u901A\u77E5",ft="\u8FD4\u56DE",Ct="\u5E94\u7528",kt="\u5E94\u7528\u5185\u901A\u77E5",yt="\u5728\u79FB\u52A8\u5E94\u7528\u4E2D\u63A8\u9001\u901A\u77E5\u3002",St="\u663E\u793A\u6D6E\u5C42\u63D0\u793A",Tt="\u5728\u9875\u9762\u89D2\u843D\u663E\u793A\u7AD9\u5185\u901A\u77E5\u3002",xt="\u53D1\u9001\u90AE\u4EF6",At="\u53D1\u9001\u5230\u8D26\u6237\u8BBE\u7F6E\u4E2D\u7684\u90AE\u7BB1\u5730\u5740\u3002",Pt="Webhook URL",Lt="\u63D0\u9192\u89E6\u53D1\u65F6\u5411\u4F60\u7684 URL \u53D1\u9001 POST \u8BF7\u6C42\u3002",Rt="\u64AD\u653E\u58F0\u97F3",Ut="\u63D0\u9192\u89E6\u53D1\u65F6\u64AD\u653E\u63D0\u793A\u97F3\u3002",Bt="\u53D1\u9001\u7EAF\u6587\u672C",Mt="\u5411\u5907\u7528\u90AE\u7BB1\u53D1\u9001\u7EAF\u6587\u672C\u3002",It="\u65E0",Vt="URL \u6709\u6548",Dt="URL \u65E0\u6548 \u2014 \u8BF7\u4F7F\u7528 http(s)://\uFF0C\u4E0D\u652F\u6301\u79C1\u6709\u6216\u672C\u5730\u5730\u5740",Wt="\u4E00\u6B21",Et="\u91CD\u590D",Ft="\u901A\u77E5\u8BA1\u5212",Ot="\u5728\u8BA1\u5212\u4E4B\u5916\u7684\u65F6\u95F4\u9759\u97F3\u901A\u77E5\u3002",zt="\u5168\u5929",Ht="\u5DE5\u4F5C\u65E5",Jt="\u5DE5\u4F5C\u65F6\u95F4\uFF08\u5468\u4E00\u81F3\u5468\u4E94 09:00\u201318:00\uFF09",Kt="\u81EA\u5B9A\u4E49",Gt="\u5DF2\u9001\u8FBE",jt="\u5931\u8D25",Nt="\u53D1\u9001\u4E2D",Xt="\u65E5\u5FD7",qt="\u6E05\u9664\u65E5\u5FD7",Yt="\u64CD\u4F5C",Qt="\u6682\u65E0\u89E6\u53D1\u8BB0\u5F55",Zt="Webhook \u5DF2\u6210\u529F\u9001\u8FBE",$t="Webhook \u53D1\u9001\u5931\u8D25",er="\u8BF7\u6C42\u8D85\u65F6",ar="\u4E86\u89E3\u66F4\u591A",tr="\u5438\u9644\u5230\u4EF7\u683C\u6C34\u5E73",rr="\u4E0D\u5438\u9644",or="\u811A\u672C\u7F16\u8F91\u5668",ir="\u7F16\u5199 JavaScript \u2014 \u4F7F\u7528 Pine Script \u98CE\u683C\u7684 API\uFF08ta.* \u8F85\u52A9\u51FD\u6570\uFF09",lr="\u7F16\u8BD1",nr="\u6DFB\u52A0\u5230\u56FE\u8868",_r="\u5DF2\u7F16\u8BD1",cr="API \u53C2\u8003",sr="\u81EA\u9009\u80A1",dr="\u65B0\u5EFA\u81EA\u9009",hr="\u6DFB\u52A0\u81EA\u9009\u5217\u8868",gr="\u5220\u9664\u81EA\u9009\u5217\u8868",mr="\u6DFB\u52A0",pr="\u79FB\u9664",br="\u8F93\u5165\u4EE3\u7801\u2026",vr={indicator:t,main_indicator:r,sub_indicator:o,setting:i,timezone:l,screenshot:n,full_screen:_,exit_full_screen:c,save:s,confirm:d,color:h,show:g,cancel:m,ma:p,ema:b,sma:v,boll:u,bbi:w,sar:f,vol:C,macd:k,kdj:y,rsi:S,bias:T,brar:x,cci:A,dmi:P,cr:L,psy:R,dma:U,trix:B,obv:M,vr:I,wr:V,mtm:D,emv:W,roc:E,pvt:F,ao:O,utc:z,honolulu:H,juneau:J,los_angeles:K,chicago:G,toronto:j,sao_paulo:N,london:X,berlin:q,bahrain:Y,dubai:Q,ashkhabad:Z,almaty:$,bangkok:ee,shanghai:ae,tokyo:te,sydney:re,norfolk:oe,horizontal_straight_line:ie,horizontal_ray_line:le,horizontal_segment:ne,vertical_straight_line:_e,vertical_ray_line:ce,vertical_segment:se,straight_line:de,ray_line:he,segment:ge,arrow:me,price_line:pe,price_channel_line:be,parallel_straight_line:ve,fibonacci_line:ue,fibonacci_segment:we,fibonacci_circle:fe,fibonacci_spiral:Ce,fibonacci_speed_resistance_fan:ke,fibonacci_extension:ye,gann_box:Se,gann_fan:Te,pitchfork:xe,rect:Ae,parallelogram:Pe,circle:Le,triangle:Re,three_waves:Ue,five_waves:Be,eight_waves:Me,any_waves:Ie,abcd:Ve,xabcd:De,weak_magnet:We,strong_magnet:Ee,symbol_search:Fe,symbol_code:Oe,params_1:ze,params_2:He,params_3:Je,params_4:Ke,params_5:Ge,line_1:je,line_2:Ne,line_3:Xe,line_4:qe,line_5:Ye,period:Qe,standard_deviation:Ze,candle_type:$e,candle_solid:ea,candle_stroke:aa,candle_up_stroke:ta,candle_down_stroke:ra,ohlc:oa,area:ia,last_price_show:la,high_price_show:na,low_price_show:_a,indicator_last_value_show:ca,price_axis_type:sa,normal:da,percentage:ha,log:ga,reverse_coordinate:ma,grid_show:pa,restore_default:ba,alerts:va,alert_create:ua,alert_create_alert:wa,alert_create_on:fa,alert_save:Ca,alert_history:ka,alert_condition:ya,alert_price:Sa,alert_source:Ta,alert_source_price:xa,alert_operator:Aa,alert_value:Pa,alert_crosses:La,alert_is_between:Ra,alert_add_condition:Ua,alert_note:Ba,alert_note_placeholder:Ma,alert_add:Ia,alert_create_button:Va,alert_submitting:Da,alert_saving:Wa,alert_invalid_price:Ea,alert_invalid_condition:Fa,alert_quota_exceeded:Oa,alert_retry:za,alert_active:Ha,alert_no_active:Ja,alert_no_history:Ka,alert_reactivate:Ga,alert_pause:ja,alert_resume:Na,alert_edit:Xa,alert_delete:qa,alert_search:Ya,alert_filter_all:Qa,alert_filter_current:Za,alert_paused:$a,alert_expired:et,alert_dismissed:at,alert_above:tt,alert_below:rt,alert_crosses_above:ot,alert_crosses_below:it,alert_trigger:lt,alert_trigger_once:nt,alert_trigger_every_bar:_t,alert_trigger_bar_close:ct,alert_expiration:st,alert_expiration_open:dt,alert_expiration_1d:ht,alert_expiration_1w:gt,alert_expiration_1m:mt,alert_expiration_2m:pt,alert_message:bt,alert_message_hint:vt,alert_webhook_body:ut,alert_notifications:wt,alert_back:ft,alert_apply:Ct,alert_notif_app:kt,alert_notif_app_desc:yt,alert_notif_toast:St,alert_notif_toast_desc:Tt,alert_notif_email:xt,alert_notif_email_desc:At,alert_notif_webhook:Pt,alert_notif_webhook_desc:Lt,alert_notif_sound:Rt,alert_notif_sound_desc:Ut,alert_notif_plaintext:Bt,alert_notif_plaintext_desc:Mt,alert_notif_none:It,alert_webhook_valid:Vt,alert_webhook_invalid:Dt,alert_sound_once:Wt,alert_sound_repeating:Et,alert_schedule:Ft,alert_schedule_desc:Ot,"alert_schedule_24/7":zt,alert_schedule_weekdays:Ht,alert_schedule_working_hours:Jt,alert_schedule_custom:Kt,alert_webhook_delivered:Gt,alert_webhook_failed:jt,alert_webhook_sending:Nt,alert_log:Xt,alert_clear_log:qt,alert_actions:Yt,alert_no_log:Qt,alert_webhook_success:Zt,alert_webhook_failed_prefix:$t,alert_webhook_timeout:er,alert_webhook_learn_more:ar,snap_levels:tr,no_magnet:rr,script_editor:or,script_editor_hint:ir,script_compile:lr,script_apply:nr,script_compiled:_r,script_api_ref:cr,watchlist:sr,watchlist_new:dr,watchlist_add_list:hr,watchlist_delete_list:gr,watchlist_add_symbol:mr,watchlist_remove_symbol:pr,watchlist_enter_symbol:br};
|
|
3
|
+
export{Ve as abcd,tt as alert_above,Yt as alert_actions,Ha as alert_active,Ia as alert_add,Ua as alert_add_condition,Ct as alert_apply,ft as alert_back,rt as alert_below,qt as alert_clear_log,ya as alert_condition,ua as alert_create,wa as alert_create_alert,Va as alert_create_button,fa as alert_create_on,La as alert_crosses,ot as alert_crosses_above,it as alert_crosses_below,qa as alert_delete,at as alert_dismissed,Xa as alert_edit,st as alert_expiration,ht as alert_expiration_1d,mt as alert_expiration_1m,gt as alert_expiration_1w,pt as alert_expiration_2m,dt as alert_expiration_open,et as alert_expired,Qa as alert_filter_all,Za as alert_filter_current,ka as alert_history,Fa as alert_invalid_condition,Ea as alert_invalid_price,Ra as alert_is_between,Xt as alert_log,bt as alert_message,vt as alert_message_hint,Ja as alert_no_active,Ka as alert_no_history,Qt as alert_no_log,Ba as alert_note,Ma as alert_note_placeholder,kt as alert_notif_app,yt as alert_notif_app_desc,xt as alert_notif_email,At as alert_notif_email_desc,It as alert_notif_none,Bt as alert_notif_plaintext,Mt as alert_notif_plaintext_desc,Rt as alert_notif_sound,Ut as alert_notif_sound_desc,St as alert_notif_toast,Tt as alert_notif_toast_desc,Pt as alert_notif_webhook,Lt as alert_notif_webhook_desc,wt as alert_notifications,Aa as alert_operator,ja as alert_pause,$a as alert_paused,Sa as alert_price,Oa as alert_quota_exceeded,Ga as alert_reactivate,Na as alert_resume,za as alert_retry,Ca as alert_save,Wa as alert_saving,Ft as alert_schedule,zt as "alert_schedule_24/7",Kt as alert_schedule_custom,Ot as alert_schedule_desc,Ht as alert_schedule_weekdays,Jt as alert_schedule_working_hours,Ya as alert_search,Wt as alert_sound_once,Et as alert_sound_repeating,Ta as alert_source,xa as alert_source_price,Da as alert_submitting,lt as alert_trigger,ct as alert_trigger_bar_close,_t as alert_trigger_every_bar,nt as alert_trigger_once,Pa as alert_value,ut as alert_webhook_body,Gt as alert_webhook_delivered,jt as alert_webhook_failed,$t as alert_webhook_failed_prefix,Dt as alert_webhook_invalid,ar as alert_webhook_learn_more,Nt as alert_webhook_sending,Zt as alert_webhook_success,er as alert_webhook_timeout,Vt as alert_webhook_valid,va as alerts,$ as almaty,Ie as any_waves,O as ao,ia as area,me as arrow,Z as ashkhabad,Y as bahrain,ee as bangkok,w as bbi,q as berlin,T as bias,u as boll,x as brar,m as cancel,ra as candle_down_stroke,ea as candle_solid,aa as candle_stroke,$e as candle_type,ta as candle_up_stroke,A as cci,G as chicago,Le as circle,h as color,d as confirm,L as cr,vr as default,U as dma,P as dmi,Q as dubai,Me as eight_waves,b as ema,W as emv,c as exit_full_screen,fe as fibonacci_circle,ye as fibonacci_extension,ue as fibonacci_line,we as fibonacci_segment,ke as fibonacci_speed_resistance_fan,Ce as fibonacci_spiral,Be as five_waves,_ as full_screen,Se as gann_box,Te as gann_fan,pa as grid_show,na as high_price_show,H as honolulu,le as horizontal_ray_line,ne as horizontal_segment,ie as horizontal_straight_line,t as indicator,ca as indicator_last_value_show,J as juneau,y as kdj,la as last_price_show,je as line_1,Ne as line_2,Xe as line_3,qe as line_4,Ye as line_5,ga as log,X as london,K as los_angeles,_a as low_price_show,p as ma,k as macd,r as main_indicator,D as mtm,rr as no_magnet,oe as norfolk,da as normal,M as obv,oa as ohlc,ve as parallel_straight_line,Pe as parallelogram,ze as params_1,He as params_2,Je as params_3,Ke as params_4,Ge as params_5,ha as percentage,Qe as period,xe as pitchfork,sa as price_axis_type,be as price_channel_line,pe as price_line,R as psy,F as pvt,he as ray_line,Ae as rect,ba as restore_default,ma as reverse_coordinate,E as roc,S as rsi,N as sao_paulo,f as sar,s as save,n as screenshot,cr as script_api_ref,nr as script_apply,lr as script_compile,_r as script_compiled,or as script_editor,ir as script_editor_hint,ge as segment,i as setting,ae as shanghai,g as show,v as sma,tr as snap_levels,Ze as standard_deviation,de as straight_line,Ee as strong_magnet,o as sub_indicator,re as sydney,Oe as symbol_code,Fe as symbol_search,Ue as three_waves,l as timezone,te as tokyo,j as toronto,Re as triangle,B as trix,z as utc,ce as vertical_ray_line,se as vertical_segment,_e as vertical_straight_line,C as vol,I as vr,sr as watchlist,hr as watchlist_add_list,mr as watchlist_add_symbol,gr as watchlist_delete_list,br as watchlist_enter_symbol,dr as watchlist_new,pr as watchlist_remove_symbol,We as weak_magnet,V as wr,De as xabcd};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Mirror Plan: Symbol Search + Watchlist Parity
|
|
2
|
+
|
|
3
|
+
> **Status:** Phases 2–5 executed 2026-07-13 (commit `c14a88c` local / `4bfe8fd` server). **Phase 1 (dedicated capture scripts) was not executed** — see note below.
|
|
4
|
+
> **Date:** 2026-07-13
|
|
5
|
+
> **CDP source:** Chrome port 9223, chart `8Wh6yRIo`, BTC.D active
|
|
6
|
+
> **Scope:** Capture live TV DOM for Symbol Search + Watchlist; implement functional parity in Astroneum
|
|
7
|
+
>
|
|
8
|
+
> **Execution note:** Phase 1's `cdp-symbol-search.js` / `cdp-watchlist.js` / `ref-symbol-search.html` / `ref-watchlist.html` were never created. The geometry and DOM structure used for Phases 2–5 came from ad hoc CDP `Runtime.evaluate` calls during the working session, recorded inline in `tv-mirror-reference/structure.md` §4.1 and `watchlist.md` §2.4/§3/§8. This is an honest gap, not a silent one: the values are real (captured live), but the capture is not re-runnable from a checked-in script. See `tv-mirror-reference/README.md` for the current, corrected evidence-files table. A follow-up should build the two capture scripts before the next visual refresh.
|
|
9
|
+
>
|
|
10
|
+
> **What actually shipped (Phases 2–3):** rewrote `SymbolSearchModal` (840px, 11 asset tabs, 4-cell rows, debounce, token-driven styling) and fixed the `Modal`/`Input` primitives it depends on (initial-focus targeting, native attribute forwarding, removed a hardcoded TradingView-blue focus glow). Rebuilt the Watchlist header to TV's actual 4-control anatomy (list selector / Add symbol / Advanced view / Settings) instead of the previous 5-tab layout, added collapsible group headers, widened the sidebar, and wired a working resize handle. Full detail in `docs/TODO-DESIGN.md` changelog, entry "2026-07-13 (c)".
|
|
11
|
+
>
|
|
12
|
+
> **What did not ship:** the automated visual-regression gate (Phase 5 in spirit, never formally scoped here) that the accompanying UX audit called for. Verification for this pass was `pnpm verify` + demo build + CDP computed-style comparison, not a screenshot-diff CI gate.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
Both surfaces were inspected live via CDP on 2026-07-13.
|
|
19
|
+
|
|
20
|
+
### Symbol Search is a shared library component
|
|
21
|
+
|
|
22
|
+
`src/widget/symbol-search-modal/index.tsx` is consumed by two entry points:
|
|
23
|
+
- `AstroneumChart.tsx:656` — chart symbol change (`chart.setSymbol()`)
|
|
24
|
+
- `WatchlistPanel.tsx:219` — add to watchlist (`manager.addSymbolFromInfo()`)
|
|
25
|
+
|
|
26
|
+
Rewriting it improves both entry points simultaneously.
|
|
27
|
+
|
|
28
|
+
### Current state
|
|
29
|
+
|
|
30
|
+
| Surface | File | Lines | Gap |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| Symbol Search (library) | `src/widget/symbol-search-modal/index.tsx` | 100 | Large — 460px, no tabs, no row structure, no debounce |
|
|
33
|
+
| Symbol Search SCSS | `src/widget/symbol-search-modal/index.scss` | 42 | Layout for 460px modal |
|
|
34
|
+
| Watchlist (demo) | `demo/src/app/components/panels/WatchlistPanel.tsx` | 514 | Small — missing section group headers |
|
|
35
|
+
| Watchlist CSS | `demo/src/app/components/panels/panels.css` L1-286 | 286 | Mature |
|
|
36
|
+
| WatchlistManager (library) | `src/chart/WatchlistManager.ts` | 244 | Stable — add `group` field |
|
|
37
|
+
|
|
38
|
+
### Live TV DOM (observed via CDP 2026-07-13)
|
|
39
|
+
|
|
40
|
+
**Symbol Search dialog** — `[data-name="symbol-search-items-dialog"]` role=dialog, 840x680px:
|
|
41
|
+
```
|
|
42
|
+
Title "Symbol search" + Close [data-qa-id="close"]
|
|
43
|
+
Input [data-qa-id="symbol-search-input"] role=searchbox, placeholder="Symbol, ISIN, or CUSIP"
|
|
44
|
+
Tabs [role=tablist]: All(active) / Stocks / Funds / Futures / Forex / Crypto / Indices / Bonds / Economy / Options / More
|
|
45
|
+
Results: [data-name="symbol-search-dialog-content-item"] rows, 4 cells:
|
|
46
|
+
1. ticker (bold, e.g. "BTCUSD")
|
|
47
|
+
2. description (muted, e.g. "Bitcoin / U.S. dollar")
|
|
48
|
+
3. exchange (smaller, e.g. "spot crypto defiBitstamp")
|
|
49
|
+
4. actions (empty, hover-revealed)
|
|
50
|
+
51 rows in listContainer, active row has indigo tint
|
|
51
|
+
Footer: "Search using ISIN and CUSIP codes"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Watchlist panel** — rail `[data-name="base"]` 44x44px aria-pressed=true:
|
|
55
|
+
```
|
|
56
|
+
Header:
|
|
57
|
+
[data-name="watchlists-button"] "Red list" 102x34px
|
|
58
|
+
[data-name="add-symbol-button"] aria-label="Add symbol" 38x38px
|
|
59
|
+
[data-name="advanced-view-button"] aria-label="Advanced view" 38x38px
|
|
60
|
+
[data-name="settings-button"] aria-label="Settings" 38x38px
|
|
61
|
+
Columns: Symbol / Last / Chg / Chg% (sortable buttons, class="sortable-PYr1BHmD", NO aria-sort on TV)
|
|
62
|
+
Rows: [data-name="symbol-list-wrap"] 319x775px
|
|
63
|
+
Each row: ticker + "Market open" status + last + chg + chg% + volume
|
|
64
|
+
Section dividers: plain text group headers (e.g. "US STOCK")
|
|
65
|
+
Details: details-add-note / details-settings / details-more-technicals
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Phase 1: CDP Capture
|
|
71
|
+
|
|
72
|
+
### 1A. Create two targeted capture scripts
|
|
73
|
+
|
|
74
|
+
| Script | Output | Target |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `cdp-symbol-search.js` | `ref-symbol-search.html` + `symbol-search.png` | `[data-name="symbol-search-items-dialog"]` outerHTML + screenshot |
|
|
77
|
+
| `cdp-watchlist.js` | `ref-watchlist.html` + `watchlist.png` | Watchlist panel (header + columns + rows) outerHTML + screenshot |
|
|
78
|
+
|
|
79
|
+
Both reuse the existing CDP pattern (port 9223, WebSocket, `Runtime.evaluate` + `Page.captureScreenshot`). Output only to `tv-mirror-reference/`.
|
|
80
|
+
|
|
81
|
+
### 1B. Update reference docs
|
|
82
|
+
|
|
83
|
+
| File | Section | Change |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `tv-mirror-reference/watchlist.md` | 2.4, 3, 8 | Fill `not captured` items: row DOM (4 cells per row), section group headers ("US STOCK"), "Market open" status, details buttons |
|
|
86
|
+
| `tv-mirror-reference/structure.md` | 4.1 | Update Symbol Search: 840px width, 11 tabs, 4-cell rows, footer text, selectors |
|
|
87
|
+
| `tv-mirror-reference/README.md` | Evidence files table | Add `ref-symbol-search.html`, `ref-watchlist.html` rows |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Phase 2: Symbol Search Rewrite (library-level)
|
|
92
|
+
|
|
93
|
+
### 2A. Component rewrite — `src/widget/symbol-search-modal/index.tsx`
|
|
94
|
+
|
|
95
|
+
| # | Change | Why |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| S1 | Width 460px to 840px | Fit 4-cell rows |
|
|
98
|
+
| S2 | Asset-class tabs: All / Stocks / Funds / Futures / Forex / Crypto / Indices / Bonds / Economy / Options / More | Core TV function — filter results by `symbol.type` |
|
|
99
|
+
| S3 | 4-cell result rows: ticker / description / exchange / actions | TV row structure |
|
|
100
|
+
| S4 | Keyboard up/down navigation through results | Functional — can't use dialog without it |
|
|
101
|
+
| S5 | 200ms debounce on input | Prevents API hammering (Polygon) |
|
|
102
|
+
| S6 | Footer "Search using ISIN and CUSIP codes" | Part of the dialog |
|
|
103
|
+
|
|
104
|
+
`SymbolInfo.type` is `string` (not union) — tab filter maps common values client-side: `stock/equity` to Stocks, `crypto` to Crypto, `forex/fx` to Forex, etc. Undefined `type` shows in "All" only.
|
|
105
|
+
|
|
106
|
+
### 2B. Props interface — no breaking change
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
export interface SymbolSearchModalProps {
|
|
110
|
+
locale: string
|
|
111
|
+
searchSymbols: (query?: string) => Promise<SymbolInfo[]>
|
|
112
|
+
onSymbolSelected: (symbol: SymbolInfo) => void
|
|
113
|
+
onClose: () => void
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
No change to `Datafeed.searchSymbols` signature — filtering by `type` happens client-side on returned results.
|
|
118
|
+
|
|
119
|
+
### 2C. SCSS rewrite — `src/widget/symbol-search-modal/index.scss`
|
|
120
|
+
|
|
121
|
+
- 840px width, max-height with internal scroll on results
|
|
122
|
+
- Tab bar: horizontal scroll, 28px buttons, active underline
|
|
123
|
+
- Result rows: 4-cell grid layout (ticker | description | exchange | actions)
|
|
124
|
+
- Active row: `--astroneum-accent-tint` background
|
|
125
|
+
- Footer: muted, 12px, centered
|
|
126
|
+
|
|
127
|
+
### 2D. i18n — `src/i18n/en-US.json`
|
|
128
|
+
|
|
129
|
+
New keys (en-US first, 17 other locales fall back):
|
|
130
|
+
```json
|
|
131
|
+
"symbol_search_all": "All",
|
|
132
|
+
"symbol_search_stocks": "Stocks",
|
|
133
|
+
"symbol_search_funds": "Funds",
|
|
134
|
+
"symbol_search_futures": "Futures",
|
|
135
|
+
"symbol_search_forex": "Forex",
|
|
136
|
+
"symbol_search_crypto": "Crypto",
|
|
137
|
+
"symbol_search_indices": "Indices",
|
|
138
|
+
"symbol_search_bonds": "Bonds",
|
|
139
|
+
"symbol_search_economy": "Economy",
|
|
140
|
+
"symbol_search_options": "Options",
|
|
141
|
+
"symbol_search_more": "More",
|
|
142
|
+
"symbol_search_footer": "Search using ISIN and CUSIP codes"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 2E. No subpath export needed
|
|
146
|
+
|
|
147
|
+
`SymbolSearchModal` rides the root barrel (`src/index.ts:82`). No `src/entries/` or `package.json` change.
|
|
148
|
+
|
|
149
|
+
### 2F. Demo consumer fix — `WatchlistPanel.tsx:219`
|
|
150
|
+
|
|
151
|
+
Pass `locale` prop instead of hardcoded `"en-US"`.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Phase 3: Watchlist — section group headers
|
|
156
|
+
|
|
157
|
+
### 3A. `WatchlistManager.ts` — add `group` field
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
export interface WatchSymbol {
|
|
161
|
+
ticker: string
|
|
162
|
+
// ... existing fields ...
|
|
163
|
+
group?: string // section header label (e.g. "US STOCK")
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`group` persists (not stripped by `_storedSymbol` — it's user-organized, not injected like quotes). Add to the whitelist in `_storedSymbol` (L196-206).
|
|
168
|
+
|
|
169
|
+
### 3B. Render section dividers — `WatchlistPanel.tsx`
|
|
170
|
+
|
|
171
|
+
When iterating sorted symbols, detect `group` changes and insert a non-interactive divider row with the group label text.
|
|
172
|
+
|
|
173
|
+
### 3C. Divider row style — `panels.css`
|
|
174
|
+
|
|
175
|
+
```css
|
|
176
|
+
.term-wl-group-header {
|
|
177
|
+
padding: 8px 12px;
|
|
178
|
+
font-size: 11px;
|
|
179
|
+
font-weight: 600;
|
|
180
|
+
color: var(--term-text-2);
|
|
181
|
+
text-transform: uppercase;
|
|
182
|
+
letter-spacing: 0.04em;
|
|
183
|
+
background: var(--term-panel-bg);
|
|
184
|
+
position: sticky;
|
|
185
|
+
top: 0;
|
|
186
|
+
z-index: 1;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Phase 4: Docs Sync
|
|
193
|
+
|
|
194
|
+
| File | Section | Change |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `docs/TODO-DESIGN.md` | 7.6 | Symbol Search: update spec (840px, tabs, 4-cell rows) |
|
|
197
|
+
| `docs/TODO-DESIGN.md` | 3b | Add section group header row |
|
|
198
|
+
| `docs/TODO-DESIGN.md` | 11.10 | Symbol search result-state parity: mark progress |
|
|
199
|
+
| `docs/design-astroneum.md` | Symbol Search Pill | Update width 640 to 840px, add tab + 4-cell row spec |
|
|
200
|
+
| `docs/design-astroneum.md` | Watchlist Panel | Add section group headers |
|
|
201
|
+
| `tv-mirror-reference/watchlist.md` | 2.4, 3, 8 | Fill `not captured` items from CDP |
|
|
202
|
+
| `tv-mirror-reference/structure.md` | 4.1 | Update Symbol Search DOM |
|
|
203
|
+
| `tv-mirror-reference/README.md` | Evidence table | Add new capture files |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Phase 5: Verification
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
pnpm verify (= pnpm lint && pnpm typecheck && pnpm build && pnpm test)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Must pass before any deploy. No deploy in this plan — implementation only.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Execution Order
|
|
218
|
+
|
|
219
|
+
1. Phase 1A — CDP capture scripts (2 new files in `tv-mirror-reference/`)
|
|
220
|
+
2. Phase 2A-2F — Symbol Search rewrite (library: tsx + scss + i18n; demo: locale fix)
|
|
221
|
+
3. Phase 3A-3C — Watchlist section group headers (library: `group` field; demo: divider rows + CSS)
|
|
222
|
+
4. Phase 4 — Docs sync
|
|
223
|
+
5. Phase 5 — `pnpm verify`
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Files Touched
|
|
228
|
+
|
|
229
|
+
| File | Phase | Change |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `tv-mirror-reference/cdp-symbol-search.js` | 1A | New — CDP capture script |
|
|
232
|
+
| `tv-mirror-reference/cdp-watchlist.js` | 1A | New — CDP capture script |
|
|
233
|
+
| `tv-mirror-reference/ref-symbol-search.html` | 1A | New — capture output |
|
|
234
|
+
| `tv-mirror-reference/ref-watchlist.html` | 1A | New — capture output |
|
|
235
|
+
| `tv-mirror-reference/structure.md` | 1B | Update 4.1 |
|
|
236
|
+
| `tv-mirror-reference/watchlist.md` | 1B | Update 2.4, 3, 8 |
|
|
237
|
+
| `tv-mirror-reference/README.md` | 1B | Update evidence table |
|
|
238
|
+
| `src/widget/symbol-search-modal/index.tsx` | 2A | Rewrite — tabs, 4-cell rows, debounce, keyboard nav |
|
|
239
|
+
| `src/widget/symbol-search-modal/index.scss` | 2C | Rewrite — 840px layout |
|
|
240
|
+
| `src/i18n/en-US.json` | 2D | 12 new keys |
|
|
241
|
+
| `demo/src/app/components/panels/WatchlistPanel.tsx` | 2F, 3B | Locale fix + section dividers |
|
|
242
|
+
| `demo/src/app/components/panels/panels.css` | 3C | Group header style |
|
|
243
|
+
| `src/chart/WatchlistManager.ts` | 3A | Add `group` field |
|
|
244
|
+
| `docs/TODO-DESIGN.md` | 4 | Update 7.6, 3b, 11.10 |
|
|
245
|
+
| `docs/design-astroneum.md` | 4 | Update Symbol Search + Watchlist sections |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Risks
|
|
250
|
+
|
|
251
|
+
| Risk | Mitigation |
|
|
252
|
+
|---|---|
|
|
253
|
+
| `SymbolInfo.type` is `string` not union — tab filter may miss non-standard values | Client-side filter: if `type` undefined, show in "All" only; map common values |
|
|
254
|
+
| No debounce currently — Polygon API rate limits | 200ms debounce + cancel in-flight on new keystroke |
|
|
255
|
+
| `group` field on `WatchSymbol` needs migration for existing localStorage | `_load()` already validates; add `group` with fallback `undefined` |
|