1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticas chartsvgzero-dependencycandlestickohlcfinancetrading

Candlestick Chart

Gráfico de velas OHLC con velas alcistas huecas y bajistas rellenas para no depender del color.

Vista Previa en Vivo

ACME / USD

Daily candles

$196.19 +7.80%
  • Up (hollow)
  • Down (filled)
$175.00 $180.00 $185.00 $190.00 $195.00 $200.00 Oct 6 Oct 10 Oct 14 Oct 18 Oct 22 Oct 26
View data
DateOpenHighLowClose
Oct 6$182.00$183.00$178.60$182.60
Oct 7$182.60$184.45$180.16$183.09
Oct 8$183.09$187.52$181.50$185.31
Oct 9$185.31$189.72$184.39$186.84
Oct 10$186.84$190.13$184.13$184.64
Oct 11$184.64$188.04$182.49$182.89
Oct 12$182.89$189.90$182.28$186.71
Oct 13$186.71$193.95$185.60$191.26
Oct 14$191.26$193.21$187.23$189.08
Oct 15$189.08$190.13$181.66$184.41
Oct 16$184.41$187.36$181.33$186.64
Oct 17$186.64$194.82$184.49$193.17
Oct 18$193.17$196.11$191.83$193.65
Oct 19$193.65$196.70$187.75$188.50
Oct 20$188.50$191.86$187.36$187.80
Oct 21$187.80$196.52$187.36$193.16
Oct 22$193.16$199.02$192.42$195.96
Oct 23$195.96$198.43$191.97$193.30
Oct 24$193.30$194.97$189.47$191.60
Oct 25$191.60$194.60$188.54$193.86
Oct 26$193.86$197.04$191.09$196.01
Oct 27$196.01$198.13$194.15$196.19

Uso

Copia y pega este código directamente en tu página de Astro:

astro
---
import { CandlestickChart } from 'astro-component-kit/charts';
---

<CandlestickChart
  title="ACME / USD"
  candles={[
    { label: 'Oct 1', open: 182, high: 186, low: 180, close: 185 },
    { label: 'Oct 2', open: 185, high: 187, low: 181, close: 182 },
  ]}
/>
--- import { CandlestickChart } from 'astro-component-kit/charts'; --- <CandlestickChart title="ACME / USD" candles={[ { label: 'Oct 1', open: 182, high: 186, low: 180, close: 185 }, { label: 'Oct 2', open: 185, high: 187, low: 181, close: 182 }, ]} />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/lib/CandlestickChart.astro:

astro
---
/**
 * CandlestickChart — OHLC price chart. Bodies span open→close, wicks span
 * low→high. Up candles are hollow, down candles filled, so direction is
 * readable without color.
 *
 * @param {Array<{label: string, open: number, high: number, low: number, close: number}>} candles - OHLC data.
 * @param {string} prefix - Price prefix.
 * @param {number} height - SVG height in viewBox units (width is 640).
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
import ChartFrame from './ChartFrame.astro';
import { scaleLinear, niceTicks, POSITIVE, NEGATIVE } from './chart-utils';

interface Candle { label: string; open: number; high: number; low: number; close: number }
interface Props {
  title?: string;
  subtitle?: string;
  candles?: Candle[];
  prefix?: string;
  height?: number;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

const demo: Candle[] = (() => {
  let p = 182;
  return Array.from({ length: 22 }, (_, i) => {
    const r1 = Math.sin(i * 4.7) * 0.5 + Math.sin(i * 1.3) * 0.5;
    const r2 = Math.abs(Math.sin(i * 9.1));
    const open = p;
    const close = Math.round((p + r1 * 6 + 0.6) * 100) / 100;
    const high = Math.round((Math.max(open, close) + r2 * 3 + 0.4) * 100) / 100;
    const low = Math.round((Math.min(open, close) - (1 - r2) * 3 - 0.4) * 100) / 100;
    p = close;
    return { label: `Oct ${i + 6}`, open, high, low, close };
  });
})();

const {
  title = 'ACME / USD',
  subtitle = 'Daily candles',
  candles = demo,
  prefix = '$',
  height = 300,
  variant = 'glass',
  size = 'md',
  className = '',
} = Astro.props;

const W = 640;
const H = height;
const m = { t: 12, r: 52, b: 28, l: 12 };
const ticks = niceTicks(Math.min(...candles.map((c) => c.low)), Math.max(...candles.map((c) => c.high)), 5);
const y = scaleLinear(ticks[0], ticks[ticks.length - 1], H - m.b, m.t);
const band = (W - m.l - m.r) / candles.length;
const bw = Math.max(3, band * 0.6);
const f = (v: number) => `${prefix}${v.toFixed(2)}`;
const last = candles[candles.length - 1];
const first = candles[0];
const change = ((last.close - first.open) / first.open) * 100;
const labelEvery = Math.ceil(candles.length / 6);
---

<ChartFrame
  title={title}
  subtitle={subtitle}
  value={f(last.close)}
  delta={`${change >= 0 ? '+' : ''}${change.toFixed(2)}%`}
  deltaPositive={change >= 0}
  variant={variant}
  size={size}
  className={className}
  legend={[{ label: 'Up (hollow)', color: POSITIVE }, { label: 'Down (filled)', color: NEGATIVE }]}
  table={{ headers: ['Date', 'Open', 'High', 'Low', 'Close'], rows: candles.map((c) => [c.label, f(c.open), f(c.high), f(c.low), f(c.close)]) }}
>
  <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: candlestick chart, ${candles.length} periods, ${change.toFixed(1)}% change`}>
    {ticks.map((t) => (
      <g>
        <line class="cf-grid" x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} />
        <text class="cf-label" x={W - m.r + 8} y={y(t) + 4}>{f(t)}</text>
      </g>
    ))}
    {candles.map((c, i) => {
      const cx = m.l + i * band + band / 2;
      const up = c.close >= c.open;
      const col = up ? POSITIVE : NEGATIVE;
      const top = y(Math.max(c.open, c.close));
      const h = Math.max(1.5, Math.abs(y(c.open) - y(c.close)));
      return (
        <g class="cf-mark cf-dim" tabindex="0" role="img"
          aria-label={`${c.label}: open ${f(c.open)}, high ${f(c.high)}, low ${f(c.low)}, close ${f(c.close)}`}
          data-tip-title={c.label} data-tip={`${col}::${up ? 'Up' : 'Down'} ${(((c.close - c.open) / c.open) * 100).toFixed(2)}%|O ${f(c.open)} · H ${f(c.high)}|L ${f(c.low)} · C ${f(c.close)}`}>
          <rect x={cx - band / 2} y={m.t} width={band} height={H - m.t - m.b} fill="transparent" />
          <line x1={cx} x2={cx} y1={y(c.high)} y2={y(c.low)} stroke={col} stroke-width="1.5" />
          <rect x={cx - bw / 2} y={top} width={bw} height={h} rx="1.5" fill={up ? 'var(--chart-surface, #0d0d0f)' : col} stroke={col} stroke-width="1.5" />
          {i % labelEvery === 0 && <text class="cf-label" x={cx} y={H - 8} text-anchor="middle">{c.label}</text>}
        </g>
      );
    })}
    <line x1={m.l} x2={W - m.r} y1={y(last.close)} y2={y(last.close)} stroke="var(--c-text-1, #fff)" stroke-opacity="0.3" stroke-dasharray="3 4" aria-hidden="true" />
  </svg>
</ChartFrame>
--- /** * CandlestickChart — OHLC price chart. Bodies span open→close, wicks span * low→high. Up candles are hollow, down candles filled, so direction is * readable without color. * * @param {Array<{label: string, open: number, high: number, low: number, close: number}>} candles - OHLC data. * @param {string} prefix - Price prefix. * @param {number} height - SVG height in viewBox units (width is 640). * @param {string} variant - Card style (glass|solid|ghost). * @param {string} size - Density (sm|md|lg). */ import ChartFrame from './ChartFrame.astro'; import { scaleLinear, niceTicks, POSITIVE, NEGATIVE } from './chart-utils'; interface Candle { label: string; open: number; high: number; low: number; close: number } interface Props { title?: string; subtitle?: string; candles?: Candle[]; prefix?: string; height?: number; variant?: 'glass' | 'solid' | 'ghost'; size?: 'sm' | 'md' | 'lg'; className?: string; } const demo: Candle[] = (() => { let p = 182; return Array.from({ length: 22 }, (_, i) => { const r1 = Math.sin(i * 4.7) * 0.5 + Math.sin(i * 1.3) * 0.5; const r2 = Math.abs(Math.sin(i * 9.1)); const open = p; const close = Math.round((p + r1 * 6 + 0.6) * 100) / 100; const high = Math.round((Math.max(open, close) + r2 * 3 + 0.4) * 100) / 100; const low = Math.round((Math.min(open, close) - (1 - r2) * 3 - 0.4) * 100) / 100; p = close; return { label: `Oct ${i + 6}`, open, high, low, close }; }); })(); const { title = 'ACME / USD', subtitle = 'Daily candles', candles = demo, prefix = '$', height = 300, variant = 'glass', size = 'md', className = '', } = Astro.props; const W = 640; const H = height; const m = { t: 12, r: 52, b: 28, l: 12 }; const ticks = niceTicks(Math.min(...candles.map((c) => c.low)), Math.max(...candles.map((c) => c.high)), 5); const y = scaleLinear(ticks[0], ticks[ticks.length - 1], H - m.b, m.t); const band = (W - m.l - m.r) / candles.length; const bw = Math.max(3, band * 0.6); const f = (v: number) => `${prefix}${v.toFixed(2)}`; const last = candles[candles.length - 1]; const first = candles[0]; const change = ((last.close - first.open) / first.open) * 100; const labelEvery = Math.ceil(candles.length / 6); --- <ChartFrame title={title} subtitle={subtitle} value={f(last.close)} delta={`${change >= 0 ? '+' : ''}${change.toFixed(2)}%`} deltaPositive={change >= 0} variant={variant} size={size} className={className} legend={[{ label: 'Up (hollow)', color: POSITIVE }, { label: 'Down (filled)', color: NEGATIVE }]} table={{ headers: ['Date', 'Open', 'High', 'Low', 'Close'], rows: candles.map((c) => [c.label, f(c.open), f(c.high), f(c.low), f(c.close)]) }} > <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: candlestick chart, ${candles.length} periods, ${change.toFixed(1)}% change`}> {ticks.map((t) => ( <g> <line class="cf-grid" x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} /> <text class="cf-label" x={W - m.r + 8} y={y(t) + 4}>{f(t)}</text> </g> ))} {candles.map((c, i) => { const cx = m.l + i * band + band / 2; const up = c.close >= c.open; const col = up ? POSITIVE : NEGATIVE; const top = y(Math.max(c.open, c.close)); const h = Math.max(1.5, Math.abs(y(c.open) - y(c.close))); return ( <g class="cf-mark cf-dim" tabindex="0" role="img" aria-label={`${c.label}: open ${f(c.open)}, high ${f(c.high)}, low ${f(c.low)}, close ${f(c.close)}`} data-tip-title={c.label} data-tip={`${col}::${up ? 'Up' : 'Down'} ${(((c.close - c.open) / c.open) * 100).toFixed(2)}%|O ${f(c.open)} · H ${f(c.high)}|L ${f(c.low)} · C ${f(c.close)}`}> <rect x={cx - band / 2} y={m.t} width={band} height={H - m.t - m.b} fill="transparent" /> <line x1={cx} x2={cx} y1={y(c.high)} y2={y(c.low)} stroke={col} stroke-width="1.5" /> <rect x={cx - bw / 2} y={top} width={bw} height={h} rx="1.5" fill={up ? 'var(--chart-surface, #0d0d0f)' : col} stroke={col} stroke-width="1.5" /> {i % labelEvery === 0 && <text class="cf-label" x={cx} y={H - 8} text-anchor="middle">{c.label}</text>} </g> ); })} <line x1={m.l} x2={W - m.r} y1={y(last.close)} y2={y(last.close)} stroke="var(--c-text-1, #fff)" stroke-opacity="0.3" stroke-dasharray="3 4" aria-hidden="true" /> </svg> </ChartFrame>

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
CandlestickChart.astro
Dependencias
apexcharts (peer)
Etiquetas
chartsvgzero-dependencycandlestickohlcfinancetrading