react-native-vroom-chart 0.6.0 → 0.7.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/cpp/VroomChartHostObject.cpp +150 -32
- package/cpp/_core_include/vroom/vroom_chart.h +180 -22
- package/cpp/_core_src/bollinger.h +1 -1
- package/cpp/_core_src/candles.cpp +138 -41
- package/cpp/_core_src/candles.h +11 -1
- package/cpp/_core_src/chart.cpp +91 -40
- package/cpp/_core_src/chart.h +55 -20
- package/cpp/_core_src/chart_facade.cpp +206 -66
- package/cpp/_core_src/gradient.cpp +46 -0
- package/cpp/_core_src/gradient.h +31 -0
- package/cpp/_core_src/labels.cpp +49 -16
- package/cpp/_core_src/labels.h +33 -4
- package/cpp/_core_src/liquidity.cpp +3 -37
- package/cpp/_core_src/ma_overlay.cpp +174 -12
- package/cpp/_core_src/ma_overlay.h +55 -3
- package/cpp/_core_src/macd.cpp +13 -42
- package/cpp/_core_src/macd.h +11 -8
- package/cpp/_core_src/macd_pane.cpp +57 -27
- package/cpp/_core_src/price_line_layout.h +1 -1
- package/cpp/_core_src/rsi.cpp +4 -18
- package/cpp/_core_src/rsi.h +6 -6
- package/cpp/_core_src/rsi_pane.cpp +34 -19
- package/cpp/_core_src/series_ma.cpp +64 -0
- package/cpp/_core_src/series_ma.h +30 -0
- package/cpp/_core_src/style_inherit.h +35 -0
- package/cpp/_core_src/theme.cpp +2 -1
- package/cpp/_core_src/viewport.cpp +36 -12
- package/cpp/_core_src/viewport.h +50 -0
- package/cpp/_core_src/volume.cpp +33 -8
- package/cpp/_core_src/volume.h +12 -1
- package/cpp/_core_src/volume_anim.cpp +32 -0
- package/cpp/_core_src/volume_anim.h +41 -0
- package/lib/index.d.mts +161 -28
- package/lib/index.d.ts +161 -28
- package/lib/index.js +156 -31
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +156 -31
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/VroomChart.tsx +69 -3
- package/src/easing.ts +40 -0
- package/src/index.ts +3 -0
- package/src/jsi.d.ts +76 -20
- package/src/theme.ts +1 -0
- package/src/types.ts +3 -0
- package/src/useChartCore.ts +103 -27
package/lib/index.d.ts
CHANGED
|
@@ -82,7 +82,12 @@ type VroomTheme = {
|
|
|
82
82
|
candleRadius?: number;
|
|
83
83
|
/** Round the wick end caps. Defaults to false. */
|
|
84
84
|
wickRoundCap?: boolean;
|
|
85
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Corner radius (px) of the *top* of volume bars. Defaults to 0 (square).
|
|
87
|
+
*
|
|
88
|
+
* @deprecated Use `volume.radius`, which sits with the rest of the volume
|
|
89
|
+
* styling. This still applies when `volume.radius` is omitted.
|
|
90
|
+
*/
|
|
86
91
|
volumeRadius?: number;
|
|
87
92
|
/** Gridlines. */
|
|
88
93
|
grid?: VroomColor;
|
|
@@ -92,10 +97,16 @@ type VroomTheme = {
|
|
|
92
97
|
crosshair?: VroomColor;
|
|
93
98
|
/** Crosshair target — the hollow ring/dot at the intersection. */
|
|
94
99
|
crosshairTarget?: VroomColor;
|
|
95
|
-
/** Line-chart-mode close polyline color. Defaults to
|
|
100
|
+
/** Line-chart-mode close polyline color. Defaults to violet, matching the RSI line. */
|
|
96
101
|
lineColor?: VroomColor;
|
|
97
102
|
/** Line-chart-mode polyline stroke width in px. Defaults to 1.5. */
|
|
98
103
|
lineWidth?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Opacity of the gradient filled beneath the line-chart polyline, at its
|
|
106
|
+
* strongest point. The fill uses `lineColor` and ramps to fully transparent at
|
|
107
|
+
* the bottom of the price pane. Defaults to 0.28; set to 0 to disable the fill.
|
|
108
|
+
*/
|
|
109
|
+
lineGradientOpacity?: number;
|
|
99
110
|
};
|
|
100
111
|
/** A time window over the candle data, as Unix epoch milliseconds. */
|
|
101
112
|
type VisibleRange = {
|
|
@@ -117,6 +128,11 @@ type ChartMode = 'pan' | 'draw';
|
|
|
117
128
|
* overlays, crosshair, and drawings still render.
|
|
118
129
|
*/
|
|
119
130
|
type ChartType = 'candles' | 'line';
|
|
131
|
+
/**
|
|
132
|
+
* Easing curve for animated transitions (candle↔line and interval switches).
|
|
133
|
+
* Defaults to `'ease-in-out'`.
|
|
134
|
+
*/
|
|
135
|
+
type TransitionEasing = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
120
136
|
/** Active drawing tool while in `draw` mode. `null` draws nothing. */
|
|
121
137
|
type DrawTool = null | 'line' | 'box' | 'pencil';
|
|
122
138
|
/** A drawing anchor in data space, so it stays glued to the candles on pan/zoom. */
|
|
@@ -226,8 +242,21 @@ type UndoRedoControls = {
|
|
|
226
242
|
*/
|
|
227
243
|
clearHistory: () => void;
|
|
228
244
|
};
|
|
229
|
-
/**
|
|
245
|
+
/** Price source for a moving average. */
|
|
246
|
+
type MASource = 'close' | 'open' | 'high' | 'low' | 'hl2' | 'hlc3' | 'ohlc4';
|
|
247
|
+
/** Averaging used by a moving average: simple or exponential. */
|
|
248
|
+
type MAKind = 'sma' | 'ema';
|
|
249
|
+
/**
|
|
250
|
+
* RSI indicator config. Rendered in a pane below the candles when enabled: the
|
|
251
|
+
* RSI line, an optional moving-average trendline over it, and two dashed rules
|
|
252
|
+
* at the overbought and oversold levels.
|
|
253
|
+
*
|
|
254
|
+
* RSI reads closes only — Wilder's definition is built on close-to-close
|
|
255
|
+
* change — so unlike the moving-average, Bollinger, and MACD configs it takes
|
|
256
|
+
* no {@link MASource}.
|
|
257
|
+
*/
|
|
230
258
|
type RSIConfig = {
|
|
259
|
+
/** Draw the pane. Default false. */
|
|
231
260
|
enabled?: boolean;
|
|
232
261
|
/** Lookback period in candle counts. Default 14, clamped to >= 2. */
|
|
233
262
|
period?: number;
|
|
@@ -235,23 +264,37 @@ type RSIConfig = {
|
|
|
235
264
|
upperBand?: number;
|
|
236
265
|
/** Oversold band level (0..100). Default 30. */
|
|
237
266
|
lowerBand?: number;
|
|
238
|
-
/** Show the RSI-based moving-average trendline. Default true. */
|
|
239
|
-
maEnabled?: boolean;
|
|
240
267
|
/** Trendline (MA of RSI) length. Default 14, clamped to >= 1. */
|
|
241
268
|
maPeriod?: number;
|
|
269
|
+
/** Averaging used for the trendline ({@link MAKind}). Default 'sma'. */
|
|
270
|
+
maType?: MAKind;
|
|
271
|
+
/** Draw the moving-average trendline. Default true. */
|
|
272
|
+
maVisible?: boolean;
|
|
273
|
+
/** RSI line color (hex string or packed ARGB number). Default violet. */
|
|
274
|
+
lineColor?: string | number;
|
|
275
|
+
/** RSI line stroke width in px. Default 1.5. */
|
|
276
|
+
lineWidth?: number;
|
|
277
|
+
/** Draw the RSI line. Default true. */
|
|
278
|
+
lineVisible?: boolean;
|
|
279
|
+
/** Trendline color. Default amber. */
|
|
280
|
+
maColor?: string | number;
|
|
281
|
+
/** Trendline stroke width in px. Default 1.5. */
|
|
282
|
+
maWidth?: number;
|
|
283
|
+
/** Color of both dashed band rules. Default gray. */
|
|
284
|
+
bandColor?: string | number;
|
|
285
|
+
/** Draw the overbought/oversold rules. Default true. */
|
|
286
|
+
bandsVisible?: boolean;
|
|
242
287
|
};
|
|
243
|
-
/** Price source for a moving average. */
|
|
244
|
-
type MASource = 'close' | 'open' | 'high' | 'low' | 'hl2' | 'hlc3' | 'ohlc4';
|
|
245
288
|
/**
|
|
246
289
|
* A moving-average overlay line drawn on the price pane. Provide an array of
|
|
247
290
|
* these via `movingAverages` to render a ribbon of SMA/EMA lines.
|
|
248
291
|
*/
|
|
249
292
|
type MovingAverageOverlay = {
|
|
250
|
-
/**
|
|
251
|
-
|
|
293
|
+
/** Averaging for this line ({@link MAKind}). */
|
|
294
|
+
maType: MAKind;
|
|
252
295
|
/** Lookback in candles. */
|
|
253
|
-
|
|
254
|
-
/** Price source. Default 'close'. */
|
|
296
|
+
period: number;
|
|
297
|
+
/** Price source ({@link MASource}). Default 'close'. */
|
|
255
298
|
source?: MASource;
|
|
256
299
|
/** Line color (hex string or packed ARGB number). */
|
|
257
300
|
color?: string | number;
|
|
@@ -263,6 +306,7 @@ type MovingAverageOverlay = {
|
|
|
263
306
|
* pane, resetting each session.
|
|
264
307
|
*/
|
|
265
308
|
type VWAPConfig = {
|
|
309
|
+
/** Draw the line. Default false. */
|
|
266
310
|
enabled?: boolean;
|
|
267
311
|
/** Session reset offset from UTC midnight, in minutes (default 0). */
|
|
268
312
|
resetMinutes?: number;
|
|
@@ -278,18 +322,20 @@ type VWAPConfig = {
|
|
|
278
322
|
* fill between the bands. No pane is reserved.
|
|
279
323
|
*/
|
|
280
324
|
type BollingerBandsConfig = {
|
|
325
|
+
/** Draw the bands. Default false. */
|
|
281
326
|
enabled?: boolean;
|
|
282
327
|
/** Lookback in candles. Default 20, clamped to >= 1. */
|
|
283
328
|
period?: number;
|
|
284
329
|
/** Standard-deviation multiplier. Default 2. */
|
|
285
330
|
stdDev?: number;
|
|
286
|
-
/** Price source. Default 'close'. */
|
|
331
|
+
/** Price source ({@link MASource}). Default 'close'. */
|
|
287
332
|
source?: MASource;
|
|
288
333
|
/**
|
|
289
|
-
*
|
|
290
|
-
* window's arithmetic mean, even with an EMA basis
|
|
334
|
+
* Averaging for the basis (middle) line ({@link MAKind}). Default 'sma'. The
|
|
335
|
+
* stdev always uses the window's arithmetic mean, even with an EMA basis
|
|
336
|
+
* (the standard semantics).
|
|
291
337
|
*/
|
|
292
|
-
|
|
338
|
+
maType?: MAKind;
|
|
293
339
|
/** Upper band color (hex string or packed ARGB number). Default blue. */
|
|
294
340
|
upperColor?: string | number;
|
|
295
341
|
/** Upper band stroke width in px. Default 1. */
|
|
@@ -302,11 +348,40 @@ type BollingerBandsConfig = {
|
|
|
302
348
|
lowerColor?: string | number;
|
|
303
349
|
/** Lower band stroke width in px. Default 1. */
|
|
304
350
|
lowerWidth?: number;
|
|
305
|
-
/**
|
|
306
|
-
|
|
351
|
+
/** Draw the translucent fill between the bands. Default true. */
|
|
352
|
+
fillVisible?: boolean;
|
|
307
353
|
/** Fill opacity 0..1, applied to the upper band color. Default 0.1. */
|
|
308
354
|
fillOpacity?: number;
|
|
309
355
|
};
|
|
356
|
+
/**
|
|
357
|
+
* Volume bar config. One bottom-anchored bar per candle on the price pane,
|
|
358
|
+
* drawn under the candles and sharing their x position and body width.
|
|
359
|
+
*
|
|
360
|
+
* Unlike the other indicator configs the bars are on by default, so omitting
|
|
361
|
+
* this prop leaves the chart looking as it always has.
|
|
362
|
+
*/
|
|
363
|
+
type VolumeConfig = {
|
|
364
|
+
/** Draw the bars. Default true. */
|
|
365
|
+
enabled?: boolean;
|
|
366
|
+
/** Bar opacity 0..1 (1 = opaque). Default 0.5, so bars read quieter than the candles. */
|
|
367
|
+
opacity?: number;
|
|
368
|
+
/**
|
|
369
|
+
* Height of the tallest bar as a fraction of the price pane, 0..1.
|
|
370
|
+
* Default 0.2.
|
|
371
|
+
*
|
|
372
|
+
* This is a ceiling rather than a reserved strip: raising it lets the bars
|
|
373
|
+
* reach further up over the candles rather than compressing them, matching
|
|
374
|
+
* the conventional volume overlay. Heights always auto-fit the loudest volume
|
|
375
|
+
* in view, so the tallest bar sits exactly at the ceiling.
|
|
376
|
+
*/
|
|
377
|
+
height?: number;
|
|
378
|
+
/** Corner radius (px) of the *top* of each bar. Defaults to `theme.volumeRadius`, else 0 (square). */
|
|
379
|
+
radius?: number;
|
|
380
|
+
/** Up-bar color (hex string or packed ARGB number). Defaults to `theme.accentBull`. */
|
|
381
|
+
upColor?: string | number;
|
|
382
|
+
/** Down-bar color (hex string or packed ARGB number). Defaults to `theme.accentBear`. */
|
|
383
|
+
downColor?: string | number;
|
|
384
|
+
};
|
|
310
385
|
/**
|
|
311
386
|
* A single resting-liquidity band: a price interval carrying a total order size
|
|
312
387
|
* on one side of the book. Consolidate raw L2 levels into these buckets before
|
|
@@ -360,9 +435,9 @@ type LiquidityConfig = {
|
|
|
360
435
|
* plus an optional solid-filled `quantity` pill and an optional close button),
|
|
361
436
|
* with a price badge in the y-axis strip.
|
|
362
437
|
*
|
|
363
|
-
* Interaction is opt-in and callback-gated
|
|
364
|
-
*
|
|
365
|
-
*
|
|
438
|
+
* Interaction is opt-in and callback-gated: the line is only draggable when
|
|
439
|
+
* `draggable` is set, and the close button only renders when you pass
|
|
440
|
+
* `onPriceLineClose`. Dragging is a *preview* — the chart never mutates
|
|
366
441
|
* the price you gave it, so a move your backend rejects reverts on its own
|
|
367
442
|
* simply by leaving your `priceLines` state unchanged.
|
|
368
443
|
*/
|
|
@@ -424,15 +499,65 @@ type PriceLinesStyle = {
|
|
|
424
499
|
*/
|
|
425
500
|
hoverBoost?: number;
|
|
426
501
|
};
|
|
427
|
-
/**
|
|
502
|
+
/**
|
|
503
|
+
* MACD indicator config. Rendered in its own pane below the candles: the gap
|
|
504
|
+
* between a fast and a slow moving average, a signal line smoothing that gap,
|
|
505
|
+
* and a histogram of the distance between the two.
|
|
506
|
+
*
|
|
507
|
+
* Every style field is optional and falls back to the stock look, so an
|
|
508
|
+
* untouched config renders exactly as it always has.
|
|
509
|
+
*/
|
|
428
510
|
type MACDConfig = {
|
|
511
|
+
/** Draw the pane. Default false. */
|
|
429
512
|
enabled?: boolean;
|
|
430
|
-
/** Fast
|
|
513
|
+
/** Fast moving-average length. Default 12. */
|
|
431
514
|
fast?: number;
|
|
432
|
-
/** Slow
|
|
515
|
+
/** Slow moving-average length (forced > fast). Default 26. */
|
|
433
516
|
slow?: number;
|
|
434
|
-
/** Signal-line
|
|
517
|
+
/** Signal-line length. Default 9. */
|
|
435
518
|
signal?: number;
|
|
519
|
+
/** Price source for the fast/slow legs ({@link MASource}). Default 'close'. */
|
|
520
|
+
source?: MASource;
|
|
521
|
+
/** Averaging used for the fast and slow legs ({@link MAKind}). Default 'ema'. */
|
|
522
|
+
maType?: MAKind;
|
|
523
|
+
/** Averaging applied to the MACD series for the signal line. Default 'ema'. */
|
|
524
|
+
signalMaType?: MAKind;
|
|
525
|
+
/** MACD line color (hex string or packed ARGB number). Default blue. */
|
|
526
|
+
lineColor?: string | number;
|
|
527
|
+
/** MACD line stroke width in px. Default 1.5. */
|
|
528
|
+
lineWidth?: number;
|
|
529
|
+
/** Draw the MACD line. Default true. */
|
|
530
|
+
lineVisible?: boolean;
|
|
531
|
+
/** Signal line color. Default orange. */
|
|
532
|
+
signalColor?: string | number;
|
|
533
|
+
/** Signal line stroke width in px. Default 1.5. */
|
|
534
|
+
signalWidth?: number;
|
|
535
|
+
/** Draw the signal line. Default true. */
|
|
536
|
+
signalVisible?: boolean;
|
|
537
|
+
/** Draw the histogram bars. Default true. */
|
|
538
|
+
histogramVisible?: boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Bars above zero that are still growing away from it. Defaults to
|
|
541
|
+
* `theme.accentBull`. Set all four histogram colors alike for a flat,
|
|
542
|
+
* single-color histogram.
|
|
543
|
+
*/
|
|
544
|
+
histogramUpColor?: string | number;
|
|
545
|
+
/**
|
|
546
|
+
* Bars above zero that are falling back toward it, i.e. momentum easing.
|
|
547
|
+
* Defaults to `histogramUpColor` at half opacity.
|
|
548
|
+
*/
|
|
549
|
+
histogramUpFadingColor?: string | number;
|
|
550
|
+
/** Bars below zero still growing away from it. Defaults to `theme.accentBear`. */
|
|
551
|
+
histogramDownColor?: string | number;
|
|
552
|
+
/**
|
|
553
|
+
* Bars below zero rising back toward it. Defaults to `histogramDownColor` at
|
|
554
|
+
* half opacity.
|
|
555
|
+
*/
|
|
556
|
+
histogramDownFadingColor?: string | number;
|
|
557
|
+
/** Zero-reference line color. Default gray. */
|
|
558
|
+
zeroLineColor?: string | number;
|
|
559
|
+
/** Draw the zero-reference line. Default true. */
|
|
560
|
+
zeroLineVisible?: boolean;
|
|
436
561
|
};
|
|
437
562
|
/**
|
|
438
563
|
* Platform-agnostic props shared by every vroom chart component. Each platform
|
|
@@ -474,11 +599,17 @@ type VroomChartCoreProps = {
|
|
|
474
599
|
*/
|
|
475
600
|
chartType?: ChartType;
|
|
476
601
|
/**
|
|
477
|
-
* Duration (ms) of the animated candle↔line
|
|
478
|
-
*
|
|
479
|
-
*
|
|
602
|
+
* Duration (ms) of the animated transitions: the candle↔line switch when
|
|
603
|
+
* `chartType` changes, and the candle reshape when the `candles` array is
|
|
604
|
+
* swapped for a different interval of the same asset. Default ~300. `0` snaps
|
|
605
|
+
* instantly. Ignored (snaps) when the OS requests reduced motion, which
|
|
606
|
+
* instead uses a plain cross-fade.
|
|
480
607
|
*/
|
|
481
608
|
transitionMs?: number;
|
|
609
|
+
/**
|
|
610
|
+
* Easing curve applied to those transitions. Default `'ease-in-out'`.
|
|
611
|
+
*/
|
|
612
|
+
transitionEasing?: TransitionEasing;
|
|
482
613
|
theme?: VroomTheme;
|
|
483
614
|
/** RSI indicator (pane below the candles). Omit/disable to hide it. */
|
|
484
615
|
rsi?: RSIConfig;
|
|
@@ -490,6 +621,8 @@ type VroomChartCoreProps = {
|
|
|
490
621
|
vwap?: VWAPConfig;
|
|
491
622
|
/** Bollinger Bands overlay (three lines + fill on the price pane). */
|
|
492
623
|
bollingerBands?: BollingerBandsConfig;
|
|
624
|
+
/** Volume bars under the candles. On by default; disable or restyle them here. */
|
|
625
|
+
volume?: VolumeConfig;
|
|
493
626
|
/** Resting-order / order-book liquidity bands drawn behind the candles. */
|
|
494
627
|
liquidity?: LiquidityConfig;
|
|
495
628
|
/**
|
|
@@ -650,4 +783,4 @@ declare global {
|
|
|
650
783
|
*/
|
|
651
784
|
declare function VroomChart(props: VroomChartProps): React.JSX.Element;
|
|
652
785
|
|
|
653
|
-
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type MACDConfig, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type VWAPConfig, type VisibleRange, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme };
|
|
786
|
+
export { type BollingerBandsConfig, type Candle, type ChartType, type CrosshairEvent, type MACDConfig, type MAKind, type MASource, type MovingAverageOverlay, type PriceLine, type PriceLinesStyle, type RSIConfig, type TransitionEasing, type VWAPConfig, type VisibleRange, type VolumeConfig, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme };
|
package/lib/index.js
CHANGED
|
@@ -104,8 +104,10 @@ var FLOAT_KEYS = {
|
|
|
104
104
|
// VROOM_FLOAT_CANDLE_RADIUS_PX
|
|
105
105
|
volumeRadius: 10,
|
|
106
106
|
// VROOM_FLOAT_VOLUME_RADIUS_PX
|
|
107
|
-
lineWidth: 11
|
|
107
|
+
lineWidth: 11,
|
|
108
108
|
// VROOM_FLOAT_LINE_WIDTH_PX
|
|
109
|
+
lineGradientOpacity: 12
|
|
110
|
+
// VROOM_FLOAT_LINE_GRADIENT_OPACITY
|
|
109
111
|
};
|
|
110
112
|
var BOOL_KEYS = {
|
|
111
113
|
wickRoundCap: 9
|
|
@@ -151,16 +153,43 @@ var MA_SOURCES = [
|
|
|
151
153
|
"hlc3",
|
|
152
154
|
"ohlc4"
|
|
153
155
|
];
|
|
156
|
+
var inheritColor = (v) => (v != null ? parseColor(v) : null) ?? 0;
|
|
154
157
|
function overlayToNumeric(o) {
|
|
155
158
|
const srcIdx = o.source ? MA_SOURCES.indexOf(o.source) : 0;
|
|
156
159
|
return {
|
|
157
|
-
kind: o.
|
|
158
|
-
period: o.
|
|
160
|
+
kind: o.maType === "ema" ? 1 : 0,
|
|
161
|
+
period: o.period,
|
|
159
162
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
160
163
|
color: (o.color != null ? parseColor(o.color) : null) ?? 4280902399,
|
|
161
164
|
width: o.width ?? 1.5
|
|
162
165
|
};
|
|
163
166
|
}
|
|
167
|
+
function rsiToSpec(cfg) {
|
|
168
|
+
return {
|
|
169
|
+
enabled: cfg?.enabled ?? false,
|
|
170
|
+
period: cfg?.period ?? 14,
|
|
171
|
+
upperBand: cfg?.upperBand ?? 70,
|
|
172
|
+
lowerBand: cfg?.lowerBand ?? 30,
|
|
173
|
+
maPeriod: cfg?.maPeriod ?? 14,
|
|
174
|
+
maKind: cfg?.maType === "ema" ? 1 : 0,
|
|
175
|
+
maVisible: cfg?.maVisible ?? true,
|
|
176
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
177
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
178
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
179
|
+
maColor: inheritColor(cfg?.maColor),
|
|
180
|
+
maWidth: cfg?.maWidth ?? -1,
|
|
181
|
+
bandColor: inheritColor(cfg?.bandColor),
|
|
182
|
+
bandsVisible: cfg?.bandsVisible ?? true
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function vwapToSpec(cfg) {
|
|
186
|
+
return {
|
|
187
|
+
enabled: cfg?.enabled ?? false,
|
|
188
|
+
resetOffsetMin: cfg?.resetMinutes ?? 0,
|
|
189
|
+
color: inheritColor(cfg?.color),
|
|
190
|
+
width: cfg?.width ?? -1
|
|
191
|
+
};
|
|
192
|
+
}
|
|
164
193
|
var DEFAULT_BB_BAND_COLOR = 4280902399;
|
|
165
194
|
var DEFAULT_BB_BASIS_COLOR = 4294929664;
|
|
166
195
|
function bollingerToSpec(cfg) {
|
|
@@ -170,17 +199,52 @@ function bollingerToSpec(cfg) {
|
|
|
170
199
|
period: cfg?.period ?? 20,
|
|
171
200
|
mult: cfg?.stdDev ?? 2,
|
|
172
201
|
source: srcIdx < 0 ? 0 : srcIdx,
|
|
173
|
-
basisKind: cfg?.
|
|
202
|
+
basisKind: cfg?.maType === "ema" ? 1 : 0,
|
|
174
203
|
upperColor: (cfg?.upperColor != null ? parseColor(cfg.upperColor) : null) ?? DEFAULT_BB_BAND_COLOR,
|
|
175
204
|
upperWidth: cfg?.upperWidth ?? 1,
|
|
176
205
|
middleColor: (cfg?.middleColor != null ? parseColor(cfg.middleColor) : null) ?? DEFAULT_BB_BASIS_COLOR,
|
|
177
206
|
middleWidth: cfg?.middleWidth ?? 1,
|
|
178
207
|
lowerColor: (cfg?.lowerColor != null ? parseColor(cfg.lowerColor) : null) ?? DEFAULT_BB_BAND_COLOR,
|
|
179
208
|
lowerWidth: cfg?.lowerWidth ?? 1,
|
|
180
|
-
fillEnabled: cfg?.
|
|
209
|
+
fillEnabled: cfg?.fillVisible ?? true,
|
|
181
210
|
fillOpacity: cfg?.fillOpacity ?? 0.1
|
|
182
211
|
};
|
|
183
212
|
}
|
|
213
|
+
function macdToSpec(cfg) {
|
|
214
|
+
const srcIdx = cfg?.source ? MA_SOURCES.indexOf(cfg.source) : 0;
|
|
215
|
+
return {
|
|
216
|
+
enabled: cfg?.enabled ?? false,
|
|
217
|
+
fast: cfg?.fast ?? 12,
|
|
218
|
+
slow: cfg?.slow ?? 26,
|
|
219
|
+
signal: cfg?.signal ?? 9,
|
|
220
|
+
source: srcIdx < 0 ? 0 : srcIdx,
|
|
221
|
+
maKind: cfg?.maType === "sma" ? 0 : 1,
|
|
222
|
+
signalMaKind: cfg?.signalMaType === "sma" ? 0 : 1,
|
|
223
|
+
lineColor: inheritColor(cfg?.lineColor),
|
|
224
|
+
lineWidth: cfg?.lineWidth ?? -1,
|
|
225
|
+
lineVisible: cfg?.lineVisible ?? true,
|
|
226
|
+
signalColor: inheritColor(cfg?.signalColor),
|
|
227
|
+
signalWidth: cfg?.signalWidth ?? -1,
|
|
228
|
+
signalVisible: cfg?.signalVisible ?? true,
|
|
229
|
+
histVisible: cfg?.histogramVisible ?? true,
|
|
230
|
+
histUpColor: inheritColor(cfg?.histogramUpColor),
|
|
231
|
+
histUpFadingColor: inheritColor(cfg?.histogramUpFadingColor),
|
|
232
|
+
histDownColor: inheritColor(cfg?.histogramDownColor),
|
|
233
|
+
histDownFadingColor: inheritColor(cfg?.histogramDownFadingColor),
|
|
234
|
+
zeroColor: inheritColor(cfg?.zeroLineColor),
|
|
235
|
+
zeroVisible: cfg?.zeroLineVisible ?? true
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function volumeToSpec(cfg) {
|
|
239
|
+
return {
|
|
240
|
+
enabled: cfg?.enabled ?? true,
|
|
241
|
+
heightFrac: cfg?.height ?? -1,
|
|
242
|
+
opacity: cfg?.opacity ?? -1,
|
|
243
|
+
radiusPx: cfg?.radius ?? -1,
|
|
244
|
+
upColor: (cfg?.upColor != null ? parseColor(cfg.upColor) : null) ?? 0,
|
|
245
|
+
downColor: (cfg?.downColor != null ? parseColor(cfg.downColor) : null) ?? 0
|
|
246
|
+
};
|
|
247
|
+
}
|
|
184
248
|
var DEFAULT_PRICE_LINE_COLOR = 4293874512;
|
|
185
249
|
var DEFAULT_PRICE_LINE_BODY_BG = 3642499368;
|
|
186
250
|
var DEFAULT_PRICE_LINE_HOVER_BOOST = 1.25;
|
|
@@ -218,9 +282,10 @@ function ensureInstalled() {
|
|
|
218
282
|
}
|
|
219
283
|
installed = true;
|
|
220
284
|
}
|
|
221
|
-
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, priceLines) {
|
|
285
|
+
function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType, theme, rsi, macd, movingAverages, vwap, bollingerBands, volume, priceLines) {
|
|
222
286
|
const handleRef = (0, import_react.useRef)(null);
|
|
223
287
|
const defaultWidthAppliedRef = (0, import_react.useRef)(false);
|
|
288
|
+
const volumeCollapseRef = (0, import_react.useRef)(null);
|
|
224
289
|
const [picture, setPicture] = (0, import_react.useState)(null);
|
|
225
290
|
if (!handleRef.current && size.width > 0 && size.height > 0) {
|
|
226
291
|
ensureInstalled();
|
|
@@ -235,6 +300,7 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
235
300
|
const maKey = movingAverages ? JSON.stringify(movingAverages) : "";
|
|
236
301
|
const vwapKey = vwap ? JSON.stringify(vwap) : "";
|
|
237
302
|
const bollingerKey = bollingerBands ? JSON.stringify(bollingerBands) : "";
|
|
303
|
+
const volumeKey = volume ? JSON.stringify(volume) : "";
|
|
238
304
|
const priceLinesKey = priceLines ? JSON.stringify(priceLines) : "";
|
|
239
305
|
(0, import_react.useEffect)(() => {
|
|
240
306
|
const h = handleRef.current;
|
|
@@ -253,34 +319,44 @@ function useChartCore(candles, size, visibleRange, defaultCandleWidth, chartType
|
|
|
253
319
|
if (theme) {
|
|
254
320
|
applyTheme(h, theme);
|
|
255
321
|
}
|
|
256
|
-
h.setRSI(
|
|
257
|
-
|
|
258
|
-
rsi?.period ?? 14,
|
|
259
|
-
rsi?.upperBand ?? 70,
|
|
260
|
-
rsi?.lowerBand ?? 30,
|
|
261
|
-
rsi?.maEnabled ?? true,
|
|
262
|
-
rsi?.maPeriod ?? 14
|
|
263
|
-
);
|
|
264
|
-
h.setMACD(
|
|
265
|
-
macd?.enabled ?? false,
|
|
266
|
-
macd?.fast ?? 12,
|
|
267
|
-
macd?.slow ?? 26,
|
|
268
|
-
macd?.signal ?? 9
|
|
269
|
-
);
|
|
322
|
+
h.setRSI(rsiToSpec(rsi));
|
|
323
|
+
h.setMACD(macdToSpec(macd));
|
|
270
324
|
h.setOverlays((movingAverages ?? []).map(overlayToNumeric));
|
|
271
|
-
h.setVWAP(
|
|
272
|
-
vwap?.enabled ?? false,
|
|
273
|
-
vwap?.resetMinutes ?? 0,
|
|
274
|
-
(vwap?.color != null ? parseColor(vwap.color) : null) ?? 4278238420,
|
|
275
|
-
vwap?.width ?? 1.5
|
|
276
|
-
);
|
|
325
|
+
h.setVWAP(vwapToSpec(vwap));
|
|
277
326
|
h.setBollinger(bollingerToSpec(bollingerBands));
|
|
327
|
+
h.setVolume(volumeToSpec(volume));
|
|
328
|
+
const collapse = volumeCollapseRef.current;
|
|
329
|
+
if (collapse) h.setVolumeCollapse(collapse.t, collapse.easing);
|
|
278
330
|
h.setPriceLines(
|
|
279
331
|
priceLines?.lines.length ? priceLinesToSpec(priceLines) : EMPTY_PRICE_LINES
|
|
280
332
|
);
|
|
281
333
|
setPicture(h.render());
|
|
282
|
-
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, priceLinesKey]);
|
|
283
|
-
return { handle: handleRef.current, picture };
|
|
334
|
+
}, [candles, size.width, size.height, size.pxRatio, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, bollingerKey, volumeKey, priceLinesKey]);
|
|
335
|
+
return { handle: handleRef.current, picture, volumeCollapseRef };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/easing.ts
|
|
339
|
+
function ease(kind, p) {
|
|
340
|
+
switch (kind) {
|
|
341
|
+
case "linear":
|
|
342
|
+
return p;
|
|
343
|
+
case "ease-in":
|
|
344
|
+
return p * p;
|
|
345
|
+
case "ease-out":
|
|
346
|
+
return p * (2 - p);
|
|
347
|
+
default:
|
|
348
|
+
return p * p * (3 - 2 * p);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
var EASINGS = [
|
|
352
|
+
"linear",
|
|
353
|
+
"ease-in",
|
|
354
|
+
"ease-out",
|
|
355
|
+
"ease-in-out"
|
|
356
|
+
];
|
|
357
|
+
function easingIndex(kind) {
|
|
358
|
+
const i = kind ? EASINGS.indexOf(kind) : -1;
|
|
359
|
+
return i < 0 ? EASINGS.indexOf("ease-in-out") : i;
|
|
284
360
|
}
|
|
285
361
|
|
|
286
362
|
// src/VroomChart.tsx
|
|
@@ -294,12 +370,14 @@ function VroomChart(props) {
|
|
|
294
370
|
defaultCandleWidth,
|
|
295
371
|
chartType,
|
|
296
372
|
transitionMs,
|
|
373
|
+
transitionEasing,
|
|
297
374
|
theme,
|
|
298
375
|
rsi,
|
|
299
376
|
macd,
|
|
300
377
|
movingAverages,
|
|
301
378
|
vwap,
|
|
302
379
|
bollingerBands,
|
|
380
|
+
volume,
|
|
303
381
|
crosshairOffset = 40,
|
|
304
382
|
onCrosshair,
|
|
305
383
|
onViewportChange,
|
|
@@ -327,7 +405,7 @@ function VroomChart(props) {
|
|
|
327
405
|
} : void 0,
|
|
328
406
|
[priceLines, priceLinesStyle, onPriceLineClose]
|
|
329
407
|
);
|
|
330
|
-
const { handle, picture } = useChartCore(
|
|
408
|
+
const { handle, picture, volumeCollapseRef } = useChartCore(
|
|
331
409
|
candles,
|
|
332
410
|
{ width, height },
|
|
333
411
|
visibleRange,
|
|
@@ -339,6 +417,7 @@ function VroomChart(props) {
|
|
|
339
417
|
movingAverages,
|
|
340
418
|
vwap,
|
|
341
419
|
bollingerBands,
|
|
420
|
+
volume,
|
|
342
421
|
priceLinesProp
|
|
343
422
|
);
|
|
344
423
|
const emptyPicture = (0, import_react2.useMemo)(() => {
|
|
@@ -386,6 +465,8 @@ function VroomChart(props) {
|
|
|
386
465
|
const morphRaf = (0, import_react2.useRef)(null);
|
|
387
466
|
const morphFade = (0, import_react2.useRef)(null);
|
|
388
467
|
const morphHandle = (0, import_react2.useRef)(null);
|
|
468
|
+
const easingRef = (0, import_react2.useRef)(transitionEasing);
|
|
469
|
+
easingRef.current = transitionEasing;
|
|
389
470
|
(0, import_react2.useEffect)(() => {
|
|
390
471
|
if (!handle) return void 0;
|
|
391
472
|
const target = chartType === "line" ? 1 : 0;
|
|
@@ -415,8 +496,7 @@ function VroomChart(props) {
|
|
|
415
496
|
const step = (now) => {
|
|
416
497
|
if (startTs == null) startTs = now;
|
|
417
498
|
const prog = Math.min(1, (now - startTs) / dur);
|
|
418
|
-
const
|
|
419
|
-
const fade = from + (target - from) * e;
|
|
499
|
+
const fade = from + (target - from) * ease(easingRef.current, prog);
|
|
420
500
|
morphFade.current = fade;
|
|
421
501
|
handle.setMorph(fade, fade);
|
|
422
502
|
const p = handle.render();
|
|
@@ -439,6 +519,51 @@ function VroomChart(props) {
|
|
|
439
519
|
}
|
|
440
520
|
};
|
|
441
521
|
}, [handle, chartType, transitionMs, pictureSV]);
|
|
522
|
+
const volumeRaf = (0, import_react2.useRef)(null);
|
|
523
|
+
const volumeHandle = (0, import_react2.useRef)(null);
|
|
524
|
+
(0, import_react2.useEffect)(() => {
|
|
525
|
+
if (!handle) return void 0;
|
|
526
|
+
const target = volume?.enabled ?? true ? 0 : 1;
|
|
527
|
+
const easing = easingIndex(easingRef.current);
|
|
528
|
+
if (volumeHandle.current !== handle || volumeCollapseRef.current == null) {
|
|
529
|
+
volumeHandle.current = handle;
|
|
530
|
+
volumeCollapseRef.current = { t: target, easing };
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
if (volumeCollapseRef.current.t === target) return void 0;
|
|
534
|
+
if (volumeRaf.current != null) {
|
|
535
|
+
cancelAnimationFrame(volumeRaf.current);
|
|
536
|
+
volumeRaf.current = null;
|
|
537
|
+
}
|
|
538
|
+
const dur = Math.max(0, transitionMs ?? 300);
|
|
539
|
+
if (dur === 0) {
|
|
540
|
+
volumeCollapseRef.current = { t: target, easing };
|
|
541
|
+
handle.setVolumeCollapse(target, easing);
|
|
542
|
+
const p = handle.render();
|
|
543
|
+
if (p) pictureSV.value = p;
|
|
544
|
+
return void 0;
|
|
545
|
+
}
|
|
546
|
+
const from = volumeCollapseRef.current.t;
|
|
547
|
+
let startTs = null;
|
|
548
|
+
const step = (now) => {
|
|
549
|
+
if (startTs == null) startTs = now;
|
|
550
|
+
const prog = Math.min(1, (now - startTs) / dur);
|
|
551
|
+
const t = prog < 1 ? from + (target - from) * prog : target;
|
|
552
|
+
const kind = easingIndex(easingRef.current);
|
|
553
|
+
volumeCollapseRef.current = { t, easing: kind };
|
|
554
|
+
handle.setVolumeCollapse(t, kind);
|
|
555
|
+
const p = handle.render();
|
|
556
|
+
if (p) pictureSV.value = p;
|
|
557
|
+
volumeRaf.current = prog < 1 ? requestAnimationFrame(step) : null;
|
|
558
|
+
};
|
|
559
|
+
volumeRaf.current = requestAnimationFrame(step);
|
|
560
|
+
return () => {
|
|
561
|
+
if (volumeRaf.current != null) {
|
|
562
|
+
cancelAnimationFrame(volumeRaf.current);
|
|
563
|
+
volumeRaf.current = null;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
}, [handle, volume?.enabled, transitionMs, pictureSV, volumeCollapseRef]);
|
|
442
567
|
const hitAxis = (0, import_react2.useCallback)(
|
|
443
568
|
(x, y) => {
|
|
444
569
|
if (!handle) return "chart";
|