Files
egutierrez a99aa661a2 fix(cpp/viz,core): bell icon TI_BELL, candlestick Setup-inside-BeginPlot, pie legend, kpi sparkline a la derecha
- toast.cpp: TI_BELL en lugar de \xf0\x9f\x94\x94 (fuera del rango cargado por icon_font, renderizaba como ?)
- candlestick.cpp: SetupAxes/SetupAxisScale/SetupAxisLimits movidos dentro de BeginPlot/EndPlot — antes el plot desaparecia al entrar
- pie_chart.cpp: SetupLegend(East, Outside, NoButtons), eliminado NoLegend
- kpi_card.cpp: layout 2 cols con sparkline a la derecha centrado verticalmente
2026-04-28 23:38:51 +02:00

85 lines
3.4 KiB
C++

#include "viz/candlestick.h"
#include "imgui.h"
#include "implot.h"
void candlestick(const char* title, const double* dates, const double* opens,
const double* closes, const double* lows, const double* highs,
int count, float width_percent, bool tooltip) {
if (count <= 0) return;
// Compute half-width of each candle body in data coordinates.
// Use spacing between consecutive dates when count > 1, else fallback to 0.5.
double spacing = (count > 1) ? (dates[1] - dates[0]) : 1.0;
double half_w = spacing * (double)width_percent * 0.5;
if (ImPlot::BeginPlot(title, ImVec2(-1, 0))) {
// ImPlot Setup* calls DEBEN ir dentro de BeginPlot/EndPlot. Antes
// estaban fuera y dejaban el plot sin ejes, lo que hacia que el demo
// de candlestick "desapareciera" al entrar en la galeria.
ImPlot::SetupAxes("Date", "Price",
ImPlotAxisFlags_None,
ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Time);
ImPlot::SetupAxisLimits(ImAxis_X1,
dates[0] - spacing,
dates[count - 1] + spacing,
ImGuiCond_Always);
ImDrawList* draw = ImPlot::GetPlotDrawList();
const ImU32 col_bull = IM_COL32(0, 200, 80, 255); // green — close >= open
const ImU32 col_bear = IM_COL32(220, 50, 50, 255); // red — close < open
int hovered_idx = -1;
for (int i = 0; i < count; i++) {
double x = dates[i];
double open = opens[i];
double close = closes[i];
double low = lows[i];
double high = highs[i];
bool bullish = (close >= open);
ImU32 col = bullish ? col_bull : col_bear;
// Convert data coordinates to screen pixels.
ImVec2 body_tl = ImPlot::PlotToPixels(x - half_w, bullish ? close : open);
ImVec2 body_br = ImPlot::PlotToPixels(x + half_w, bullish ? open : close);
ImVec2 wick_hi = ImPlot::PlotToPixels(x, high);
ImVec2 wick_lo = ImPlot::PlotToPixels(x, low);
float cx = (body_tl.x + body_br.x) * 0.5f;
// Wick (high-low vertical line).
draw->AddLine(ImVec2(cx, wick_hi.y), ImVec2(cx, wick_lo.y), col, 1.5f);
// Body rectangle (open-close).
// Ensure at least 1px height so flat candles are visible.
if (body_br.y <= body_tl.y + 1.0f) body_br.y = body_tl.y + 1.0f;
draw->AddRectFilled(body_tl, body_br, col);
draw->AddRect(body_tl, body_br, col);
// Track hovered candle for tooltip.
if (tooltip && ImPlot::IsPlotHovered()) {
ImVec2 mouse = ImGui::GetMousePos();
if (mouse.x >= body_tl.x - 4 && mouse.x <= body_br.x + 4 &&
mouse.y >= wick_hi.y - 4 && mouse.y <= wick_lo.y + 4) {
hovered_idx = i;
}
}
}
// Tooltip for the hovered candle.
if (tooltip && hovered_idx >= 0) {
int i = hovered_idx;
ImGui::BeginTooltip();
ImGui::Text("O: %.4f", opens[i]);
ImGui::Text("H: %.4f", highs[i]);
ImGui::Text("L: %.4f", lows[i]);
ImGui::Text("C: %.4f", closes[i]);
ImGui::EndTooltip();
}
ImPlot::EndPlot();
}
}