react-native-vroom-chart 0.14.0 → 0.16.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 (47) hide show
  1. package/cpp/VroomChartHostObject.cpp +289 -2
  2. package/cpp/_core_include/vroom/vroom_chart.h +221 -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/chart.cpp +477 -172
  8. package/cpp/_core_src/chart.h +173 -1
  9. package/cpp/_core_src/chart_facade.cpp +317 -23
  10. package/cpp/_core_src/fair_value_gaps.cpp +76 -0
  11. package/cpp/_core_src/fair_value_gaps.h +54 -0
  12. package/cpp/_core_src/footprints.cpp +226 -0
  13. package/cpp/_core_src/footprints.h +45 -0
  14. package/cpp/_core_src/footprints_layout.cpp +140 -0
  15. package/cpp/_core_src/footprints_layout.h +94 -0
  16. package/cpp/_core_src/fvg_overlay.cpp +262 -0
  17. package/cpp/_core_src/fvg_overlay.h +43 -0
  18. package/cpp/_core_src/ichimoku.cpp +65 -0
  19. package/cpp/_core_src/ichimoku.h +45 -0
  20. package/cpp/_core_src/labels.cpp +3 -0
  21. package/cpp/_core_src/line_morph.h +159 -0
  22. package/cpp/_core_src/ma_overlay.cpp +259 -36
  23. package/cpp/_core_src/ma_overlay.h +57 -2
  24. package/cpp/_core_src/macd.cpp +24 -1
  25. package/cpp/_core_src/macd.h +16 -0
  26. package/cpp/_core_src/macd_pane.cpp +71 -62
  27. package/cpp/_core_src/macd_pane.h +13 -1
  28. package/cpp/_core_src/pane_series.h +169 -0
  29. package/cpp/_core_src/rsi.cpp +4 -0
  30. package/cpp/_core_src/rsi.h +7 -0
  31. package/cpp/_core_src/rsi_pane.cpp +85 -29
  32. package/cpp/_core_src/rsi_pane.h +11 -1
  33. package/cpp/_core_src/tip_pulse.h +4 -4
  34. package/cpp/_core_src/viewport.h +35 -0
  35. package/lib/index.d.mts +385 -3
  36. package/lib/index.d.ts +385 -3
  37. package/lib/index.js +305 -7
  38. package/lib/index.js.map +1 -1
  39. package/lib/index.mjs +305 -7
  40. package/lib/index.mjs.map +1 -1
  41. package/package.json +1 -1
  42. package/src/VroomChart.tsx +108 -7
  43. package/src/dataTransitions.ts +47 -0
  44. package/src/index.ts +10 -0
  45. package/src/jsi.d.ts +136 -1
  46. package/src/types.ts +10 -0
  47. package/src/useChartCore.ts +330 -5
@@ -42,7 +42,7 @@ ChartHostObject::~ChartHostObject() {
42
42
  std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
43
43
  jsi::Runtime& rt) {
44
44
  std::vector<jsi::PropNameID> out;
45
- out.reserve(39);
45
+ out.reserve(42);
46
46
  out.push_back(jsi::PropNameID::forAscii(rt, "setCandles"));
47
47
  out.push_back(jsi::PropNameID::forAscii(rt, "setSize"));
48
48
  out.push_back(jsi::PropNameID::forAscii(rt, "setColor"));
@@ -57,6 +57,7 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
57
57
  out.push_back(jsi::PropNameID::forAscii(rt, "getVisiblePriceEnvelope"));
58
58
  out.push_back(jsi::PropNameID::forAscii(rt, "preservePriceEnvelope"));
59
59
  out.push_back(jsi::PropNameID::forAscii(rt, "beginIntervalMorph"));
60
+ out.push_back(jsi::PropNameID::forAscii(rt, "beginStreamMorph"));
60
61
  out.push_back(jsi::PropNameID::forAscii(rt, "setIntervalMorph"));
61
62
  out.push_back(jsi::PropNameID::forAscii(rt, "pan"));
62
63
  out.push_back(jsi::PropNameID::forAscii(rt, "translate"));
@@ -71,9 +72,12 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
71
72
  out.push_back(jsi::PropNameID::forAscii(rt, "getCrosshairInfo"));
72
73
  out.push_back(jsi::PropNameID::forAscii(rt, "setRSI"));
73
74
  out.push_back(jsi::PropNameID::forAscii(rt, "setMACD"));
75
+ out.push_back(jsi::PropNameID::forAscii(rt, "setATR"));
74
76
  out.push_back(jsi::PropNameID::forAscii(rt, "setOverlays"));
75
77
  out.push_back(jsi::PropNameID::forAscii(rt, "setVWAP"));
76
78
  out.push_back(jsi::PropNameID::forAscii(rt, "setBollinger"));
79
+ out.push_back(jsi::PropNameID::forAscii(rt, "setIchimoku"));
80
+ out.push_back(jsi::PropNameID::forAscii(rt, "setFairValueGaps"));
77
81
  out.push_back(jsi::PropNameID::forAscii(rt, "setVolume"));
78
82
  out.push_back(jsi::PropNameID::forAscii(rt, "setVolumeCollapse"));
79
83
  out.push_back(jsi::PropNameID::forAscii(rt, "setAxisCollapse"));
@@ -82,6 +86,9 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
82
86
  out.push_back(jsi::PropNameID::forAscii(rt, "hitTestPriceLine"));
83
87
  out.push_back(jsi::PropNameID::forAscii(rt, "setPriceLineHover"));
84
88
  out.push_back(jsi::PropNameID::forAscii(rt, "setPriceLineDrag"));
89
+ out.push_back(jsi::PropNameID::forAscii(rt, "setFootprints"));
90
+ out.push_back(jsi::PropNameID::forAscii(rt, "hitTestFootprint"));
91
+ out.push_back(jsi::PropNameID::forAscii(rt, "setFootprintHover"));
85
92
  out.push_back(jsi::PropNameID::forAscii(rt, "render"));
86
93
  return out;
87
94
  }
