PáginasDatos OHLCV y de barras · 10 / 29
Datos OHLCV y de barras
Los backtests de QuantCraft necesitan velas OHLCV (apertura, máximo, mínimo, cierre, volumen) de cada símbolo que operas. El motor carga esos datos antes de que se ejecute tu estrategia y luego dispara los callbacks del ciclo de vida de tu estrategia con una vista de la barra actual (bar) en cada paso. Al final de la ejecución puedes trabajar opcionalmente con la serie completa que se pasa a on_finish(bars, …). Configura las cargas desde Test (Ejecutar código).
Esta página cubre de dónde proceden los datos (cargadores), cómo funcionan los rangos de fechas y el calentamiento, y cómo usar el argumento bars de serie completa en on_finish. Combínala con Datos fundamentales cuando mezcles precios y presentaciones; consulta Modelo de cuenta para ver cómo se relacionan las operaciones con los precios.
Cargadores (fuentes de datos)
En la pestaña Symbol & data de Run backtest eliges un feed de precios. El motor usa por debajo uno de dos cargadores:
Feed del bróker conectado
- Carga velas desde tu cuenta de bróker conectada usando tu clave API y tu secreto, o una conexión vinculada por OAuth con un id de enlace válido.
- Admite acciones de EE. UU. y criptomonedas (eliges el tipo de activo en el cuadro).
- Admite muchos marcos temporales: diario, semanal, horario, 4 horas y varias barras de minutos intradía (alineados con las etiquetas de marco temporal del gráfico de la aplicación).
- El historial se obtiene por tramos hasta cubrir el rango solicitado (o hasta alcanzar un número mínimo de barras cuando usas el modo heredado "sin fechas explícitas").
Usa el feed del bróker conectado cuando quieras datos del bróker conectado o el conjunto completo de marcos intradía (de 1 minuto a 4 horas, más diario y semanal).
Almacenamiento de QuantCraft (alojado)
- Carga velas desde el almacenamiento alojado de QuantCraft cuando has iniciado sesión (la aplicación envía tu token de sesión).
- Los archivos se organizan por símbolo y marco temporal. En un plan gratuito el cuadro de backtest ofrece diario, semanal y mensual; Premium desbloquea el intradía y marcos ampliados.
- Se obtiene el archivo completo y, opcionalmente, se filtra al rango de fechas calendario elegido.
- Las barras diarias llevan una
tsolo con fecha, como"2024-01-02", que se lee como UTC.
Códigos de marco temporal de QuantCraft B2: 1s, 1m, 2m, m5, 15m, 30m, h1, 2h, 4h, d, w, m, q, y. Ojo: m significa mensual, no minutos: 1 minuto es 1m, 5 minutos es m5.
Si un rango de fechas no tiene barras, la carga falla con un mensaje como No AAPL bars (d) between 2010-01-01 and 2010-02-01. Available range is … to … — adjust the backtest dates.
Usa el almacenamiento de QuantCraft cuando hayas iniciado sesión sin credenciales de bróker, o cuando quieras paquetes OHLCV alojados del catálogo de símbolos de QuantCraft (5 minutos, horario, diario, semanal o mensual).
Elegir entre ambos
| Necesidad | Elección habitual |
|---|---|
| Conjunto intradía completo del bróker conectado (1m, 5m, 15m, 30m, 1H, 4H, …) | Feed del bróker conectado |
| 5 minutos u horario del catálogo de QuantCraft | Almacenamiento de QuantCraft |
| Diario / semanal / mensual de archivos alojados | Almacenamiento de QuantCraft |
| Cuenta de bróker conectada + claves | Feed del bróker conectado |
| Acceso con sesión iniciada sin credenciales de bróker | Almacenamiento de QuantCraft |
En los callbacks de estrategia, lee los precios del objeto
barque te pasa el motor, no de un singleton OHLCV global. En el IDE, elqc_ohlcva nivel de módulo suele estar sin definir cuando se carga tu archivo; depender de él puede romper tu código. También esNoneen una ejecución con objetivo de screener, y donde sí está definido abarca el rango completo cargado, calentamiento incluido, no la ventana ejecutada que dibuja el gráfico, por lo que es la fuente equivocada para series de gráfico. La vistabarsiempre es correcta para el paso de simulación actual.
Rangos de tiempo
Controlas qué barras participan en el backtest con la fecha de inicio, la fecha de fin y, opcionalmente, el calentamiento.
Rango calendario (start_date + end_date)
Cuando estableces ambas fechas en la pestaña Simulation (formato YYYY-MM-DD, interpretadas como días naturales UTC):
- El motor conserva solo las barras que caen en esa ventana.
- El rango se trata como un intervalo semiabierto: desde el inicio del día de comienzo hasta el final del día de fin según la convención habitual del producto (las barras del día natural final se incluyen según lo configurado por el motor).
Feed del bróker conectado: se solicitan velas para ese tramo calendario.
Almacenamiento de QuantCraft: se descarga la serie completa del archivo y luego se filtra a la misma ventana de días naturales.
Si omites ambas fechas:
- El feed del bróker conectado usa un recorrido hacia atrás heredado: el motor retrocede en el tiempo hasta cargar suficiente historial (sujeto a los límites del marco temporal), en lugar de una ventana calendario escrita por ti.
- El almacenamiento de QuantCraft usa todas las barras del archivo (sin filtro adicional).
Barras de calentamiento (warmup_bars)
Entero no negativo opcional: barras extra cargadas antes de tu fecha de inicio para que los indicadores puedan mirar atrás sin quedarse sin historial.
- Las barras de calentamiento son visibles mediante
bar.close[1],bar.close[2], … ybar.bars_backen la primera barra de ejecución. on_bar/on_tick/on_timersiguen ejecutándose solo dentro de tu ventana de ejecución seleccionada: el calentamiento es solo para historial, no añade "días de trading" simulados extra al resultado.
bar_index frente al calendario
bar_indexcuenta solo barras de ejecución (la primera barra dentro de tu rango seleccionado es0, luego1, …). No cuenta barras que solo sirven de calentamiento.- Las barras se simulan de la más antigua a la más reciente dentro de la ventana de ejecución.
Datos de barra dentro de los callbacks (bar)
En cada on_bar y on_tick (y en on_timer de las ejecuciones forward cuando está configurado), el motor pasa bar: una vista de barra OHLC (OhlcvBarView) del símbolo al que corresponde ese callback. Se comporta como un mapeo de solo lectura, pero isinstance(bar, dict) es falso: usa bar["close"] o acceso por atributo, no APIs exclusivas de dict.
En backtest, on_tick recibe precios sintéticos a partir del OHLC de cada barra (cuatro llamadas por barra, sin historial real de ticks). En las ejecuciones forward, on_tick se dispara con cada cotización en vivo del bróker conectado mientras el argumento price sigue la última marca; consulta Ciclo de vida de la estrategia.
Puedes usarla como un mapeo de solo lectura:
o = float(bar["open"])
h = float(bar["high"])
l = float(bar["low"])
c = float(bar["close"])
v = float(bar["volume"])
t = bar["t"] # ISO timestamp string (QuantCraft B2 daily bars: a date like "2024-01-02", UTC)También puedes leer el historial relativo a la barra actual con indexación por desplazamiento (la misma barra, cierres anteriores, etc.):
current_close = bar.close[0]
prev_close = bar.close[1]bar.close[0]es el cierre del contexto de barra actual (la misma idea quefloat(bar["close"])enon_barde backtest); consulta Índices de barras enon_barfrente aon_tickpara saber de qué barra se trata en cada callback.- Los índices mayores retroceden en el tiempo;
bar.bars_backte dice hasta dónde puedes mirar.
Índices de barras en on_bar frente a on_tick
Los desplazamientos como bar.close[0], bar.close[1], … siempre cuentan hacia atrás desde la barra más reciente que el callback puede ver, pero cuál es la "más reciente" depende del callback:

