react-native-vroom-chart 0.15.0 → 0.17.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.
Files changed (53) hide show
  1. package/cpp/VroomChartHostObject.cpp +252 -1
  2. package/cpp/_core_include/vroom/vroom_chart.h +178 -4
  3. package/cpp/_core_src/atr.cpp +67 -0
  4. package/cpp/_core_src/atr.h +44 -0
  5. package/cpp/_core_src/atr_pane.cpp +162 -0
  6. package/cpp/_core_src/atr_pane.h +48 -0
  7. package/cpp/_core_src/candles.cpp +15 -14
  8. package/cpp/_core_src/chart.cpp +758 -200
  9. package/cpp/_core_src/chart.h +221 -3
  10. package/cpp/_core_src/chart_facade.cpp +236 -23
  11. package/cpp/_core_src/color_lerp.h +28 -0
  12. package/cpp/_core_src/fair_value_gaps.cpp +76 -0
  13. package/cpp/_core_src/fair_value_gaps.h +54 -0
  14. package/cpp/_core_src/fvg_overlay.cpp +262 -0
  15. package/cpp/_core_src/fvg_overlay.h +43 -0
  16. package/cpp/_core_src/ichimoku.cpp +65 -0
  17. package/cpp/_core_src/ichimoku.h +45 -0
  18. package/cpp/_core_src/labels.cpp +3 -0
  19. package/cpp/_core_src/line_morph.h +159 -0
  20. package/cpp/_core_src/loading_line.cpp +183 -0
  21. package/cpp/_core_src/loading_line.h +38 -0
  22. package/cpp/_core_src/loading_wave.h +140 -0
  23. package/cpp/_core_src/ma_overlay.cpp +273 -45
  24. package/cpp/_core_src/ma_overlay.h +64 -2
  25. package/cpp/_core_src/macd.cpp +24 -1
  26. package/cpp/_core_src/macd.h +16 -0
  27. package/cpp/_core_src/macd_pane.cpp +71 -62
  28. package/cpp/_core_src/macd_pane.h +13 -1
  29. package/cpp/_core_src/pane_series.h +169 -0
  30. package/cpp/_core_src/price_indicator.cpp +21 -7
  31. package/cpp/_core_src/price_indicator.h +11 -1
  32. package/cpp/_core_src/price_indicator_anim.h +81 -0
  33. package/cpp/_core_src/rsi.cpp +4 -0
  34. package/cpp/_core_src/rsi.h +7 -0
  35. package/cpp/_core_src/rsi_pane.cpp +85 -29
  36. package/cpp/_core_src/rsi_pane.h +11 -1
  37. package/cpp/_core_src/theme.cpp +5 -0
  38. package/cpp/_core_src/tip_geometry.h +55 -0
  39. package/cpp/_core_src/viewport.h +68 -0
  40. package/lib/index.d.mts +281 -3
  41. package/lib/index.d.ts +281 -3
  42. package/lib/index.js +292 -14
  43. package/lib/index.js.map +1 -1
  44. package/lib/index.mjs +292 -14
  45. package/lib/index.mjs.map +1 -1
  46. package/package.json +1 -1
  47. package/src/VroomChart.tsx +33 -3
  48. package/src/dataTransitions.ts +47 -0
  49. package/src/index.ts +5 -0
  50. package/src/jsi.d.ts +139 -1
  51. package/src/theme.ts +1 -0
  52. package/src/types.ts +5 -0
  53. package/src/useChartCore.ts +402 -8
@@ -9,6 +9,7 @@
9
9
  #pragma once
10
10
 
11
11
  #include <chrono>
12
+ #include <cstddef>
12
13
  #include <string>
13
14
  #include <vector>
14
15
 
@@ -22,14 +23,33 @@
22
23
  #include "include/core/SkRefCnt.h"
23
24
  #pragma clang diagnostic pop
24
25
 
26
+ #include "fair_value_gaps.h"
25
27
  #include "footprints_layout.h"
26
28
  #include "labels.h"
29
+ #include "line_morph.h"
27
30
  #include "price_format.h"
28
31
  #include "theme.h"
