@tradecanvas/svelte 1.0.15 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ Streams live Binance data by default. Pass `data={bars}` for static OHLC, or `ad
35
35
 
36
36
  ## Props
37
37
 
38
- `symbol`, `timeframe`, `theme`, `chartType`, `indicators`, `data`, `adapter`, `historyLimit`, `features`, `autoScale`, `signalMarkers`, `signalMarkerStyle`, `tradeZones`, `tradeZoneStyle`, `watermarkText`, `onReady`, and `bind:chart`.
38
+ `symbol`, `timeframe`, `theme`, `chartType`, `indicators` (ids, or `{ id, params, position }`), `data` (bars of your own; no stream), `adapter`, `stream` (`false`: no stream of its own), `historyLimit`, `features` (changes apply after mount too), `autoScale`, `signalMarkers`, `signalMarkerStyle`, `tradeZones`, `tradeZoneStyle`, `overrides` (style overrides by key, all of your app's), `watermarkText`, `onReady`, and `bind:chart`.
39
39
 
40
40
  Reach the underlying `Chart` (drawings, trading, execution adapters, plugins, resizable panes) via `bind:chart` or `onReady`. See the [main docs](https://github.com/bonguynvan/tradecanvas).
41
41
 
@@ -5,6 +5,7 @@
5
5
  BinanceAdapter,
6
6
  DARK_THEME,
7
7
  LIGHT_THEME,
8
+ syncIndicators,
8
9
  } from '@tradecanvas/chart';
9
10
  import type {
10
11
  ChartType,
@@ -17,6 +18,8 @@
17
18
  SignalMarkerStyle,
18
19
  TradeZoneStyle,
19
20
  DataAdapter,
21
+ ChartStyleOverrides,
22
+ IndicatorSpec,
20
23
  } from '@tradecanvas/chart';
21
24
 
22
25
  interface Props {
@@ -24,16 +27,23 @@
24
27
  timeframe?: TimeFrame;
25
28
  theme?: 'dark' | 'light' | Theme;
26
29
  chartType?: ChartType;
27
- indicators?: string[];
30
+ /** Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`). */
31
+ indicators?: IndicatorSpec[];
32
+ /** Static bars: the chart shows these, and opens no stream of its own when it has them at mount. */
28
33
  data?: OHLCBar[];
29
34
  adapter?: DataAdapter;
35
+ /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */
36
+ stream?: boolean;
30
37
  historyLimit?: number;
38
+ /** What users can do on the chart; changes after mount are applied too (the keys given). */
31
39
  features?: FeaturesConfig;
32
40
  autoScale?: boolean;
33
41
  signalMarkers?: SignalMarker[];
34
42
  signalMarkerStyle?: SignalMarkerStyle;
35
43
  tradeZones?: TradeZone[];
36
44
  tradeZoneStyle?: TradeZoneStyle;
45
+ /** Style overrides, by key: all of your app's (`chart.setOverrides`). */
46
+ overrides?: ChartStyleOverrides;
37
47
  watermarkText?: string;
38
48
  onReady?: (chart: Chart) => void;
39
49
  chart?: Chart | null;
@@ -44,9 +54,10 @@
44
54
  timeframe = '5m' as TimeFrame,
45
55
  theme = 'dark' as 'dark' | 'light' | Theme,
46
56
  chartType = 'candlestick' as ChartType,
47
- indicators = [] as string[],
57
+ indicators = [] as IndicatorSpec[],
48
58
  data = undefined as OHLCBar[] | undefined,
49
59
  adapter = undefined as DataAdapter | undefined,
60
+ stream = true,
50
61
  historyLimit = 500,
51
62
  features = undefined as FeaturesConfig | undefined,
52
63
  autoScale = true,
@@ -54,6 +65,7 @@
54
65
  signalMarkerStyle = undefined as SignalMarkerStyle | undefined,
55
66
  tradeZones = undefined as TradeZone[] | undefined,
56
67
  tradeZoneStyle = undefined as TradeZoneStyle | undefined,
68
+ overrides = undefined as ChartStyleOverrides | undefined,
57
69
  watermarkText = undefined as string | undefined,
58
70
  onReady = undefined as ((chart: Chart) => void) | undefined,
59
71
  chart = $bindable(null) as Chart | null,
@@ -68,8 +80,10 @@
68
80
  let container: HTMLDivElement;
69
81
  let instance: Chart | null = null;
70
82
  const indicatorIds = new Map<string, string>();
71
- let prevSymbol = symbol;
72
- let prevTimeframe = timeframe;
83
+ /** The stream open now (`symbol|timeframe`), or null. */
84
+ let streamKey: string | null = null;
85
+ /** The features last applied, as text. */
86
+ let featuresKey: string | null = null;
73
87
 
74
88
  onMount(() => {
75
89
  instance = new Chart(container, {
@@ -104,10 +118,12 @@
104
118
 
105
119
  if (data) {
106
120
  instance.setData(data);
107
- } else {
121
+ } else if (stream) {
108
122
  const dataAdapter = adapter ?? new BinanceAdapter();
109
123
  instance.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });
124
+ streamKey = `${symbol}|${timeframe}`;
110
125
  }
126
+ featuresKey = JSON.stringify($state.snapshot(features) ?? null);
111
127
 
112
128
  onReady?.(instance);
113
129
  });
@@ -121,44 +137,55 @@
121
137
  }
122
138
  });
123
139
 