@@ -507,6 +514,24 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
507
514
  });
508
515
  }
509
516
 
517
+ if (name == "beginStreamMorph") {
518
+ // beginStreamMorph() — capture the visible geometry so the next setCandles
519
+ // can ease a live tick into place. Leaves the axes alone, and continues
520
+ // from the shape on screen when one is still animating. Call before
521
+ // setCandles.
522
+ return jsi::Function::createFromHostFunction(
523
+ rt,
524
+ jsi::PropNameID::forAscii(rt, "beginStreamMorph"),
525
+ 0,
526
+ [this](jsi::Runtime& /*rt2*/,
527
+ const jsi::Value& /*thisVal*/,
528
+ const jsi::Value* /*args*/,
529
+ size_t /*count*/) -> jsi::Value {
530
+ vroom_chart_begin_stream_morph(chart_);
531
+ return jsi::Value::undefined();
532
+ });
533
+ }
534
+
510
535
  if (name == "setIntervalMorph") {
511
536
  // setIntervalMorph(t) — advance the capture toward the new candles. `t` is
512
537
  // pre-eased progress: 0 = the captured frame, 1 = settled (capture freed).
@@ -758,7 +783,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
758
783
  if (name == "setRSI") {
759
784
  // setRSI({enabled, period, upperBand, lowerBand, maPeriod, maKind,
760
785
  // maVisible, lineColor, lineWidth, lineVisible, maColor, maWidth,
761
- // bandColor, bandsVisible}) — configures the RSI pane. No render; the next
786
+ // bandColor, bandsVisible, extremeFill}) — configures the RSI pane. No
787
+ // render; the next
762
788
  // render() picks it up.
763
789
  return jsi::Function::createFromHostFunction(
764
790
  rt,
@@ -794,6 +820,8 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
794
820
  s.getProperty(rt2, "bandColor").asNumber());
795
821
  cfg.bands_visible =
796
822
  s.getProperty(rt2, "bandsVisible").asBool() ? 1 : 0;
823
+ cfg.extreme_fill =
824
+ s.getProperty(rt2, "extremeFill").asBool() ? 1 : 0;
797
825
  vroom_chart_set_rsi(chart_, &cfg);
798
826
  return jsi::Value::undefined();
799
827
  });
@@ -860,6 +888,34 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
860
888
  });
861
889
  }
862
890
 
891
+ if (name == "setATR") {
892
+ // setATR({enabled, period, smoothing, lineColor, lineWidth}) — configures
893
+ // the ATR pane. No render; the next render() picks it up.
894
+ return jsi::Function::createFromHostFunction(
895
+ rt,
896
+ jsi::PropNameID::forAscii(rt, "setATR"),
897
+ 1,
898
+ [this](jsi::Runtime& rt2,
899
+ const jsi::Value& /*thisVal*/,
900
+ const jsi::Value* args,
901
+ size_t count) -> jsi::Value {
902
+ if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
903
+ auto s = args[0].asObject(rt2);
904
+ VroomATR cfg{};
905
+ cfg.enabled = s.getProperty(rt2, "enabled").asBool() ? 1 : 0;
906
+ cfg.period = static_cast<int32_t>(
907
+ s.getProperty(rt2, "period").asNumber());
908
+ cfg.smoothing = static_cast<int32_t>(
909
+ s.getProperty(rt2, "smoothing").asNumber());
910
+ cfg.line_color = static_cast<uint32_t>(
911
+ s.getProperty(rt2, "lineColor").asNumber());
912
+ cfg.line_width = static_cast<float>(
913
+ s.getProperty(rt2, "lineWidth").asNumber());
914
+ vroom_chart_set_atr(chart_, &cfg);
915
+ return jsi::Value::undefined();
916
+ });
917
+ }
918
+
863
919
  if (name == "setOverlays") {
864
920
  // setOverlays([{ kind, period, source, color, width }, ...]) — replaces the
865
921
  // full set of MA/EMA overlay lines. No render; the next render() picks it up.
@@ -969,6 +1025,123 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
969
1025
  });
970
1026
  }
971
1027
 
