Blog/Desarrollo

¿Cómo funciona un gráfico del precio del oro en vivo?

Aprende cómo un gráfico del oro combina cotizaciones spot, barras OHLC, marcas UTC y polling controlado, con un ejemplo funcional en TypeScript y SVG.

Desarrollo

Un gráfico del precio del oro en vivo se construye con dos conjuntos de datos relacionados: una cotización spot actual para la cifra principal y barras OHLC históricas para la serie dibujada. El cliente carga una ventana de barras, las ordena por tiempo, representa sus valores y después actualiza solo la observación más reciente. También debe indicar si la última barra sigue en formación.

La distinción importa. La cotización spot puede cambiar entre dos barras, mientras que el cierre de una barra abierta es provisional. Si ambas se tratan como el mismo dato, aparecen puntos duplicados, horas engañosas o una línea que parece más reciente que sus datos.

Las cuatro piezas de un gráfico en vivo

La mayoría de gráficos necesita:

  • un símbolo y un intervalo, como XAU-USD-SPOT y 1d
  • un rango inicial de barras OHLC
  • una cotización spot para la cifra actual
  • un bucle que sustituya la última barra en vez de añadir cada respuesta

El dibujo es el último paso. Antes hay que validar números, ordenar observaciones, conservar horas y decidir cómo mostrar una barra aún abierta.

Spot y OHLC responden preguntas distintas

El endpoint spot responde «¿cuál es el precio derivado actual?». Una barra diaria contiene apertura, máximo, mínimo y cierre para ese día UTC.

GET https://api.goldprice.dev/v1/spot/XAU-USD-SPOT
GET https://api.goldprice.dev/v1/bars?symbol=XAU-USD-SPOT&interval=1d&from=2026-07-25&to=2026-08-23

La respuesta spot incluye price, computed_at e is_stale. Úsalos para el precio grande y su estado de frescura.

Cada barra incluye bar_start, open, high, low, close, volume e is_closed. En spot, volume puede ser null, por lo que no debe ser obligatorio para dibujar. is_closed: false señala una barra en formación cuyos valores todavía pueden cambiar.

Las barras llegan de más reciente a más antigua. Ordénalas en sentido ascendente antes de dibujar el tiempo de izquierda a derecha.

Carga y normaliza los datos

Conserva los precios decimales como cadenas en el modelo y conviértelos a números solo cuando el cálculo de coordenadas lo exija.

type GoldBar = {
  bar_start: string;
  open: string | null;
  high: string | null;
  low: string | null;
  close: string | null;
  volume: string | null;
  is_closed: boolean;
};

type BarsResponse = {
  bars: GoldBar[];
  next_cursor: string | null;
};

type ChartPoint = {
  time: string;
  close: number;
  isClosed: boolean;
};

async function loadPoints(from: string, to: string): Promise<ChartPoint[]> {
  const url = new URL("https://api.goldprice.dev/v1/bars");
  url.search = new URLSearchParams({
    symbol: "XAU-USD-SPOT", interval: "1d", from, to,
  }).toString();

  const response = await fetch(url);
  if (!response.ok) throw new Error(`Gold bars failed: ${response.status}`);
  const page = (await response.json()) as BarsResponse;

  return page.bars
    .filter((bar) => bar.close !== null && Number.isFinite(Number(bar.close)))
    .map((bar) => ({ time: bar.bar_start, close: Number(bar.close), isClosed: bar.is_closed }))
    .sort((a, b) => Date.parse(a.time) - Date.parse(b.time));
}

Una página basta para un gráfico diario reciente. Para un periodo mayor, continúa con next_cursor hasta reunir suficientes observaciones o recibir null.

Dibuja la línea con SVG

Este cálculo asigna el tiempo al eje horizontal y el cierre al vertical. El resultado se usa como atributo d de un <path>.

function linePath(points: ChartPoint[], width: number, height: number): string {
  if (points.length === 0) return "";
  const values = points.map((point) => point.close);
  const min = Math.min(...values);
  const span = Math.max(...values) - min || 1;

  return points.map((point, index) => {
    const x = points.length === 1 ? width / 2 : (index / (points.length - 1)) * width;
    const y = height - ((point.close - min) / span) * height;
    return `${index === 0 ? "M" : "L"} ${x.toFixed(2)} ${y.toFixed(2)}`;
  }).join(" ");
}

Añade role="img" y un aria-label claro al SVG. Como los puntos no dependen del renderer, el mismo modelo funciona con Canvas, una vista móvil o una librería de gráficos.

