react-native-vroom-chart 0.5.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/android/CMakeLists.txt +143 -0
- package/android/README.md +60 -5
- package/android/build.gradle +115 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/VroomChartJni.cpp +24 -0
- package/android/src/main/java/com/vroom/chart/VroomChartModule.kt +31 -0
- package/android/src/main/java/com/vroom/chart/VroomChartPackage.kt +31 -0
- package/cpp/VroomChartHostObject.cpp +368 -33
- package/cpp/VroomJsiInstaller.cpp +11 -1
- package/cpp/VroomSkiaContext.cpp +15 -7
- package/cpp/_core_include/vroom/vroom_chart.h +250 -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 +98 -40
- package/cpp/_core_src/chart.h +82 -20
- package/cpp/_core_src/chart_facade.cpp +297 -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.cpp +87 -0
- package/cpp/_core_src/price_line_layout.h +80 -0
- package/cpp/_core_src/price_lines.cpp +428 -0
- package/cpp/_core_src/price_lines.h +56 -0
- 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 +261 -25
- package/lib/index.d.ts +261 -25
- package/lib/index.js +252 -36
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +252 -36
- package/lib/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/VroomChart.tsx +162 -11
- package/src/easing.ts +40 -0
- package/src/index.ts +5 -0
- package/src/jsi.d.ts +124 -20
- package/src/theme.ts +1 -0
- package/src/types.ts +5 -0
- package/src/useChartCore.ts +166 -28
package/cpp/_core_src/chart.cpp
CHANGED
|
@@ -30,13 +30,19 @@
|
|
|
30
30
|
#include "macd.h"
|
|
31
31
|
#include "macd_pane.h"
|
|
32
32
|
#include "price_indicator.h"
|
|
33
|
+
#include "price_lines.h"
|
|
33
34
|
#include "rsi.h"
|
|
34
35
|
#include "rsi_pane.h"
|
|
36
|
+
#include "style_inherit.h"
|
|
35
37
|
#include "volume.h"
|
|
36
38
|
#include "vwap.h"
|
|
37
39
|
#include "theme.h"
|
|
38
40
|
#include "viewport.h"
|
|
39
41
|
|
|
42
|
+
namespace {
|
|
43
|
+
constexpr SkColor kVwapLine = 0xff00bcd4; // cyan
|
|
44
|
+
} // namespace
|
|
45
|
+
|
|
40
46
|
VroomChart::VroomChart() : theme(vroom::default_theme()) {}
|
|
41
47
|
|
|
42
48
|
void VroomChart::mark_dirty() {
|
|
@@ -48,7 +54,7 @@ vroom::Layout VroomChart::layout() const {
|
|
|
48
54
|
const float axis_w = axis_width_px > 0.f
|
|
49
55
|
? axis_width_px
|
|
50
56
|
: width_px * theme.floats[VROOM_FLOAT_Y_AXIS_WIDTH_RATIO];
|
|
51
|
-
const int pane_count = (
|
|
57
|
+
const int pane_count = (rsi.enabled ? 1 : 0) + (macd.enabled ? 1 : 0);
|
|
52
58
|
const float indicator_h = static_cast<float>(pane_count) * height_px *
|
|
53
59
|
theme.floats[VROOM_FLOAT_INDICATOR_HEIGHT_FRAC];
|
|
54
60
|
return vroom::Layout{
|
|
@@ -65,10 +71,11 @@ vroom::Layout VroomChart::layout() const {
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
void VroomChart::ensure_rsi() {
|
|
68
|
-
if (!
|
|
69
|
-
vroom::rsi::compute(candles.data(), candles.size(),
|
|
70
|
-
if (
|
|
71
|
-
vroom::rsi::compute_ma(rsi_cache,
|
|
74
|
+
if (!rsi.enabled || !rsi_dirty) return;
|
|
75
|
+
vroom::rsi::compute(candles.data(), candles.size(), rsi.period, rsi_cache);
|
|
76
|
+
if (rsi.ma_visible) {
|
|
77
|
+
vroom::rsi::compute_ma(rsi_cache, rsi.ma_period, rsi.ma_kind,
|
|
78
|
+
rsi_ma_cache);
|
|
72
79
|
} else {
|
|
73
80
|
rsi_ma_cache.clear();
|
|
74
81
|
}
|
|
@@ -76,9 +83,10 @@ void VroomChart::ensure_rsi() {
|
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
void VroomChart::ensure_macd() {
|
|
79
|
-
if (!
|
|
80
|
-
vroom::macd::compute(candles.data(), candles.size(),
|
|
81
|
-
|
|
86
|
+
if (!macd.enabled || !macd_dirty) return;
|
|
87
|
+
vroom::macd::compute(candles.data(), candles.size(), macd.fast, macd.slow,
|
|
88
|
+
macd.signal, macd.source, macd.ma_kind,
|
|
89
|
+
macd.signal_ma_kind, macd_cache, macd_signal_cache,
|
|
82
90
|
macd_hist_cache);
|
|
83
91
|
macd_dirty = false;
|
|
84
92
|
}
|
|
@@ -95,8 +103,8 @@ void VroomChart::ensure_overlays() {
|
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
void VroomChart::ensure_vwap() {
|
|
98
|
-
if (!
|
|
99
|
-
vroom::vwap::compute(candles.data(), candles.size(),
|
|
106
|
+
if (!vwap.enabled || !vwap_dirty) return;
|
|
107
|
+
vroom::vwap::compute(candles.data(), candles.size(), vwap.reset_offset_min,
|
|
100
108
|
vwap_cache, vwap_breaks);
|
|
101
109
|
vwap_dirty = false;
|
|
102
110
|
}
|
|
@@ -111,6 +119,7 @@ void VroomChart::ensure_bollinger() {
|
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
122
|
+
begin_frame();
|
|
114
123
|
const auto lay = layout();
|
|
115
124
|
|
|
116
125
|
// 1. Background
|
|
@@ -139,19 +148,60 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
139
148
|
|
|
140
149
|
// 3. Update label fade state ONCE per frame — both gridlines and labels
|
|
141
150
|
// share these opacities so their animations stay in lockstep.
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
// While an interval morph fades the old ticks out, the axes are still laid
|
|
152
|
+
// out against the pre-switch scale and window so nothing appears to move;
|
|
153
|
+
// they adopt the new ones at the midpoint, invisible. Every axis call in
|
|
154
|
+
// the frame has to agree on which of the two it's using.
|
|
155
|
+
const auto axis_phase = vroom::labels::interval_phase(*this);
|
|
156
|
+
const auto& axis_bounds = axis_phase.outgoing ? morph_from_bounds : bounds;
|
|
157
|
+
const int64_t axis_start_ms =
|
|
158
|
+
axis_phase.outgoing ? morph_from_start_ms : visible_start_ms;
|
|
159
|
+
const int64_t axis_end_ms =
|
|
160
|
+
axis_phase.outgoing ? morph_from_end_ms : visible_end_ms;
|
|
161
|
+
vroom::labels::update_y_fades(*this, lay, axis_bounds);
|
|
162
|
+
vroom::labels::update_x_fades(*this, lay, axis_start_ms, axis_end_ms);
|
|
144
163
|
|
|
145
164
|
// 4. Gridlines — drawn before candles so candle bodies overlay them.
|
|
146
165
|
// Vertical (time) gridlines are intentionally disabled for now —
|
|
147
166
|
// re-enable by adding `vroom::labels::draw_x_gridlines(canvas, *this,
|
|
148
|
-
// candle_right, candle_area_h);` here.
|
|
149
|
-
vroom::labels::draw_y_gridlines(canvas, *this, lay,
|
|
167
|
+
// axis_start_ms, axis_end_ms, candle_right, candle_area_h);` here.
|
|
168
|
+
vroom::labels::draw_y_gridlines(canvas, *this, lay, axis_bounds,
|
|
150
169
|
candle_right, candle_area_h);
|
|
151
170
|
|
|
171
|
+
// 4.35. Price-series morph state, shared by the gradient fill below and the
|
|
172
|
+
// series itself (5). `morph_fade` crossfades candles→line and
|
|
173
|
+
// `morph_collapse` folds each candle toward its close (the line vertex);
|
|
174
|
+
// fade 0 = pure candles, fade 1 = pure line. An interval morph
|
|
175
|
+
// additionally reshapes each slot from the geometry it held before the
|
|
176
|
+
// timeframe switch (see `morph_from`) — candle bodies, line vertices and
|
|
177
|
+
// the fill alike, so every layer stays in step mid-crossfade.
|
|
178
|
+
const float fade = morph_fade;
|
|
179
|
+
const float collapse = morph_collapse;
|
|
180
|
+
const bool morphing = interval_morph_t < 1.f && !morph_from.empty();
|
|
181
|
+
const vroom::CandleSnapshot* morph_src = morphing ? morph_from.data() : nullptr;
|
|
182
|
+
const std::size_t morph_n = morphing ? morph_from.size() : 0;
|
|
183
|
+
const float morph_t = morphing ? interval_morph_t : 1.f;
|
|
184
|
+
|
|
185
|
+
// 4.4. Line-mode gradient fill — the wash under the close polyline. Behind the
|
|
186
|
+
// volume bars so they stay legible on top of it, which puts it at the
|
|
187
|
+
// back of the price pane's content.
|
|
188
|
+
if (fade > 0.f) {
|
|
189
|
+
vroom::ma_overlay::draw_close_gradient(
|
|
190
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
191
|
+
visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
|
|
192
|
+
theme.colors[VROOM_COLOR_LINE],
|
|
193
|
+
theme.floats[VROOM_FLOAT_LINE_GRADIENT_OPACITY],
|
|
194
|
+
fade, morph_src, morph_n, morph_t);
|
|
195
|
+
}
|
|
196
|
+
|
|
152
197
|
// 4.5. Volume bars — drawn under the candles so candles z-index above.
|
|
153
|
-
|
|
154
|
-
|
|
198
|
+
// A fully collapsed chart has no bars left to draw, which is also how
|
|
199
|
+
// "volume disabled" is represented (set_volume snaps the scalar).
|
|
200
|
+
if (volume_collapse_t < 1.f) {
|
|
201
|
+
vroom::volume::draw(canvas, visible, n, lay, theme, volume,
|
|
202
|
+
volume_collapse_t, volume_collapse_easing,
|
|
203
|
+
window_ms, visible_start_ms, candle_duration_ms);
|
|
204
|
+
}
|
|
155
205
|
|
|
156
206
|
// 4.6. Liquidity bands (resting-order depth) — behind the candles so the
|
|
157
207
|
// candle bodies paint over the volume-driven tint. Anchored in price
|
|
@@ -177,25 +227,19 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
177
227
|
}
|
|
178
228
|
|
|
179
229
|
// 5. Price series — candles, a close-price line, or a blend of the two during
|
|
180
|
-
// the candle↔line morph.
|
|
181
|
-
//
|
|
182
|
-
// fade 0 = pure candles, fade 1 = pure line. The line reuses the MA-overlay
|
|
183
|
-
// polyline routine, fed the visible closes and styled by theme.LINE.
|
|
184
|
-
const float fade = morph_fade;
|
|
185
|
-
const float collapse = morph_collapse;
|
|
230
|
+
// the candle↔line morph (state hoisted to 4.35 for the gradient fill). The
|
|
231
|
+
// line is styled by theme.LINE.
|
|
186
232
|
if (fade < 1.f) {
|
|
187
233
|
vroom::candles::draw(canvas, visible, n, lay, theme, bounds, window_ms,
|
|
188
234
|
visible_start_ms, candle_duration_ms, collapse,
|
|
189
|
-
1.f - fade);
|
|
235
|
+
1.f - fade, morph_src, morph_n, morph_t);
|
|
190
236
|
}
|
|
191
237
|
if (fade > 0.f) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
vroom::ma_overlay::draw(
|
|
195
|
-
canvas, lay, bounds, visible, n, closes.data(), window_ms,
|
|
238
|
+
vroom::ma_overlay::draw_close_line(
|
|
239
|
+
canvas, lay, bounds, visible, n, window_ms,
|
|
196
240
|
visible_start_ms, candle_duration_ms, candle_right, candle_area_h,
|
|
197
241
|
theme.colors[VROOM_COLOR_LINE], theme.floats[VROOM_FLOAT_LINE_WIDTH_PX],
|
|
198
|
-
|
|
242
|
+
fade, morph_src, morph_n, morph_t);
|
|
199
243
|
}
|
|
200
244
|
|
|
201
245
|
// 5.5. Moving-average overlay lines (SMA/EMA) on the price pane, over the
|
|
@@ -215,7 +259,7 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
215
259
|
|
|
216
260
|
// 5.6. VWAP overlay (session, configurable reset) — a single price-pane line
|
|
217
261
|
// that breaks at each session reset (vwap_breaks).
|
|
218
|
-
if (
|
|
262
|
+
if (vwap.enabled) {
|
|
219
263
|
ensure_vwap();
|
|
220
264
|
if (vwap_cache.size() == candles.size()) {
|
|
221
265
|
const double* vis = vwap_cache.data() + range.start;
|
|
@@ -225,7 +269,10 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
225
269
|
vroom::ma_overlay::draw(canvas, lay, bounds, visible, n, vis,
|
|
226
270
|
window_ms, visible_start_ms,
|
|
227
271
|
candle_duration_ms, candle_right,
|
|
228
|
-
candle_area_h,
|
|
272
|
+
candle_area_h,
|
|
273
|
+
vroom::style::color_or(vwap.color, kVwapLine),
|
|
274
|
+
vroom::style::width_or(vwap.width, 1.5f),
|
|
275
|
+
brk);
|
|
229
276
|
}
|
|
230
277
|
}
|
|
231
278
|
|
|
@@ -271,14 +318,20 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
271
318
|
axis_bg);
|
|
272
319
|
|
|
273
320
|
// 7. Labels (read from y_fades / x_fades, no state mutation here)
|
|
274
|
-
vroom::labels::draw_y_labels(canvas, *this, lay,
|
|
275
|
-
vroom::labels::draw_x_labels(canvas, *this, lay);
|
|
321
|
+
vroom::labels::draw_y_labels(canvas, *this, lay, axis_bounds);
|
|
322
|
+
vroom::labels::draw_x_labels(canvas, *this, lay, axis_start_ms, axis_end_ms);
|
|
276
323
|
|
|
277
324
|
// 7.5. Current-price line + box — above labels so the box covers any label
|
|
278
325
|
// it overlaps; tracks the latest close as the price scale moves.
|
|
279
326
|
vroom::price_indicator::draw(canvas, *this, lay, bounds,
|
|
280
327
|
candle_right, candle_area_h);
|
|
281
328
|
|
|
329
|
+
// 7.55. Consumer-supplied price status lines — same tier as the current-price
|
|
330
|
+
// indicator (their badges must cover the labels underneath), but after
|
|
331
|
+
// it so a resting order at the last close stays readable.
|
|
332
|
+
vroom::price_lines::draw(canvas, *this, lay, bounds, candle_right,
|
|
333
|
+
candle_area_h);
|
|
334
|
+
|
|
282
335
|
// 7.6. Indicator panes stacked below the candles, ordered by enable
|
|
283
336
|
// sequence (most recently enabled at the bottom). Each pane is
|
|
284
337
|
// INDICATOR_HEIGHT_FRAC of the height; the candle pane already shrank
|
|
@@ -287,8 +340,8 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
287
340
|
struct ActivePane { int order; int type; }; // type: 0 = RSI, 1 = MACD
|
|
288
341
|
ActivePane panes[2];
|
|
289
342
|
int count = 0;
|
|
290
|
-
if (
|
|
291
|
-
if (
|
|
343
|
+
if (rsi.enabled) panes[count++] = {rsi_order, 0};
|
|
344
|
+
if (macd.enabled) panes[count++] = {macd_order, 1};
|
|
292
345
|
if (count == 2 && panes[0].order > panes[1].order) {
|
|
293
346
|
const ActivePane tmp = panes[0];
|
|
294
347
|
panes[0] = panes[1];
|
|
@@ -305,7 +358,7 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
305
358
|
const double* rsi_vis = rsi_cache.size() == candles.size()
|
|
306
359
|
? rsi_cache.data() + range.start : nullptr;
|
|
307
360
|
const double* rsi_ma_vis =
|
|
308
|
-
(
|
|
361
|
+
(rsi.ma_visible && rsi_ma_cache.size() == candles.size())
|
|
309
362
|
? rsi_ma_cache.data() + range.start : nullptr;
|
|
310
363
|
vroom::rsi_pane::draw(canvas, *this, lay, visible, n, rsi_vis,
|
|
311
364
|
rsi_ma_vis, window_ms, visible_start_ms,
|
|
@@ -354,19 +407,24 @@ void VroomChart::draw_chart(SkCanvas* canvas) {
|
|
|
354
407
|
vroom::labels::gc_x_fades(*this);
|
|
355
408
|
}
|
|
356
409
|
|
|
357
|
-
void VroomChart::
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
|
|
410
|
+
void VroomChart::begin_frame() {
|
|
411
|
+
// dt for the fade animations. A large gap (resume from background, or an idle
|
|
412
|
+
// chart that a click just woke up) is clamped to a nominal frame rather than
|
|
413
|
+
// zeroed: dt == 0 means "snap" to the fade updaters, which would finish every
|
|
414
|
+
// fade in the first frame after any idle period.
|
|
415
|
+
constexpr float kNominalFrameSeconds = 1.f / 60.f;
|
|
416
|
+
const auto now = std::chrono::steady_clock::now();
|
|
361
417
|
float dt = 0.f;
|
|
362
418
|
if (anim_started) {
|
|
363
419
|
dt = std::chrono::duration<float>(now - last_anim_tick).count();
|
|
364
|
-
if (dt > 0.1f) dt =
|
|
420
|
+
if (dt > 0.1f) dt = kNominalFrameSeconds;
|
|
365
421
|
}
|
|
366
422
|
last_anim_tick = now;
|
|
367
423
|
anim_started = true;
|
|
368
424
|
last_dt_seconds = dt;
|
|
425
|
+
}
|
|
369
426
|
|
|
427
|
+
void VroomChart::rebuild_chart_picture() {
|
|
370
428
|
SkPictureRecorder recorder;
|
|
371
429
|
SkCanvas* canvas = recorder.beginRecording(SkRect::MakeWH(width_px, height_px));
|
|
372
430
|
draw_chart(canvas);
|
package/cpp/_core_src/chart.h
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#pragma once
|
|
10
10
|
|
|
11
11
|
#include <chrono>
|
|
12
|
+
#include <string>
|
|
12
13
|
#include <vector>
|
|
13
14
|
|
|
14
15
|
#include "vroom/vroom_chart.h"
|
|
@@ -63,6 +64,21 @@ struct VroomChart {
|
|
|
63
64
|
float morph_collapse = 0.f;
|
|
64
65
|
float morph_fade = 0.f;
|
|
65
66
|
|
|
67
|
+
// Interval morph: the outgoing candle geometry captured when a timeframe
|
|
68
|
+
// switch begins, indexed from the right of the visible slice (slot 0 =
|
|
69
|
+
// newest) — the pairing the preserved slot grid guarantees. Stored as
|
|
70
|
+
// normalized fractions so it survives the new bounds and a resize.
|
|
71
|
+
// Empty when not morphing.
|
|
72
|
+
std::vector<vroom::CandleSnapshot> morph_from;
|
|
73
|
+
float interval_morph_t = 1.f; // 1 = not morphing
|
|
74
|
+
// The pre-switch price scale and time window. The candle capture above is
|
|
75
|
+
// normalized against these, and the axes keep rendering their old ticks from
|
|
76
|
+
// them while fading out (see labels::interval_phase), so a label is never
|
|
77
|
+
// drawn at a position it didn't already occupy.
|
|
78
|
+
vroom::PriceBounds morph_from_bounds{0.0, 1.0};
|
|
79
|
+
int64_t morph_from_start_ms = 0;
|
|
80
|
+
int64_t morph_from_end_ms = 0;
|
|
81
|
+
|
|
66
82
|
// Cached y-axis width in pixels, sized to fit the widest formatted price
|
|
67
83
|
// label. 0 = uncomputed; layout() falls back to a width ratio.
|
|
68
84
|
float axis_width_px = 0.f;
|
|
@@ -85,24 +101,27 @@ struct VroomChart {
|
|
|
85
101
|
// RSI, drawn in a pane below the candles. rsi_cache is aligned to `candles`
|
|
86
102
|
// (one value per candle, NaN where undefined) and recomputed lazily by
|
|
87
103
|
// ensure_rsi() when rsi_dirty (set on any data or config change).
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
// Defaults: 14-period RSI with 70/30 bands and a 14-period SMA trendline,
|
|
105
|
+
// every style field on its inherit sentinel.
|
|
106
|
+
VroomRSI rsi{0, 14, 70.0, 30.0, 14, 0, 1,
|
|
107
|
+
0u, -1.f, 1, // RSI line
|
|
108
|
+
0u, -1.f, // trendline
|
|
109
|
+
0u, 1}; // band rules
|
|
94
110
|
std::vector<double> rsi_cache; // RSI per candle (NaN where undefined)
|
|
95
|
-
std::vector<double> rsi_ma_cache; //
|
|
111
|
+
std::vector<double> rsi_ma_cache; // MA of rsi_cache (empty if MA off)
|
|
96
112
|
bool rsi_dirty = true;
|
|
97
113
|
// User y-axis zoom for the RSI pane (drag on its y-axis strip). 1.0 = the
|
|
98
114
|
// default 0..100 fit; >1 zooms in (taller), <1 zooms out. Anchored at 50.
|
|
99
115
|
double rsi_y_scale = 1.0;
|
|
100
116
|
|
|
101
117
|
// MACD, drawn in its own pane. Caches aligned to `candles` (NaN warmup).
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
// Defaults: 12/26/9 EMAs of close, every style field on its inherit
|
|
119
|
+
// sentinel so an untouched chart keeps the stock look.
|
|
120
|
+
VroomMACD macd{0, 12, 26, 9, 0, 1, 1,
|
|
121
|
+
0u, -1.f, 1, // MACD line
|
|
122
|
+
0u, -1.f, 1, // signal line
|
|
123
|
+
1, 0u, 0u, 0u, 0u, // histogram
|
|
124
|
+
0u, 1}; // zero line
|
|
106
125
|
std::vector<double> macd_cache;
|
|
107
126
|
std::vector<double> macd_signal_cache;
|
|
108
127
|
std::vector<double> macd_hist_cache;
|
|
@@ -127,10 +146,8 @@ struct VroomChart {
|
|
|
127
146
|
|
|
128
147
|
// VWAP overlay (session anchor, configurable reset time). Single line on the
|
|
129
148
|
// price pane; vwap_breaks marks session resets so the line lifts the pen.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
uint32_t vwap_color = 0xff00bcd4; // cyan
|
|
133
|
-
float vwap_width = 1.5f;
|
|
149
|
+
// Style fields sit on their inherit sentinel (cyan, 1.5px).
|
|
150
|
+
VroomVWAP vwap{0, 0, 0u, -1.f};
|
|
134
151
|
std::vector<double> vwap_cache;
|
|
135
152
|
std::vector<unsigned char> vwap_breaks;
|
|
136
153
|
bool vwap_dirty = true;
|
|
@@ -149,6 +166,18 @@ struct VroomChart {
|
|
|
149
166
|
std::vector<double> bb_lower_cache;
|
|
150
167
|
bool bollinger_dirty = true;
|
|
151
168
|
|
|
169
|
+
// Volume bars (price pane, under the candles). On by default with every
|
|
170
|
+
// style field left on its inherit sentinel, so an untouched chart looks
|
|
171
|
+
// exactly as it did before the config existed. No cache — bar heights come
|
|
172
|
+
// straight off the visible candles' volume.
|
|
173
|
+
VroomVolume volume{1, -1.f, -1.f, -1.f, 0u, 0u};
|
|
174
|
+
// Staggered collapse of those bars, driven per-frame by the host animation
|
|
175
|
+
// loop: 0 = full height, 1 = all flat. Doubles as the visibility gate, since
|
|
176
|
+
// a fully collapsed chart draws nothing — set_volume snaps it to match
|
|
177
|
+
// `volume.enabled`, and the host overrides it while animating.
|
|
178
|
+
float volume_collapse_t = 0.f;
|
|
179
|
+
int32_t volume_collapse_easing = VROOM_EASING_IN_OUT;
|
|
180
|
+
|
|
152
181
|
// --- drawings (annotations) --------------------------------------------
|
|
153
182
|
// Committed drawings, anchored in data space so they track the candles on
|
|
154
183
|
// pan/zoom. Drawn on the price pane above candles/overlays.
|
|
@@ -195,6 +224,32 @@ struct VroomChart {
|
|
|
195
224
|
std::vector<VroomBand> bands;
|
|
196
225
|
VroomLiquidityStyle liquidity_style{};
|
|
197
226
|
|
|
227
|
+
// --- price status lines -------------------------------------------------
|
|
228
|
+
// Consumer-supplied horizontal lines at fixed prices (resting orders, TP/SL,
|
|
229
|
+
// liquidation levels), each with a label group and an optional close button.
|
|
230
|
+
//
|
|
231
|
+
// Mirrors the public VroomPriceLine but *owns* its label strings, so the
|
|
232
|
+
// caller may free theirs as soon as the setter returns.
|
|
233
|
+
struct StoredPriceLine {
|
|
234
|
+
double price = 0.0;
|
|
235
|
+
uint32_t color = 0xffef5350;
|
|
236
|
+
float width = 1.f;
|
|
237
|
+
int32_t line_style = 1; // dotted, matching the price indicator
|
|
238
|
+
std::string text;
|
|
239
|
+
std::string quantity;
|
|
240
|
+
int32_t flags = 0; // VroomPriceLineFlags
|
|
241
|
+
};
|
|
242
|
+
std::vector<StoredPriceLine> price_lines;
|
|
243
|
+
VroomPriceLineStyle price_line_style{};
|
|
244
|
+
|
|
245
|
+
// Interaction state, driven by the host's gesture layer. The hovered segment
|
|
246
|
+
// renders highlighted; while a line is dragged it renders at
|
|
247
|
+
// dragged_price_line_price with a ghost at its committed price. -1 = none.
|
|
248
|
+
int32_t hovered_price_line = -1;
|
|
249
|
+
int32_t hovered_price_line_part = -1;
|
|
250
|
+
int32_t dragged_price_line = -1;
|
|
251
|
+
double dragged_price_line_price = 0.0;
|
|
252
|
+
|
|
198
253
|
// --- theme --------------------------------------------------------------
|
|
199
254
|
vroom::Theme theme;
|
|
200
255
|
|
|
@@ -208,9 +263,9 @@ struct VroomChart {
|
|
|
208
263
|
std::chrono::steady_clock::time_point last_anim_tick{};
|
|
209
264
|
bool anim_started = false;
|
|
210
265
|
|
|
211
|
-
// dt of the current
|
|
212
|
-
//
|
|
213
|
-
//
|
|
266
|
+
// dt of the current frame — populated by begin_frame and consumed by the
|
|
267
|
+
// label-fade updaters. Public so the labels namespace can read it without a
|
|
268
|
+
// getter.
|
|
214
269
|
float last_dt_seconds = 0.f;
|
|
215
270
|
|
|
216
271
|
// --- methods ------------------------------------------------------------
|
|
@@ -240,11 +295,18 @@ struct VroomChart {
|
|
|
240
295
|
// indicator is enabled.
|
|
241
296
|
void ensure_bollinger();
|
|
242
297
|
|
|
243
|
-
// The main drawing pass. Calls into the labels and candles modules
|
|
298
|
+
// The main drawing pass. Calls into the labels and candles modules, and owns
|
|
299
|
+
// the per-frame animation clock (see begin_frame) so every host gets it —
|
|
300
|
+
// the web build draws straight through vroom_chart_draw rather than the
|
|
301
|
+
// SkPicture cache below.
|
|
244
302
|
void draw_chart(SkCanvas* canvas);
|
|
245
303
|
|
|
304
|
+
// Per-frame animation bookkeeping: computes `last_dt_seconds`, which paces
|
|
305
|
+
// the label fades. Called at the top of draw_chart.
|
|
306
|
+
void begin_frame();
|
|
307
|
+
|
|
246
308
|
// Re-records `chart_picture` by invoking draw_chart on a fresh
|
|
247
|
-
// SkPictureRecorder.
|
|
309
|
+
// SkPictureRecorder.
|
|
248
310
|
void rebuild_chart_picture();
|
|
249
311
|
|
|
250
312
|
// True if any axis label is mid-fade. Used by the JS-side animation loop
|