29
32
  #include "viewport.h"
30
33
 
31
34
  class SkCanvas;
32
35
 
36
+ namespace vroom {
37
+
38
+ // Which indicator owns a below-chart pane. Panes are otherwise
39
+ // interchangeable: they are all the same height and stack in enable order.
40
+ enum class PaneKind { Rsi, Macd, Atr };
41
+
42
+ // Count of PaneKind values — the most panes that can be stacked at once.
43
+ constexpr int kMaxPanes = 3;
44
+
45
+ // One enabled pane and the sequence number it claimed when it was turned on.
46
+ struct IndicatorPane {
47
+ int order;
48
+ PaneKind kind;
49
+ };
50
+
51
+ } // namespace vroom
52
+
33
53
  struct VroomChart {
34
54
  // --- bridge / lifecycle -------------------------------------------------
35
55
  VroomCallbacks cb{};
@@ -72,6 +92,42 @@ struct VroomChart {
72
92
  // is_animating_now keeps the host's redraw loop alive (see tip_pulse.h).
73
93
  float tip_pulse_elapsed_s = 0.f;
74
94
 
95
+ // --- loading line -------------------------------------------------------
96
+ // See loading_line.h for the three stages this state drives.
97
+ //
98
+ // Authoritative, not inferred: the host tells us it's loading *and* that it
99
+ // has no data, because an empty `candles` alone can't be trusted here. The
100
+ // hosts' data effects skip pushing an empty array (it would read as "hold
101
+ // the last frame"), so a chart mid-asset-switch can be loading while still
102
+ // holding the previous asset's bars — inferring from emptiness would show
103
+ // that stale series as if it were the new one's.
104
+ //
105
+ // True for stages 1 and 2 both: the line stands in for the chart until the
106
+ // candles actually take over in stage 3.
107
+ bool loading = false;
108
+ // Whether the wave travels. Cleared for reduced motion, which both freezes
109
+ // the line at phase 0 and keeps is_animating_now from pinning a host loop.
110
+ bool loading_animate = true;
111
+ // Phase of the wave, in seconds, advanced by begin_frame like the tip pulse
112
+ // above and wrapped for the same reason.
113
+ float loading_elapsed_s = 0.f;
114
+ // Fades the line in on arrival, so a chart that resolves instantly from
115
+ // cache doesn't flash a placeholder.
116
+ float loading_fade_in = 0.f;
117
+
118
+ // The line's morph vertices, left to right, captured when the data lands.
119
+ // Empty means stage 1 — nothing to morph toward yet — which is what
120
+ // loading_line::draw switches on.
121
+ std::vector<vroom::LinePoint> loading_line;
122
+ // Stage 2's progress, 0 = the frozen sine, 1 = through the candle centres.
123
+ // Parked at 1 when no morph is running.
124
+ float loading_line_t = 1.f;
125
+ // Set for stage 3, where the line rides `interval_morph_t` out on the same
126
+ // clock as the candles growing out of it. A flag rather than its own float
127
+ // because the two have to finish together to avoid a line left hanging over
128
+ // a settled chart.
129
+ bool loading_line_revealing = false;
130
+
75
131
  // Interval morph: the outgoing candle geometry captured when a timeframe
76
132
  // switch begins, indexed from the right of the visible slice (slot 0 =
77
133
  // newest). `interval_morph_fade` is the host's choice at capture time:
@@ -82,6 +138,19 @@ struct VroomChart {
82
138
  std::vector<vroom::CandleSnapshot> morph_from;
83
139
  float interval_morph_t = 1.f; // 1 = not morphing
84
140
  bool interval_morph_fade = false;
141
+ // Set when the capture came from a live update rather than a timeframe
142
+ // switch. Same slot machinery, but the axis holds still: a stream morph
143
+ // only reshapes the last bar, and the ticks it sits between don't move, so
144
+ // running them through the interval envelope would flash the whole axis on
145
+ // every tick (see labels::interval_phase).
146
+ bool morph_is_stream = false;
147
+ // The outgoing shape of every indicator series that was on screen, captured
148
+ // alongside the candles and keyed by identity so a toggle landing in the
149
+ // same commit as the switch can't pair two different lines (see
150
+ // line_morph.h). A timeframe switch invalidates every indicator cache at
151
+ // once, so without this the lines would pop to their new values while the
152
+ // candles under them reshape.
153
+ std::vector<vroom::LineMorph> morph_lines;
85
154
  // The pre-switch price scale and time window. The candle capture above is
86
155
  // normalized against these, and the axes keep rendering their old ticks from
87
156
  // them while fading out (see labels::interval_phase), so a label is never
@@ -123,7 +192,8 @@ struct VroomChart {
123
192
  VroomRSI rsi{0, 14, 70.0, 30.0, 14, 0, 1,
124
193
  0u, -1.f, 1, // RSI line
125
194
  0u, -1.f, // trendline
126
- 0u, 1}; // band rules
195
+ 0u, 1, // band rules
196
+ 1}; // extreme shading
127
197
  std::vector<double> rsi_cache; // RSI per candle (NaN where undefined)
128
198
  std::vector<double> rsi_ma_cache; // MA of rsi_cache (empty if MA off)
129
199
  bool rsi_dirty = true;
@@ -147,11 +217,23 @@ struct VroomChart {
147
217
  // default auto-fit amplitude; >1 zooms in, <1 zooms out. Anchored at zero.
148
218
  double macd_y_scale = 1.0;
149
219
 
220
+ // ATR, drawn in its own pane. atr_cache is aligned to `candles` (NaN
221
+ // warmup), recomputed lazily by ensure_atr() when atr_dirty.
222
+ // Defaults: 14-period true range under Wilder smoothing, style fields on
223
+ // their inherit sentinel.
224
+ VroomATR atr{0, 14, 0, 0u, -1.f};
225
+ std::vector<double> atr_cache;
226
+ bool atr_dirty = true;
227
+ // User y-axis zoom for the ATR pane (drag on its y-axis strip). 1.0 = the
228
+ // default 0..peak fit; >1 zooms in, <1 zooms out. Anchored at the baseline.
229
+ double atr_y_scale = 1.0;
230
+
150
231
  // Stacking order for the indicator panes: each indicator gets the next
151
232
  // sequence number on its off->on transition, so the most recently enabled
152
233
  // pane sorts last (bottom). -1 = not currently enabled.
153
234
  int rsi_order = -1;
154
235
  int macd_order = -1;
236
+ int atr_order = -1;
155
237
  int pane_seq = 0;
156
238
 
157
239
  // Moving-average overlay lines (SMA/EMA) drawn on the price pane. Not panes
@@ -183,6 +265,46 @@ struct VroomChart {
183
265
  std::vector<double> bb_lower_cache;
184
266
  bool bollinger_dirty = true;
185
267
 
268
+ // Ichimoku overlay (price pane; no pane reserved). Caches aligned to
269
+ // `candles` (NaN warmup) and indexed by the bar each value was computed
270
+ // from — displacement is applied when drawing, so the caches survive a
271
+ // change to it. Recomputed lazily by ensure_ichimoku() when ichimoku_dirty.
272
+ // Defaults: the standard 9/26/52/26, blue tenkan / red kijun, green span A
273
+ // over orange span B, teal chikou, 15% cloud.
274
+ VroomIchimoku ichimoku{0, 9, 26, 52, 26,
275
+ 0xff2962ff, 1.f, 1, // tenkan: blue
276
+ 0xffef5350, 1.f, 1, // kijun: red
277
+ 0xff26a69a, 1.f, 1, // senkou A: green
278
+ 0xffff6d00, 1.f, 1, // senkou B: orange
279
+ 0xff00bcd4, 1.f, 1, // chikou: teal
280
+ 1, 0xff26a69a, 0xffef5350, 0.15f};
281
+ std::vector<double> ich_tenkan_cache;
282
+ std::vector<double> ich_kijun_cache;
283
+ std::vector<double> ich_senkou_a_cache;
284
+ std::vector<double> ich_senkou_b_cache;
285
+ std::vector<double> ich_chikou_cache;
286
+ bool ichimoku_dirty = true;
287
+
288
+ // Fair Value Gap overlay (price pane; no pane reserved). The cache holds the
289
+ // detected imbalances in absolute time/price, oldest first, so box geometry
290
+ // and style are free to change without re-scanning — only the detection
291
+ // inputs below dirty it. Recomputed lazily by ensure_fvg() when fvg_dirty.
292
+ //
293
+ // Defaults: 300 bars back, close-settled fills, deleted once filled, 20-slot
294
+ // boxes in green/red at 15%, solid 1px border, labels on.
295
+ VroomFairValueGaps fvg{0, 300, 0, 0, 1, 0, 20,
296
+ 0xff26a69a, 0xffef5350, 0.15f,
297
+ 1, 0, 1.f, 0xff26a69a, 0xffef5350,
298
+ 1, nullptr, 10, 0u, 0.f,
299
+ 0, 0xff26a69a, 0xffef5350, nullptr};
300
+ // Owns the label texts, so the caller may free theirs as soon as the setter
301
+ // returns. `fvg.label` and `fvg.inverse_label` are left null and these are
302
+ // what the renderer reads.
303
+ std::string fvg_label = "FVG";
304
+ std::string fvg_inverse_label = "iFVG";
305
+ std::vector<vroom::fvg::Gap> fvg_cache;
306
+ bool fvg_dirty = true;
307
+
186
308
  // Volume bars (price pane, under the candles). On by default with every
187
309
  // style field left on its inherit sentinel, so an untouched chart looks
188
310
  // exactly as it did before the config existed. No cache — bar heights come
@@ -348,6 +470,69 @@ struct VroomChart {
348
470
  // indicator is enabled.
349
471
  void ensure_bollinger();
350
472
 
473
+ // Recomputes the Ichimoku caches when ichimoku_dirty and the indicator is
474
+ // enabled.
475
+ void ensure_ichimoku();
476
+
477
+ // Redetects the Fair Value Gaps when fvg_dirty and the indicator is enabled.
478
+ void ensure_fvg();
479
+
480
+ // Recomputes the ATR cache when atr_dirty and the indicator is enabled.
481
+ void ensure_atr();
482
+
483
+ // Fills morph_lines with the outgoing shape of every enabled indicator,
484
+ // normalized the way morph_from is. Called by begin_interval_morph while
485
+ // the pre-switch candles and caches are still resident — after the swap
486
+ // there is nothing left to capture. `range` is the visible slice and
487
+ // `bounds` the price band the capture is taken against, both shared with
488
+ // the candle capture so the two land on the same pixels.
489
+ void capture_morph_lines(const vroom::Layout& lay,
490
+ const vroom::PriceBounds& bounds,
491
+ vroom::IndexRange range, int64_t window_ms);
492
+
493
+ // Source indices whose *displaced* plot time lands in the visible window,
494
+ // for an Ichimoku span drawn at `time_ms + shift_ms`. A bar of padding
495
+ // either side keeps a run reaching the pane edge instead of stopping at the
496
+ // last bar strictly inside it. Shared by the cloud, the lines and the
497
+ // interval-morph capture so all three slice the same bars.
498
+ vroom::IndexRange ichimoku_source_range(int64_t shift_ms) const;
499
+
500
+ // The enabled panes written to `out` top to bottom, returning how many.
501
+ // Both the draw pass and the y-axis hit test read this, so the stack they
502
+ // see can't drift apart.
503
+ int indicator_panes(vroom::IndicatorPane out[vroom::kMaxPanes]) const;
504
+
505
+ // Draws the pane stack into the band below the candles, starting at
506
+ // `pane_top`. Shared by the settled draw and the interval-morph fade, which
507
+ // paint the panes at different points in the z-order.
508
+ void draw_indicator_panes(SkCanvas* canvas, const vroom::Layout& lay,
509
+ const VroomCandle* visible, std::size_t n,
510
+ std::size_t first, int64_t window_ms,
511
+ float candle_right, float pane_top,
512
+ bool use_capture, float morph_t);
513
+
514
+ // The price-pane indicator overlays, split where the candles sit in the
515
+ // z-order: the Bollinger band and Ichimoku cloud go behind them, the moving
516
+ // averages, VWAP, Bollinger and Ichimoku lines over them.
517
+ //
518
+ // `use_capture` and `morph_t` carry the interval morph, on the same terms
519
+ // the candles take it. A transform pairs each series with its capture (see
520
+ // morph_lines) and eases toward the new shape. A fade's outgoing half
521
+ // passes n = 0 with morph_t = 0, replaying the capture alone. A settled
522
+ // frame passes no capture at all.
523
+ void draw_overlay_fills(SkCanvas* canvas, const vroom::Layout& lay,
524
+ const vroom::PriceBounds& bounds,
525
+ const VroomCandle* visible, std::size_t n,
526
+ std::size_t first, int64_t window_ms,
527
+ float candle_right, float candle_area_h,
528
+ bool use_capture, float morph_t);
529
+ void draw_overlay_lines(SkCanvas* canvas, const vroom::Layout& lay,
530
+ const vroom::PriceBounds& bounds,
531
+ const VroomCandle* visible, std::size_t n,
532
+ std::size_t first, int64_t window_ms,
533
+ float candle_right, float candle_area_h,
534
+ bool use_capture, float morph_t);
535
+
351
536
  // The main drawing pass. Calls into the labels and candles modules, and owns
352
537
  // the per-frame animation clock (see begin_frame) so every host gets it —
353
538
  // the web build draws straight through vroom_chart_draw rather than the
@@ -359,6 +544,38 @@ struct VroomChart {
359
544
  // draw_chart.
360
545
  void begin_frame();
361
546
 
547
+ // Enters or leaves the loading line. `animate` false pins it still for
548
+ // reduced motion. Entering restarts the phase and the fade-in; leaving
549
+ // outright (rather than through the two calls below) drops the line with no
550
+ // hand-off, which is what an error or a cancelled fetch wants.
551
+ void set_loading(bool on, bool animate);
552
+
553
+ // Stage 2. Call *after* pushing the real candles: it needs them to know
554
+ // where the line is heading. Freezes the sine at its current phase and
555
+ // pairs each vertex with the vertical centre of the candle that will occupy
556
+ // that column, then hands `loading_line_t` to the host to drive 0 → 1.
557
+ //
558
+ // Freezing rather than letting the wave run underneath is deliberate — a
559
+ // moving source makes the morph read as two animations fighting instead of
560
+ // one shape resolving.
561
+ void begin_loading_morph();
562
+
563
+ // The visible slice and price bounds the hand-off aims at, as draw_chart
564
+ // would resolve them. `n == 0` when there is nothing to hand off to.
565
+ struct LoadingTarget {
566
+ const ::VroomCandle* visible = nullptr;
567
+ std::size_t n = 0;
568
+ vroom::PriceBounds bounds{};
569
+ };
570
+ LoadingTarget loading_target() const;
571
+
572
+ // Stage 3. Captures every candle collapsed onto its own vertical centre at
573
+ // zero alpha and hands it to the interval-morph machinery, so the existing
574
+ // slot lerp grows each bar outward from the line while candles::draw blends
575
+ // its colour up out of nothing. Also releases `loading`, letting the axes,
576
+ // price badge and panes come back.
577
+ void begin_loading_reveal();
578
+
362
579
  // True when the line tip's pulse ring is on screen and looping. Gated on
363
580
  // line mode and on having data, so a chart that isn't showing the ring can
364
581
  // still go idle.
@@ -368,7 +585,8 @@ struct VroomChart {
368
585
  // SkPictureRecorder.
369
586
  void rebuild_chart_picture();
370
587
 
371
- // True if any axis label is mid-fade, or the tip pulse is running. Used by
372
- // the JS-side animation loop to know when to keep ticking.
588
+ // True if any axis label is mid-fade, the tip pulse is running, or the
589
+ // loading skeleton is waving. Used by the JS-side animation loop to know
590
+ // when to keep ticking.
373
591
  bool is_animating_now() const;
374
592
  };