1028
+ if (name == "setIchimoku") {
1029
+ // setIchimoku({enabled, tenkanPeriod, kijunPeriod, senkouBPeriod,
1030
+ // displacement, <line>Color/<line>Width/<line>Enabled for tenkan, kijun,
1031
+ // senkouA, senkouB and chikou, cloudEnabled, bullishCloudColor,
1032
+ // bearishCloudColor, cloudOpacity}) — the Ichimoku overlay. No render; the
1033
+ // next render() picks it up.
1034
+ return jsi::Function::createFromHostFunction(
1035
+ rt,
1036
+ jsi::PropNameID::forAscii(rt, "setIchimoku"),
1037
+ 1,
1038
+ [this](jsi::Runtime& rt2,
1039
+ const jsi::Value& /*thisVal*/,
1040
+ const jsi::Value* args,
1041
+ size_t count) -> jsi::Value {
1042
+ if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
1043
+ auto s = args[0].asObject(rt2);
1044
+ const auto num = [&](const char* k) {
1045
+ return s.getProperty(rt2, k).asNumber();
1046
+ };
1047
+ const auto flag = [&](const char* k) {
1048
+ return s.getProperty(rt2, k).asBool() ? 1 : 0;
1049
+ };
1050
+ VroomIchimoku cfg{};
1051
+ cfg.enabled = flag("enabled");
1052
+ cfg.tenkan_period = static_cast<int32_t>(num("tenkanPeriod"));
1053
+ cfg.kijun_period = static_cast<int32_t>(num("kijunPeriod"));
1054
+ cfg.senkou_b_period = static_cast<int32_t>(num("senkouBPeriod"));
1055
+ cfg.displacement = static_cast<int32_t>(num("displacement"));
1056
+ cfg.tenkan_color = static_cast<uint32_t>(num("tenkanColor"));
1057
+ cfg.tenkan_width = static_cast<float>(num("tenkanWidth"));
1058
+ cfg.tenkan_enabled = flag("tenkanEnabled");
1059
+ cfg.kijun_color = static_cast<uint32_t>(num("kijunColor"));
1060
+ cfg.kijun_width = static_cast<float>(num("kijunWidth"));
1061
+ cfg.kijun_enabled = flag("kijunEnabled");
1062
+ cfg.senkou_a_color = static_cast<uint32_t>(num("senkouAColor"));
1063
+ cfg.senkou_a_width = static_cast<float>(num("senkouAWidth"));
1064
+ cfg.senkou_a_enabled = flag("senkouAEnabled");
1065
+ cfg.senkou_b_color = static_cast<uint32_t>(num("senkouBColor"));
1066
+ cfg.senkou_b_width = static_cast<float>(num("senkouBWidth"));
1067
+ cfg.senkou_b_enabled = flag("senkouBEnabled");
1068
+ cfg.chikou_color = static_cast<uint32_t>(num("chikouColor"));
1069
+ cfg.chikou_width = static_cast<float>(num("chikouWidth"));
1070
+ cfg.chikou_enabled = flag("chikouEnabled");
1071
+ cfg.cloud_enabled = flag("cloudEnabled");
1072
+ cfg.bullish_cloud_color =
1073
+ static_cast<uint32_t>(num("bullishCloudColor"));
1074
+ cfg.bearish_cloud_color =
1075
+ static_cast<uint32_t>(num("bearishCloudColor"));
1076
+ cfg.cloud_opacity = static_cast<float>(num("cloudOpacity"));
1077
+ vroom_chart_set_ichimoku(chart_, &cfg);
1078
+ return jsi::Value::undefined();
1079
+ });
1080
+ }
1081
+
1082
+ if (name == "setFairValueGaps") {
1083
+ // setFairValueGaps({enabled, maxBarsBack, waitForClose, fillType,
1084
+ // deleteAfterFill, extendBoxes, boxLength, bullishColor, bearishColor,
1085
+ // opacity, borderEnabled, borderStyle, borderWidth, bullishBorderColor,
1086
+ // bearishBorderColor, labelsEnabled, label, labelDistance, labelColor,
1087
+ // labelFontSize, showInverse, inverseBullishColor, inverseBearishColor,
1088
+ // inverseLabel}) — the Fair Value Gap overlay. No render; the next
1089
+ // render() picks it up.
1090
+ return jsi::Function::createFromHostFunction(
1091
+ rt,
1092
+ jsi::PropNameID::forAscii(rt, "setFairValueGaps"),
1093
+ 1,
1094
+ [this](jsi::Runtime& rt2,
1095
+ const jsi::Value& /*thisVal*/,
1096
+ const jsi::Value* args,
1097
+ size_t count) -> jsi::Value {
1098
+ if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
1099
+ auto s = args[0].asObject(rt2);
1100
+ const auto num = [&](const char* k) {
1101
+ return s.getProperty(rt2, k).asNumber();
1102
+ };
1103
+ const auto flag = [&](const char* k) {
1104
+ return s.getProperty(rt2, k).asBool() ? 1 : 0;
1105
+ };
1106
+ VroomFairValueGaps cfg{};
1107
+ cfg.enabled = flag("enabled");
1108
+ cfg.max_bars_back = static_cast<int32_t>(num("maxBarsBack"));
1109
+ cfg.wait_for_close = flag("waitForClose");
1110
+ cfg.fill_type = static_cast<int32_t>(num("fillType"));
1111
+ cfg.delete_after_fill = flag("deleteAfterFill");
1112
+ cfg.extend_boxes = flag("extendBoxes");
1113
+ cfg.box_length = static_cast<int32_t>(num("boxLength"));
1114
+ cfg.bullish_color = static_cast<uint32_t>(num("bullishColor"));
1115
+ cfg.bearish_color = static_cast<uint32_t>(num("bearishColor"));
1116
+ cfg.opacity = static_cast<float>(num("opacity"));
1117
+ cfg.border_enabled = flag("borderEnabled");
1118
+ cfg.border_style = static_cast<int32_t>(num("borderStyle"));
1119
+ cfg.border_width = static_cast<float>(num("borderWidth"));
1120
+ cfg.bullish_border_color =
1121
+ static_cast<uint32_t>(num("bullishBorderColor"));
1122
+ cfg.bearish_border_color =
1123
+ static_cast<uint32_t>(num("bearishBorderColor"));
1124
+ cfg.labels_enabled = flag("labelsEnabled");
1125
+ // Held alive until the setter, which copies them.
1126
+ const std::string label =
1127
+ s.getProperty(rt2, "label").asString(rt2).utf8(rt2);
1128
+ cfg.label = label.c_str();
1129
+ cfg.label_distance = static_cast<int32_t>(num("labelDistance"));
1130
+ cfg.label_color = static_cast<uint32_t>(num("labelColor"));
1131
+ cfg.label_font_size = static_cast<float>(num("labelFontSize"));
1132
+ cfg.show_inverse = flag("showInverse");
1133
+ cfg.inverse_bullish_color =
1134
+ static_cast<uint32_t>(num("inverseBullishColor"));
1135
+ cfg.inverse_bearish_color =
1136
+ static_cast<uint32_t>(num("inverseBearishColor"));
1137
+ const std::string inverse_label =
1138
+ s.getProperty(rt2, "inverseLabel").asString(rt2).utf8(rt2);
1139
+ cfg.inverse_label = inverse_label.c_str();
1140
+ vroom_chart_set_fair_value_gaps(chart_, &cfg);
1141
+ return jsi::Value::undefined();
1142
+ });
1143
+ }
1144
+
972
1145
  if (name == "setVolume") {
973
1146
  // setVolume({enabled, heightFrac, opacity, radiusPx, upColor, downColor})
974
1147
  // — the volume bars. Negative floats / zero colors inherit the theme. No
@@ -1189,6 +1362,120 @@ jsi::Value ChartHostObject::get(jsi::Runtime& rt,
1189
1362
  });