140
+ // A stream of its own while it has no data; `stream={false}` closes it. Data given later leaves it be.
124
141
  $effect(() => {
125
- if (!instance || data) return;
126
- const _s = symbol;
127
- const _tf = timeframe;
128
- if (_s === prevSymbol && _tf === prevTimeframe) return;
129
- prevSymbol = _s;
130
- prevTimeframe = _tf;
142
+ const key = `${symbol}|${timeframe}`;
143
+ const on = stream;
144
+ const hasData = !!data;
145
+ if (!instance) return;
146
+ if (!on) {
147
+ if (streamKey !== null) instance.disconnectStream();
148
+ streamKey = null;
149
+ return;
150
+ }
151
+ if (hasData || streamKey === key) return;
152
+ streamKey = key;
131
153
  instance.disconnectStream();
132
154
  const dataAdapter = adapter ?? new BinanceAdapter();
133
- instance.connect({ adapter: dataAdapter, symbol: _s, timeframe: _tf, historyLimit });
155
+ instance.connect({ adapter: dataAdapter, symbol, timeframe, historyLimit });
134
156
  if (watermarkText) instance.setWatermark(watermarkText);
135
157
  });
136
158
 
159
+ $effect(() => {
160
+ const next = $state.snapshot(features);
161
+ const key = JSON.stringify(next ?? null);
162
+ if (!instance || key === featuresKey) return;
163
+ featuresKey = key;
164
+ if (next) instance.setFeatures(next);
165
+ });
166
+
137
167
  $effect(() => { instance?.setTheme(resolveTheme(theme)); });
138
168
  $effect(() => { instance?.setChartType(chartType); });
139
169
  $effect(() => { if (data) instance?.setData(data); });
140
170
 
141
171
  $effect(() => {
142
- if (!instance) return;
143
- const desired = new Set(indicators);
144
- for (const [name, instanceId] of indicatorIds) {
145
- if (!desired.has(name)) {
146
- instance.removeIndicator(instanceId);
147
- indicatorIds.delete(name);
148
- }
149
- }
150
- for (const name of indicators) {
151
- if (!indicatorIds.has(name)) {
152
- const instanceId = instance.addIndicator(name);
153
- if (instanceId) indicatorIds.set(name, instanceId);
154
- }
155
- }
172
+ const specs = $state.snapshot(indicators) as IndicatorSpec[];
173
+ if (instance) syncIndicators(instance, specs, indicatorIds);
156
174
  });
157
175
 
158
176
  $effect(() => { if (signalMarkers) instance?.setSignalMarkers(signalMarkers); });
159
177
  $effect(() => { if (signalMarkerStyle) instance?.setSignalMarkerStyle(signalMarkerStyle); });
160
178
  $effect(() => { if (tradeZones) instance?.setTradeZones(tradeZones); });
161
179
  $effect(() => { if (tradeZoneStyle) instance?.setTradeZoneStyle(tradeZoneStyle); });
180
+ // All of the host's overrides: keys left out of the prop are taken away.
181
+ // Without the prop, overrides set through the chart itself are left alone.
182
+ let overridesGiven = false;
183
+ $effect(() => {
184
+ const next = $state.snapshot(overrides);
185
+ if (next === undefined && !overridesGiven) return;
186
+ overridesGiven = next !== undefined;
187
+ instance?.setOverrides(next ?? {});
188
+ });
162
189
  </script>
163
190
 
164
191
  <div bind:this={container} style="width:100%;height:100%"></div>
@@ -1,20 +1,27 @@
1
1
  import { Chart } from '@tradecanvas/chart';
2
- import type { ChartType, TimeFrame, FeaturesConfig, Theme, OHLCBar, SignalMarker, TradeZone, SignalMarkerStyle, TradeZoneStyle, DataAdapter } from '@tradecanvas/chart';
2
+ import type { ChartType, TimeFrame, FeaturesConfig, Theme, OHLCBar, SignalMarker, TradeZone, SignalMarkerStyle, TradeZoneStyle, DataAdapter, ChartStyleOverrides, IndicatorSpec } from '@tradecanvas/chart';
3
3
  interface Props {
4
4
  symbol?: string;
5
5
  timeframe?: TimeFrame;
6
6
  theme?: 'dark' | 'light' | Theme;
7
7
  chartType?: ChartType;
8
- indicators?: string[];
8
+ /** Indicators by id (`'rsi'`), or with their inputs (`{ id: 'ema', params: { period: 50 } }`). */
9
+ indicators?: IndicatorSpec[];
10
+ /** Static bars: the chart shows these, and opens no stream of its own when it has them at mount. */
9
11
  data?: OHLCBar[];
10
12
  adapter?: DataAdapter;
13
+ /** `false`: no stream until you say so (no Binance by default); the chart waits for `data`. Default `true`. */
14
+ stream?: boolean;
11
15
  historyLimit?: number;
16
+ /** What users can do on the chart; changes after mount are applied too (the keys given). */
12
17
  features?: FeaturesConfig;
13
18
  autoScale?: boolean;
14
19
  signalMarkers?: SignalMarker[];
15
20
  signalMarkerStyle?: SignalMarkerStyle;
16
21
  tradeZones?: TradeZone[];
17
22
  tradeZoneStyle?: TradeZoneStyle;
23
+ /** Style overrides, by key: all of your app's (`chart.setOverrides`). */
24
+ overrides?: ChartStyleOverrides;
18
25
  watermarkText?: string;
19
26
  onReady?: (chart: Chart) => void;
20
27
  chart?: Chart | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradecanvas/svelte",
3
- "version": "1.0.15",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Svelte 5 component for @tradecanvas/chart — reactive props, runes, zero boilerplate.",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "README.md"
36
36
  ],
37
37
  "dependencies": {
38
- "@tradecanvas/chart": "1.12.0"
38
+ "@tradecanvas/chart": "1.14.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "svelte": ">=5.0.0"