<svg viewBox="0 0 720 280" role="img" aria-label="Gold price in US dollars">
  <path
    d={linePath(points, 720, 280)}
    fill="none"
    stroke="currentColor"
    strokeWidth="2"
  />
</svg>

Sustituye la última barra por su hora

Las barras OHLC se consultan por REST. Haz polling de /v1/bars/latest para obtener la barra más reciente, cerrada o en formación. Si bar_start ya existe, reemplaza esa entrada. Añade una nueva solo cuando empiece otro intervalo.

async function refreshLatest(current: ChartPoint[]): Promise<ChartPoint[]> {
  const response = await fetch(
    "https://api.goldprice.dev/v1/bars/latest?symbol=XAU-USD-SPOT&interval=1d",
    { cache: "no-store" },
  );
  if (!response.ok) throw new Error(`Latest bar failed: ${response.status}`);

  const { bar } = await response.json() as { bar: GoldBar | null };
  if (!bar || bar.close === null || !Number.isFinite(Number(bar.close))) return current;

  const next = { time: bar.bar_start, close: Number(bar.close), isClosed: bar.is_closed };
  const index = current.findIndex((point) => point.time === next.time);
  if (index === -1) return [...current, next].sort((a, b) => Date.parse(a.time) - Date.parse(b.time));

  const copy = current.slice();
  copy[index] = next;
  return copy;
}

Usa un temporizador controlado, páralo cuando la página esté oculta y evita solicitudes simultáneas. Consultar con más frecuencia no hace más reciente una observación de origen.

Separa el precio principal de la línea

El precio grande y la línea pueden tener ritmos distintos. Consulta /v1/spot/XAU-USD-SPOT por separado, muestra su computed_at y etiqueta la cotización si is_stale es true. La hora de llegada al navegador no sustituye la hora de observación.

type SpotQuote = {
  symbol: string;
  price: string;
  computed_at: string;
  is_stale: boolean;
};

async function loadSpot(): Promise<SpotQuote> {
  const response = await fetch(
    "https://api.goldprice.dev/v1/spot/XAU-USD-SPOT",
    { cache: "no-store" },
  );
  if (!response.ok) throw new Error(`Gold spot failed: ${response.status}`);
  return response.json() as Promise<SpotQuote>;
}

Cuando el mercado está cerrado

Un gráfico correcto puede quedarse quieto. Conserva la última cotización válida con su computed_at original y la última barra con su bar_start e is_closed reales. Si la interfaz necesita mostrar periodos de calendario vacíos, usa espacio o huecos; no copies el último precio como si fuera una observación nueva.

Tras un fallo de red, muestra el último dato válido con su hora o un error si nunca hubo una carga correcta. No guardes un error HTTP como precio cero.

Comprobaciones antes de publicar

  • usa el símbolo canónico XAU-USD-SPOT
  • ordena las barras de antigua a reciente
  • reemplaza cuando coincida bar_start y añade solo un intervalo nuevo
  • identifica is_closed: false como provisional
  • acepta volume: null
  • conserva computed_at e is_stale del spot
  • evita actualizaciones superpuestas y espera tras errores HTTP
  • mantén las claves API en el servidor para llamadas autenticadas

Un gráfico es fiable cuando cada punto se puede relacionar con su hora y estado. Consulta la guía de barras históricas para parámetros y paginación, y la guía de WebSocket para distinguir ticks en vivo de barras OHLC.

guías relacionadas

Desarrollo

Cómo backtestear oro en moneda local sin sesgo de anticipación (look-ahead) de FX

Combina barras diarias liquidadas de XAU/USD con observaciones históricas de FX para probar una estrategia de oro en moneda local sin usar accidentalmente tasas que no estaban disponibles en ese momento.

Leer →
Desarrollo

Agrega un widget de precio del oro en vivo a WordPress

Agrega un widget gratuito y configurable de precio del oro en vivo a WordPress con un solo iframe. Funciona en Gutenberg y Elementor, sin clave de API, sin plugin.

Leer →
Desarrollo

Construye una calculadora de precio de joyería de oro con JavaScript

Construye una calculadora de precio de joyería de oro a partir de precios en vivo por gramo y quilate, con aritmética decimal exacta, caché, márgenes y límites de valoración claros.

Leer →

goldprice.dev

Precios del oro en tiempo real, OHLC histórico y agregación multi-fuente — disponible via REST y SSE.