1190
1363
  }
1191
1364
 
1365
+ if (name == "setFootprints") {
1366
+ // setFootprints({ prints: [{ timeMs, side }, ...], radiusPx, gapPx, marginPx,
1367
+ // hoverBoost }) — replaces the full set of executed-trade badges. The core
1368
+ // buckets them onto candles itself, so raw fill times are what to pass. No
1369
+ // render; the next render() picks it up.
1370
+ return jsi::Function::createFromHostFunction(
1371
+ rt,
1372
+ jsi::PropNameID::forAscii(rt, "setFootprints"),
1373
+ 1,
1374
+ [this](jsi::Runtime& rt2,
1375
+ const jsi::Value& /*thisVal*/,
1376
+ const jsi::Value* args,
1377
+ size_t count) -> jsi::Value {
1378
+ if (count < 1 || !args[0].isObject()) return jsi::Value::undefined();
1379
+ auto cfg = args[0].asObject(rt2);
1380
+ auto prints_val = cfg.getProperty(rt2, "prints");
1381
+ if (!prints_val.isObject()) return jsi::Value::undefined();
1382
+ auto prints_obj = prints_val.asObject(rt2);
1383
+ if (!prints_obj.isArray(rt2)) return jsi::Value::undefined();
1384
+ auto arr = prints_obj.asArray(rt2);
1385
+ const size_t len = arr.size(rt2);
1386
+ std::vector<VroomFootprint> prints(len);
1387
+ for (size_t i = 0; i < len; ++i) {
1388
+ auto f = arr.getValueAtIndex(rt2, i).asObject(rt2);
1389
+ prints[i].time_ms =
1390
+ static_cast<int64_t>(f.getProperty(rt2, "timeMs").asNumber());
1391
+ prints[i].side =
1392
+ static_cast<int32_t>(f.getProperty(rt2, "side").asNumber());
1393
+ }
1394
+ VroomFootprintStyle style{};
1395
+ style.radius_px = static_cast<float>(
1396
+ cfg.getProperty(rt2, "radiusPx").asNumber());
1397
+ style.gap_px = static_cast<float>(
1398
+ cfg.getProperty(rt2, "gapPx").asNumber());
1399
+ style.margin_px = static_cast<float>(
1400
+ cfg.getProperty(rt2, "marginPx").asNumber());
1401
+ style.hover_boost = static_cast<float>(
1402
+ cfg.getProperty(rt2, "hoverBoost").asNumber());
1403
+ vroom_chart_set_footprints(chart_, prints.data(), prints.size(), &style);
1404
+ return jsi::Value::undefined();
1405
+ });
1406
+ }
1407
+
1408
+ if (name == "hitTestFootprint") {
1409
+ // hitTestFootprint(x, y) -> { side, candleTimeMs, x, y, radius, pane,
1410
+ // indices } | null. `indices` addresses the array last passed to
1411
+ // setFootprints and covers *both* sides of that candle, so one call fills a
1412
+ // tooltip; `pane` is the plot rect, for deciding which side of the badge that
1413
+ // tooltip fits on. No rendering.
1414
+ return jsi::Function::createFromHostFunction(
1415
+ rt,
1416
+ jsi::PropNameID::forAscii(rt, "hitTestFootprint"),
1417
+ 2,
1418
+ [this](jsi::Runtime& rt2,
1419
+ const jsi::Value& /*thisVal*/,
1420
+ const jsi::Value* args,
1421
+ size_t count) -> jsi::Value {
1422
+ if (count < 2) return jsi::Value::null();
1423
+ VroomFootprintHit hit{};
1424
+ if (!vroom_chart_hit_test_footprint(
1425
+ chart_, static_cast<float>(args[0].asNumber()),
1426
+ static_cast<float>(args[1].asNumber()), &hit)) {
1427
+ return jsi::Value::null();
1428
+ }
1429
+
1430
+ const int32_t n =
1431
+ vroom_chart_footprints_at(chart_, hit.candle_time_ms, nullptr, 0);
1432
+ std::vector<int32_t> idx(static_cast<size_t>(n > 0 ? n : 0));
1433
+ if (n > 0) {
1434
+ vroom_chart_footprints_at(chart_, hit.candle_time_ms, idx.data(), n);
1435
+ }
1436
+ jsi::Array indices(rt2, idx.size());
1437
+ for (size_t i = 0; i < idx.size(); ++i) {
1438
+ indices.setValueAtIndex(rt2, i, jsi::Value(idx[i]));
1439
+ }
1440
+
1441
+ jsi::Object pane(rt2);
1442
+ pane.setProperty(rt2, "left", hit.pane_left);
1443
+ pane.setProperty(rt2, "top", hit.pane_top);
1444
+ pane.setProperty(rt2, "right", hit.pane_right);
1445
+ pane.setProperty(rt2, "bottom", hit.pane_bottom);
1446
+
1447
+ jsi::Object obj(rt2);
1448
+ obj.setProperty(rt2, "side", hit.side);
1449
+ obj.setProperty(rt2, "candleTimeMs",
1450
+ static_cast<double>(hit.candle_time_ms));
1451
+ obj.setProperty(rt2, "x", hit.center_x);
1452
+ obj.setProperty(rt2, "y", hit.center_y);
1453
+ obj.setProperty(rt2, "radius", hit.radius);
1454
+ obj.setProperty(rt2, "pane", std::move(pane));
1455
+ obj.setProperty(rt2, "indices", std::move(indices));
1456
+ return obj;
1457
+ });
1458
+ }
1459
+
1460
+ if (name == "setFootprintHover") {
1461
+ // setFootprintHover(candleTimeMs, side) — highlight one badge; side -1
1462
+ // clears. Touch has no hover, so on RN this tracks the tapped badge.
1463
+ return jsi::Function::createFromHostFunction(
1464
+ rt,
1465
+ jsi::PropNameID::forAscii(rt, "setFootprintHover"),
1466
+ 2,
1467
+ [this](jsi::Runtime& /*rt2*/,
1468
+ const jsi::Value& /*thisVal*/,
1469
+ const jsi::Value* args,
1470
+ size_t count) -> jsi::Value {
1471
+ if (count < 2) return jsi::Value::undefined();
1472
+ vroom_chart_set_footprint_hover(
1473
+ chart_, static_cast<int64_t>(args[0].asNumber()),
1474
+ static_cast<int32_t>(args[1].asNumber()));
1475
+ return jsi::Value::undefined();
1476
+ });
1477
+ }
1478
+
1192
1479
  if (name == "render") {
1193
1480
  return jsi::Function::createFromHostFunction(
1194
1481
  rt,
@@ -69,6 +69,80 @@ typedef struct VroomBollinger {
69
69
  float fill_opacity; // 0..1, multiplied into upper_color's alpha
70
70
  } VroomBollinger;
71
71
 
72
+ // Ichimoku Kinko Hyo overlay drawn on the price pane: five lines plus the cloud
73
+ // (kumo) shaded between the two leading spans. No pane is reserved.
74
+ //
75
+ // Three of the lines are drawn away from the bar they were computed on:
76
+ // senkou A/B lead by `displacement` slots (so the cloud runs past the newest
77
+ // candle into empty time) and chikou lags by the same. Displacement is applied
78
+ // at draw time, so changing it re-renders without recomputing the series.
79
+ typedef struct VroomIchimoku {
80
+ int32_t enabled; // 0/1
81
+ int32_t tenkan_period; // conversion lookback (clamped >= 1; default 9)
82
+ int32_t kijun_period; // base lookback (clamped >= 1; default 26)
83
+ int32_t senkou_b_period; // span B lookback (clamped >= 1; default 52)
84
+ int32_t displacement; // slots the cloud leads / chikou lags (>= 0; default 26)
85
+ uint32_t tenkan_color; // 0xAARRGGBB
86
+ float tenkan_width; // stroke px
87
+ int32_t tenkan_enabled; // 0/1
88
+ uint32_t kijun_color;
89
+ float kijun_width;
90
+ int32_t kijun_enabled;
91
+ uint32_t senkou_a_color;
92
+ float senkou_a_width;
93
+ int32_t senkou_a_enabled;
94
+ uint32_t senkou_b_color;
95
+ float senkou_b_width;
96
+ int32_t senkou_b_enabled;
97
+ uint32_t chikou_color;
98
+ float chikou_width;
99
+ int32_t chikou_enabled;
100
+ int32_t cloud_enabled; // 0/1: shade between the leading spans
101
+ uint32_t bullish_cloud_color; // where senkou A is above senkou B
102
+ uint32_t bearish_cloud_color; // where senkou A is below senkou B
103
+ float cloud_opacity; // 0..1, multiplied into the cloud color's alpha
104
+ } VroomIchimoku;
105
+
106
+ // Fair Value Gap overlay drawn on the price pane: shaded boxes over three-candle
107
+ // imbalances, where candle i-1 and i+1's wicks fail to overlap and leave a band
108
+ // of price that was skipped. Bullish when candles[i-1].high < candles[i+1].low,
109
+ // bearish when candles[i-1].low > candles[i+1].high. No pane is reserved.
110
+ //
111
+ // Each box is anchored to the middle bar's open time and spans the untouched
112
+ // price range; it runs `box_length` slots right, or to the pane edge when
113
+ // `extend_boxes`. Only enabled, max_bars_back, wait_for_close and fill_type
114
+ // recompute — geometry and style are applied at draw time.
115
+ //
116
+ // With `show_inverse`, a filled gap draws a second box of the opposite polarity
117
+ // starting where the first one ends, lasting until price reclaims the band.
118
+ typedef struct VroomFairValueGaps {
119
+ int32_t enabled; // 0/1
120
+ int32_t max_bars_back; // bars to scan (clamped >= 0; default 300)
121
+ int32_t wait_for_close; // 0/1: withhold a gap until its 3rd bar closes
122
+ int32_t fill_type; // 0 = close through the far edge, 1 = wick
123
+ int32_t delete_after_fill; // 0/1: hide a gap once filled (else truncate it)
124
+ int32_t extend_boxes; // 0/1: run boxes to the pane edge
125
+ int32_t box_length; // box width in slots (clamped >= 1; default 20)
126
+ uint32_t bullish_color; // 0xAARRGGBB fill where the gap is bullish
127
+ uint32_t bearish_color; // fill where the gap is bearish
128
+ float opacity; // 0..1, multiplied into the fill color's alpha
129
+ int32_t border_enabled; // 0/1: stroke the box outline
130
+ int32_t border_style; // 0 = solid, 1 = dotted, 2 = dashed
131
+ float border_width; // stroke px
132
+ uint32_t bullish_border_color;
133
+ uint32_t bearish_border_color;
134
+ int32_t labels_enabled; // 0/1: draw `label` on each box
135
+ const char* label; // UTF-8, copied by the setter; NULL = "FVG"
136
+ int32_t label_distance; // slots of clearance, extend_boxes only (>= 0)
137
+ uint32_t label_color; // alpha 0 falls back to the box's border color
138
+ float label_font_size; // px; <= 0 falls back to the axis font size
139
+ int32_t show_inverse; // 0/1: keep drawing a filled gap, polarity flipped
140
+ uint32_t inverse_bullish_color; // fill where the inverted zone is bullish,
141
+ // i.e. where a bearish gap was violated
142
+ uint32_t inverse_bearish_color; // fill where a bullish gap was violated
143
+ const char* inverse_label; // UTF-8, copied by the setter; NULL = "iFVG"
144
+ } VroomFairValueGaps;
145
+
72
146
  // MACD, drawn in its own pane below the candles: the difference between a fast
73
147
  // and a slow moving average of `source`, a signal line smoothing that
74
148
  // difference, and a histogram of the gap between the two.
@@ -128,8 +202,29 @@ typedef struct VroomRSI {
128
202
  float ma_width;
129
203
  uint32_t band_color; // both dashed rules; 0 inherits the default gray
130
204
  int32_t bands_visible; // 0/1
205
+ // Shade where the line sits past a band, fading out at the rule and
206
+ // deepening toward the end of the scale. Takes its colors from
207
+ // VROOM_COLOR_ACCENT_BULL / _BEAR, and never reaches full opacity. 0/1.
208
+ int32_t extreme_fill;
131
209
  } VroomRSI;
132
210
 
211
+ // Average True Range, drawn in its own pane below the candles: a single line
212
+ // measuring volatility in price units. True Range is the widest of the bar's
213
+ // own high-low span and the two gaps from its extremes to the previous close,
214
+ // smoothed over `period` bars.
215
+ //
216
+ // The style fields carry the same inherit sentinel as VroomMACD: a fully
217
+ // transparent color falls back to the built-in default and a non-positive width
218
+ // falls back to 1.5.
219
+ typedef struct VroomATR {
220
+ int32_t enabled; // 0/1
221
+ int32_t period; // lookback in candles (clamped >= 1; default 14)
222
+ int32_t smoothing; // 0 = Wilder's RMA, 1 = SMA, 2 = EMA
223
+
224
+ uint32_t line_color; // 0 inherits the default teal
225
+ float line_width; // stroke px; <= 0 inherits 1.5
226
+ } VroomATR;
227
+
133
228
  // Session VWAP, drawn as a single line on the price pane. The session resets
134
229
  // each UTC day shifted by `reset_offset_min` minutes, and the line lifts its pen
135
230
  // at each reset. Color 0 inherits the default cyan; a non-positive width
@@ -257,6 +352,53 @@ typedef struct VroomPriceLineStyle {
257
352
  float hover_boost; // brightness multiplier for the hovered segment (1 = flat)
258
353
  } VroomPriceLineStyle;
259
354
 
355
+ // ---- Footprints (executed-trade badges) -----------------------------------
356
+
357
+ typedef enum {
358
+ VROOM_FOOTPRINT_BUY = 0, // position entry — "+" badge in the bull color
359
+ VROOM_FOOTPRINT_SELL = 1, // position exit — "-" badge in the bear color
360
+ } VroomFootprintSide;
361
+
362
+ // A single executed trade, drawn as a circular badge above the candle it fell in.
363
+ //
364
+ // `time_ms` is the raw execution time, *not* a bar-open time: the core buckets
365
+ // each footprint into whichever candle's window contains it, so one array renders
366
+ // correctly at every interval and re-groups by itself when the candles change.
367
+ // Order is irrelevant — the core sorts within each bucket.
368
+ //
369
+ // Only the fields the renderer needs live here; a host's per-trade payload (id,
370
+ // price, size) stays on the host side and is rejoined via the indices reported by
371
+ // vroom_chart_footprints_at.
372
+ typedef struct VroomFootprint {
373
+ int64_t time_ms;
374
+ int32_t side; // VroomFootprintSide
375
+ } VroomFootprint;
376
+
377
+ // Layout/style shared by every footprint badge.
378
+ typedef struct VroomFootprintStyle {
379
+ float radius_px; // badge radius; <= 0 falls back to 9
380
+ float gap_px; // vertical gap between the two stacked badges; < 0 => 4
381
+ float margin_px; // gap between the candle's high and the first badge; < 0 => 8
382
+ float hover_boost; // brightness multiplier for the hovered badge (1 = flat)
383
+ } VroomFootprintStyle;
384
+
385
+ // The badge under a pixel, as reported by vroom_chart_hit_test_footprint.
386
+ typedef struct VroomFootprintHit {
387
+ int64_t candle_time_ms; // bar-open time of the candle the badge sits on
388
+ int32_t side; // VroomFootprintSide — which of the candle's two badges
389
+ float center_x; // badge center, in the same px space as the hit test
390
+ float center_y;
391
+ float radius;
392
+ // The plot rect the badge was clipped to — same px space, axis strips
393
+ // excluded. Reported because a host placing its own tooltip has no other way
394
+ // to know it: the surface it measures includes the price and time axes, so
395
+ // sizing against that overstates the room next to a badge near the edge.
396
+ float pane_left;
397
+ float pane_top;
398
+ float pane_right;
399
+ float pane_bottom;
400
+ } VroomFootprintHit;
401
+
260
402
  // A continuous data coordinate at a pixel position (no candle snapping). Used to
261
403
  // translate a drawing-tool click into a data-space anchor.
262
404
  typedef struct VroomCoord {
@@ -428,10 +570,26 @@ void vroom_chart_preserve_price_envelope(VroomChart* chart,
428
570
  // to 1. No-op when nothing is visible.
429
571
  void vroom_chart_begin_interval_morph(VroomChart* chart, int32_t mode);
430
572
 
431
- // Advances the interval morph started by vroom_chart_begin_interval_morph. `t`
432
- // (clamped to 0..1) is the eased progress: 0 renders the captured geometry
433
- // pixel-identically to the pre-swap frame, 1 renders the new candles and
434
- // releases the capture. Driven per-frame by the host animation loop.
573
+ // Captures the same geometry for a live update to the series already shown —
574
+ // a tick to the in-progress bar. Always a transform, and unlike the interval
575
+ // morph it leaves the axes alone: the interval hasn't changed, so the ticks
576
+ // between the labels are still the right ones and must not be faded.
577
+ //
578
+ // Restarting one that is still running continues from the shape on screen
579
+ // rather than from the data under it, so ticks arriving faster than the
580
+ // animation lands stay smooth instead of snapping back each time.
581
+ //
582
+ // Call before set_candles, then drive vroom_chart_set_interval_morph from 0 to
583
+ // 1. No-op when nothing is visible. Not for an update that appends a bar: slots
584
+ // pair from the right edge, so a new bar would shift every candle onto its
585
+ // neighbour's geometry — advance the visible range instead and let the series
586
+ // translate.
587
+ void vroom_chart_begin_stream_morph(VroomChart* chart);
588
+
589
+ // Advances the morph started by either begin_*_morph above. `t` (clamped to
590
+ // 0..1) is the eased progress: 0 renders the captured geometry pixel-identically
591
+ // to the pre-swap frame, 1 renders the new candles and releases the capture.
592
+ // Driven per-frame by the host animation loop.
435
593
  void vroom_chart_set_interval_morph(VroomChart* chart, float t);
436
594
 
437
595
  void vroom_chart_pan(VroomChart* chart, float dx_px, float dy_px);
@@ -534,6 +692,11 @@ void vroom_chart_set_rsi(VroomChart* chart, const VroomRSI* cfg);
534
692
  // re-render.
535
693
  void vroom_chart_set_macd(VroomChart* chart, const VroomMACD* cfg);
536
694
 
695
+ // Configures the ATR indicator (its own pane below the candles, stacking in
696
+ // enable order like RSI and MACD). Period and smoothing changes recompute the
697
+ // series; color and width changes only re-render.
698
+ void vroom_chart_set_atr(VroomChart* chart, const VroomATR* cfg);
699
+
537
700
  // Replaces the full set of moving-average overlay lines (SMA/EMA) drawn on the
538
701
  // price pane. Pass count 0 to clear them. Overlays don't reserve a pane.
539
702
  void vroom_chart_set_overlays(VroomChart* chart, const VroomOverlay* overlays,
@@ -549,6 +712,23 @@ void vroom_chart_set_vwap(VroomChart* chart, const VroomVWAP* cfg);
549
712
  // changes only re-render; enabled/period/mult/source/basis changes recompute.
550
713
  void vroom_chart_set_bollinger(VroomChart* chart, const VroomBollinger* cfg);
551
714
 
715
+ // Configures the Ichimoku overlay (five price-pane lines + the cloud between
716
+ // the leading spans; no pane is reserved). Only enabled and the three periods
717
+ // recompute; style, visibility and displacement changes just re-render.
718
+ //
719
+ // Enabling it also pulls the view forward so the leading spans, which sit past
720
+ // the newest candle, are on screen — the same future gap the default framing
721
+ // reserves when the indicator is already on at set_candles time.
722
+ void vroom_chart_set_ichimoku(VroomChart* chart, const VroomIchimoku* cfg);
723
+
724
+ // Configures the Fair Value Gap overlay (shaded imbalance boxes on the price
725
+ // pane; no pane is reserved). Only enabled, max_bars_back, wait_for_close and
726
+ // fill_type recompute; box geometry, style and labels just re-render.
727
+ //
728
+ // `cfg->label` is copied, so the caller may free it as soon as this returns.
729
+ void vroom_chart_set_fair_value_gaps(VroomChart* chart,
730
+ const VroomFairValueGaps* cfg);
731
+
552
732
  // Configures the volume bars. Render-only — the bars come straight off each
553
733
  // candle's volume, so nothing is recomputed. Bars are enabled by default; pass
554
734
  // a config with `enabled` 0 to hide them.
@@ -689,6 +869,43 @@ void vroom_chart_set_price_line_hover(VroomChart* chart, int32_t index,
689
869
  void vroom_chart_set_price_line_drag(VroomChart* chart, int32_t index,
690
870
  double price);
691
871
 
872
+ // Replaces the full set of footprints and their shared style. Pass count 0 to
873
+ // clear; `style` may be null when count is 0.
874
+ //
875
+ // Footprints are grouped onto candles by timestamp, so at most two badges render
876
+ // per candle: one for that bar's buys and one for its sells, however many trades
877
+ // went into each. When a candle has both, they stack upward with `gap_px` between
878
+ // them, the side whose latest trade came first sitting nearest the bar. Badges sit
879
+ // above the candle's high, clipped to the price pane, and re-group on their own
880
+ // whenever the candles change (a new interval, more history) — the host never
881
+ // re-buckets anything.
882
+ void vroom_chart_set_footprints(VroomChart* chart, const VroomFootprint* prints,
883
+ size_t count,
884
+ const VroomFootprintStyle* style);
885
+
886
+ // Hit-tests pixel (x_px, y_px) against the footprint badges. On a hit fills *out
887
+ // with the badge's candle, side and pixel geometry and returns true; returns false
888
+ // on a miss (out untouched). `out` may be null to test without reading the hit.
889
+ // When two badges overlap the nearest center wins.
890
+ bool vroom_chart_hit_test_footprint(VroomChart* chart, float x_px, float y_px,
891
+ VroomFootprintHit* out);
892
+
893
+ // Fills `out_indices` with the indices — into the array last passed to
894
+ // vroom_chart_set_footprints — of every footprint bucketed into the candle opening
895
+ // at `candle_time_ms`, *both* sides, ascending by time. Returns the total count,
896
+ // which may exceed `max` (only the first `max` are written), or 0 when that candle
897
+ // has no footprints. Pass a null `out_indices` with max 0 to size the buffer first.
898
+ //
899
+ // This is how a host rejoins a hit badge to its own trade objects: hit-test to get
900
+ // the candle, then map these indices back through the array it supplied.
901
+ int32_t vroom_chart_footprints_at(VroomChart* chart, int64_t candle_time_ms,
902
+ int32_t* out_indices, int32_t max);
903
+
904
+ // Marks one footprint badge as hovered so it renders highlighted. `side` -1
905
+ // clears the hover; `candle_time_ms` and `side` match vroom_chart_hit_test_footprint.
906
+ void vroom_chart_set_footprint_hover(VroomChart* chart, int64_t candle_time_ms,
907
+ int32_t side);
908
+
692
909
  // Sets the transient in-progress "draft" the drawing tool shows while the user
693
910
  // places points. Node A is always shown; when `has_b`, node B is shown too.
694
911
  // `guide != 0` also draws the live preview (a guideline for a line, or a preview