on_barse dispara cuando una barra se cierra. El índice[0]es esa barra recién cerrada,[1]es la barra anterior,[2]la anterior a esa, y así sucesivamente. Cada índice apunta a una barra completada.on_tickse dispara mientras una barra aún se está formando. El índice[0]es la barra en formación (sus valores cambian con cada tick),[1]es la última barra cerrada,[2]la anterior, y así sucesivamente.
Así, la misma vela está un índice más arriba en on_tick que en on_bar:
| Vela | En on_bar | En on_tick |
|---|---|---|
| En formación (aún no cerrada) | no visible | [0] |
| Recién cerrada | [0] | [1] |
| Una antes | [1] | [2] |
| Dos antes | [2] | [3] |
def on_bar(bar_index, bar, fundamentals=None, symbol=None):
last_closed = bar.close[0] # the bar that just closed
one_before = bar.close[1]
def on_tick(bar_index, tick_in_bar, price, bar, fundamentals=None, symbol=None):
forming = bar.close[0] # still forming — latest price so far
last_closed = bar.close[1] # same candle on_bar saw as [0]El mismo desplazamiento se aplica a cada serie de la vista (open, high, low, close, volume). Si trasladas lógica de on_bar a on_tick, suma 1 a cada desplazamiento para seguir leyendo las mismas velas.
Backtest:
on_tickreproduce la barra actual como cuatro ticks sintéticos, así que[0]allí es la barra simulada, leída de su fila completa (consulta Volumen en[0]más abajo); no es una vela parcialmente construida como en una ejecución forward.
Exactitud de los datos en vivo (ejecuciones forward)
El flujo de precios en vivo de tu bróker es una aproximación, no un registro completo del mercado:
- Los planes gratuitos de datos en vivo suelen ver operaciones de una sola bolsa (una pequeña fracción de todo el trading de EE. UU.), así que las velas construidas en vivo muestran mucho menos volumen y pueden perder los máximos y mínimos reales.
- Los planes de pago con feed consolidado ven todas las bolsas, pero las operaciones aún pueden llegar tarde en ráfagas, sobre todo alrededor de la apertura en acciones muy activas.
La aplicación sustituye cada vela construida en vivo por la barra histórica finalizada del bróker poco después de cerrarse. En marcos pequeños como 1 minuto, eso significa:
| Índice | Qué es | Para qué usarlo |
|---|---|---|
bar.close[0] | La vela en formación, construida a partir de ticks en vivo | La estimación del precio actual |
bar.close[1] | La barra recién cerrada; puede seguir construida a partir de ticks durante los primeros ~10–30 segundos | Úsala con cuidado justo después de que se abra una barra nueva |
bar.close[2] | Una barra totalmente finalizada | Señales que necesitan un cierre confirmado |
Para señales que dependen de un cierre confirmado en barras de 1 minuto, prefiere bar.close[2].
Volumen en [0]:
on_tickforward:volume[0]en la vela en formación es provisional (a menudo el tamaño de un solo tick). Usavolume[1]para el volumen de la última barra completada.- Backtest: los cuatro ticks sintéticos usan la fila completa de la barra:
volume[0]es el volumen completo de la barra, no un tamaño provisional de tick.
Este es el lugar adecuado para la lógica de trading por paso: entradas, salidas al cierre de la barra, medias móviles simples y todo lo que solo necesite la barra actual más el historial.
Marcos temporales principal y adicionales (get_bar)
El marco temporal principal de la ejecución siempre está disponible mediante rt.get_bar (no hace falta listarlo en Additional timeframes). Cuando tu estrategia también necesita barras con una resolución diferente (por ejemplo, filtros de tendencia diarios en un backtest de 5 minutos), selecciona esos extras en el desplegable Additional timeframes del cuadro Run backtest, en Symbol & data, y luego léelos desde cualquier callback:
from quantcraft import Timeframe
import quantcraft.runtime as rt
def on_bar(bar_index, bar, fundamentals=None, symbol=None):
# Primary TF works without listing it under Additional timeframes
# (use the Timeframe member that matches the modal primary, e.g. M5):
prev_primary = rt.get_bar(Timeframe.M5, shift=1)
daily = rt.get_bar(Timeframe.DAILY, shift=1)
if daily and bar.close[0] > daily.close:
... # primary bar closed above yesterday's daily closeConfigura Additional timeframes en Backtests; guarda las selecciones en los preajustes de ejecución (.qcs) bajo secondaryTimeframes.
Campos de BarSnapshot
get_bar devuelve un BarSnapshot (o None) con: open, high, low, close, volume, t.
Semántica de shift
shift | .close | .high / .low / .open |
|---|---|---|
0 | Precio de ejecución actual: idéntico en todos los marcos temporales dentro del mismo callback (backtest; consulta la nota forward más abajo) | De la barra almacenada de ese marco temporal: no unificado con el marco principal |
>= 1 | Cierre de la barra completada en ese marco temporal | OHLC completo de la barra completada: preferible para las mechas de la vela anterior |
Backtest: get_bar solo ve barras hasta el paso de simulación actual, nunca posteriores, tanto en el marco temporal principal como en cada adicional. En shift=0, los high/low secundarios están alineados en el tiempo con el reloj de simulación para cada marco. get_bar lee las barras del símbolo de reloj; en una ejecución con varios símbolos usa los bar.close[i] propios de cada ticker para su historial.
Ejecución forward: la bar principal sigue el marco temporal de la barra de herramientas del gráfico. Los búferes secundarios se actualizan cuando una barra se completa en el flujo de ese marco: high/low en shift=0 pueden ir con retraso hasta que se complete ese tramo; prefiere shift=1 para un OHLC fiable de la barra anterior. En forward, el .close de shift=0 es el último cierre propio de cada búfer, no unificado entre marcos temporales. En on_tick, volume[0] de la barra principal puede ser provisional (aún en formación).
Devuelve None cuando ese marco temporal no tiene búfer para la ejecución (no es el principal ni está seleccionado en Additional timeframes), en un backtest Screener (sin símbolo de reloj: usa los desplazamientos bar.close[i] propios de cada ticker para el historial), o cuando los datos no están disponibles. Una excepción en backtest: shift=0 en un marco temporal que no seleccionaste devuelve la instantánea actual del marco principal en lugar de None.
tf puede ser un miembro de Timeframe o una cadena como "1D" / "5Min".
Enumeración Timeframe
Impórtala con from quantcraft import Timeframe:
| Miembro | Valor de cadena | Descripción |
|---|---|---|
M1 | "1Min" | Barras de 1 minuto |
M5 | "5Min" | Barras de 5 minutos |
M15 | "15Min" | Barras de 15 minutos |
M30 | "30Min" | Barras de 30 minutos |
H1 | "1Hour" | Barras horarias |
H4 | "4Hour" | Barras de 4 horas |
DAILY | "1D" | Barras diarias |
WEEKLY | "1Week" | Barras semanales |
Algunos marcos temporales de QuantCraft B2 (2 minutos, 2 horas, 1 segundo, mensual m, trimestral q, anual y) no tienen un miembro Timeframe: pásalos a get_bar como cadenas.
Serie completa opcional: on_finish(bars, …)
Cuando termina la simulación, el motor llama a on_finish una vez. Si tu estrategia define la firma extendida, recibes filas OHLC de serie completa para el postprocesado: indicadores sobre toda la ejecución, exportar series, marcas finales para refresh_metrics, etc.
def on_finish(bars, symbol=None, bars_by_symbol=None):
...bars — símbolo de reloj, la más antigua primero
barses unalistde diccionarios del símbolo de reloj (elSYMBOLdel script si ese ticker está cargado; si no, el primer símbolo cargado: el cuadro no tiene control de símbolo principal). En una ejecución Screener no hay símbolo de reloj:barses[]ysymboles"", así que usabars_by_symbol.- El orden es cronológico: barra más antigua primero, la más reciente al final, la convención opuesta al índice
[0] = la más recientedeQcOhlcven las APIs de estilo notebook. - Cada fila incluye al menos
open,high,low,close,volumeytcuando existen marcas de tiempo. Cuando los datos fundamentales están activados, las filas también pueden incluir una instantáneafundamentalsde esa barra.
Usos típicos:
def on_finish(bars, symbol=None, bars_by_symbol=None):
if not bars:
return
closes = [float(b["close"]) for b in bars]
last = float(bars[-1]["close"])
sym = (symbol or "AAPL").strip().upper()
metrics = account.refresh_metrics({sym: last})
chart_indicators.add(...)Usa bars[-1]["close"] (o los campos de la última fila) como marca final al llamar a account.refresh_metrics({symbol: last_price}), para que las posiciones abiertas se valoren al final de la ejecución.
Para chart_indicators, prefiere los valores que acumulaste por símbolo mientras la ejecución recorría las barras y usa on_finish solo para entregarlos; recalcula a partir de bars únicamente los indicadores que no registraste barra a barra; consulta Series de indicadores.
bars_by_symbol — ejecuciones con varios símbolos
Cuando haces backtest de más de un ticker, bars sigue refiriéndose solo al símbolo de reloj. Para la serie completa y alineada de cada símbolo, usa bars_by_symbol:
- Un mapeo de ticker en mayúsculas → lista con la misma forma que
bars(dicts cronológicos por símbolo). - Úsalo cuando necesites historiales completos transversales o por símbolo al terminar (correlación, fuerza relativa, indicadores por símbolo).
Cuándo no necesitas on_finish
Si tu estrategia solo necesita la barra actual y los desplazamientos de historial, puedes implementar solo on_bar / on_tick y evitar trabajo pesado de serie completa. on_finish es opcional para la lógica, pero sigue siendo donde se entregan las series de chart_indicators y donde refresh_metrics recibe el último precio. Ten en cuenta que cada return en on_finish es silencioso: la ejecución sigue teniendo éxito, simplemente se salta lo que venía después.
Cómo se relaciona con Test Results
El resultado estructurado del backtest incluye una lista ohlcv.bars (o equivalente): una fila por paso de ejecución simulado, con campos OHLC e instantáneas fundamentals opcionales, alineada con lo que viste en los callbacks. on_finish(bars, …) es la misma serie cronológica que el motor usa para los cálculos de "toda la ejecución" del símbolo de reloj, así que tu código de final de ejecución coincide con lo que aparece en los resultados y los gráficos.
Referencia rápida
| Tema | Resumen para el usuario |
|---|---|
| Cargadores | Feed del bróker conectado (claves + amplio intradía) frente a almacenamiento de QuantCraft (sesión iniciada; diario / semanal / mensual, más con Premium). |
| Fechas | Inicio + fin YYYY-MM-DD → ventana filtrada por calendario; omitir ambas → retroceso heredado del feed del bróker o archivo completo del almacenamiento de QuantCraft. |
| Calentamiento | Historial extra antes del inicio para los desplazamientos de bar; los callbacks solo en la ventana de ejecución. |
| Por paso | Usa bar / bar["close"] / bar.close[k] en on_bar / on_tick. [0] = última barra cerrada en on_bar, barra en formación en on_tick. |
| Marcos principal / adicionales | El principal siempre mediante rt.get_bar; los extras mediante Additional timeframes + rt.get_bar(Timeframe.X, shift). |
| Serie completa | Usa on_finish(bars, symbol=..., bars_by_symbol=...): bars de la más antigua a la más reciente para el símbolo de reloj; bars_by_symbol para todos los símbolos de las ejecuciones con varios símbolos. |
| Rendimientos de factores globales | No están en QcOhlcv ni en bar: usa los factores Fama-French (fama_french.qc). Las rows mensuales son un dict YYYYMM; usa latest_row, no rows[0]. |

