Documentación1.0 (latest)
PáginasDatos de calendario · 13 / 29

Módulo de calendario

El módulo de calendario da a tu estrategia el historial de dividendos, el historial de splits, las fechas de resultados, la fecha de salida a bolsa y las presentaciones recientes ante la SEC de una empresa, y permite que el backtest aplique automáticamente los splits y dividendos a tus posiciones.

Activa Include calendar data en la pestaña Symbol & data del cuadro Run backtest. Está activado por defecto, y tu elección (junto con las dos subopciones) se recuerda para la siguiente ejecución. Los calendarios se cargan automáticamente para cada símbolo que opera la ejecución, incluido un universo resuelto por un screener; no hay selector por ticker. El cuadro Test de un agente tiene la misma opción — Include calendar data (dividends, splits) (consulta Agentes).

Solo backtests. Los datos de calendario se cargan para backtests, Debug Test y pruebas de agentes. Las ejecuciones forward — chart forward tests, ejecuciones bulk de AutoTrading y QuantCloud — nunca reciben un calendario, así que calendar allí es siempre None.


Por qué importa: las barras de precio son crudas

Las velas del backtest no están ajustadas. Sin datos de calendario:

  • Un split 4 por 1 parece un desplome del 75 % del precio de un día para otro, y tu posición simulada se desploma con él.
  • Los dividendos nunca llegan, lo que subestima discretamente la rentabilidad total de cualquier posición mantenida mucho tiempo — aproximadamente un 2 %/año para un pagador de dividendos típico.

Las dos subopciones corrigen ambas cosas sin necesidad de cambiar código.


El parámetro de callback calendar

Añade un parámetro calendar a cualquier callback y se te pasa el manejador:

def on_bar(bar_index, bar, calendar=None, symbol=None): if calendar is None: # always guard — not every run enables calendar data return days = calendar.days_to_earnings(bar["t"], symbol) if days is not None and days <= 2: return # sit out the earnings print divs = calendar.dividends(bar["t"], symbol) if divs and (divs[-1].amount or 0) > 0.5: # amount can be None ...

Funciona en on_bar, on_tick y on_timer. El argumento calendar se asocia por nombre, así que un script sin parámetro calendar se comporta exactamente como antes, y un calendar declarado recibe el manejador o None — nunca otro objeto, sea lo que sea lo demás que tome tu firma. Si también lees datos fundamentales, la firma recomendada es def on_bar(bar_index, bar, fundamentals=None, calendar=None, symbol=None).

También puedes llegar al mismo manejador sin parámetro: la variable global del script qc_calendar, o quantcraft.process.runtime.qc_calendar.


Métodos

MétodoDevuelve
dividends(bar_time, symbol=None)Dividendos públicos en esa barra
splits(bar_time, symbol=None)Splits anunciados hasta esa barra
filings(bar_time, symbol=None)Presentaciones ante la SEC públicas en esa barra
next_earnings(bar_time, symbol=None){date, basis, estimated, …} o None
days_to_earnings(bar_time, symbol=None)int o None
ipo_date(symbol=None)date o None
current(bar_time, symbol=None)Un diccionario instantáneo de todo lo anterior en esa barra, o None
dividends_between(start, end, symbol=None)Dividendos cuya fecha ex-dividendo cae en (start, end]
splits_between(start, end, symbol=None)Splits cuya fecha efectiva cae en (start, end]
has(symbol=None)Si este símbolo tiene datos de calendario
symbols / primary_symbol() / get(symbol=None)Los símbolos cargados, el predeterminado y el calendario en bruto por símbolo (o None)

Los métodos _between usan las fechas de efecto («¿cuándo afecta a mi posición?»), a diferencia de los métodos por hora de barra anteriores, que usan las fechas de «¿podría haberlo sabido?» — consulta la siguiente sección.

bar_time acepta bar["t"], bar.t[0], un datetime, un date o segundos epoch.

Las listas van de la más antigua a la más reciente — dividends(...)[-1] es la más reciente. Es lo contrario de los datos fundamentales, donde el índice 0 es el periodo más reciente.

Un símbolo sin datos de calendario devuelve [] o None, nunca un error. La mayoría de los tickers fuera de las grandes capitalizaciones no tienen calendario en el servidor, así que comprueba si está vacío en lugar de esperar excepciones.


Dos fechas por evento: usa la correcta

Cada dividendo y cada split lleva dos fechas, y elegir la incorrecta introduce sesgo de anticipación:

«¿Podría haberlo sabido?»«¿Cuándo afecta a mi posición?»
Dividendodeclaration_dateex_date
Splitfiling_dateeffective_date

dividends() / splits() / filings() filtran por la primera columna: devuelven solo lo que era público en tu barra. Un split suele ser visible semanas antes de que entre en vigor, porque el 8-K se presenta con antelación:

from datetime import date bar_date = date.fromisoformat(str(bar["t"])[:10]) splits = calendar.splits(bar["t"], symbol) upcoming = [s for s in splits if s.effective_date and s.effective_date > bar_date] # announced, not yet effective

Campos de los eventos

DividendEvent: ex_date, amount, estimated, type, declaration_date, record_date, payment_date, period_end SplitEvent: effective_date, ratio, type ("forward" | "reverse"), filing_date, form FilingEvent: form, filing_date, report_date, items, category, accession_number, primary_document, primary_document_description IpoInfo: date, first_trade_date, source

amount (dividendos) y effective_date (splits) pueden ser None: comprueba antes de comparar.

La category de una presentación es una de earnings, acquisition, officer-director, charter-amendment, agreement, regulation-fd, auditor-change, shareholder-vote, cybersecurity, other.

El indicador estimated

EDGAR no publica las fechas ex-dividendo. Salvo que hubiera datos del mercado disponibles, la fecha ex-dividendo se estima como la fecha de declaración + 10 días y es precisa a unos ±7 días, con estimated is True. Por defecto, la simulación sigue abonando los dividendos estimados (política best_available); un preajuste de ejecución (.qcs) puede establecer dividendDatePolicy en exact_only (omitir los estimados) u off — no hay control en el cuadro para esto. Filtra por él en tu propia lógica si tu estrategia es sensible a la fecha ex-dividendo:

exact = [d for d in calendar.dividends(bar["t"], symbol) if not d.estimated]

Acciones corporativas en la simulación

Ambas subopciones están activadas por defecto cuando los datos de calendario lo están, y ambas se ejecutan antes de tus callbacks en cada barra, así que on_bar siempre ve una posición ya ajustada.

Ajustar las posiciones por splits de acciones — en la fecha efectiva, el número de acciones se multiplica por la proporción y el coste base se divide por ella. Los precios de stop y objetivo también se reescalan. Como qty × entry_price no cambia, tu curva de capital permanece continua a través del split.

Las barras de precio se mantienen deliberadamente sin ajustar, así que bar["close"] es el precio histórico real. Un indicador calculado sobre la serie de cierres en bruto seguirá viendo el salto del split: ajusta tu propia serie con calendar.splits(...) si eso importa.

Abonar los dividendos en efectivo — en la fecha ex-dividendo, qty × amount se suma al efectivo en las posiciones largas y se resta en las cortas (un vendedor en corto debe el dividendo al prestamista). Esto fluye al capital y a todas las métricas de rentabilidad, pero no a realized_pnl, que sigue siendo solo de operaciones. Lee los totales de las métricas de la cuenta:

def on_finish(bars, symbol=None, bars_by_symbol=None): from quantcraft.process.runtime import account m = account.refresh_metrics({}) print(m["total_dividend_income"], m["total_splits_applied"])

Dónde ves las acciones corporativas

Nada cambia tu posición ni tu efectivo en silencio:

  • Panel de salida — una línea [calendar] por acción corporativa aplicada, más un resumen al final:
    [calendar] 2024-01-10 AAPL — SPLIT 4:1 applied to 1 position(s): 100 -> 400 shares (cost basis unchanged; bars stay unadjusted) [calendar] 2024-02-09 AAPL — DIVIDEND $0.2400/share on 400 share(s): +$96.00 cash … [calendar] corporate actions — 1 split(s) applied; 1 dividend(s) applied (0 exact ex-date, 1 estimated +/-7d)
  • Test Results — una fila Split o Dividend aparece intercalada entre las operaciones cerradas (la tarjeta se titula entonces Closed trades & events), mostrando la proporción o el importe por acción y el impacto en efectivo. Una fecha ex-dividendo estimada se marca como est. Estas filas no llevan marcador # en el gráfico y se excluyen de la exportación CSV.

next_earnings es de un instante concreto

En un backtest, next_earnings nunca devuelve la «próxima fecha de resultados» del servidor calculada a fecha de hoy — eso sería sesgo de anticipación en una barra histórica. Se reconstruye a partir de la última presentación de resultados visible en tu barra más la cadencia de publicación de la empresa, así que basis == "filing-cadence" y estimated is True.

Devuelve None cuando aún no hay ninguna presentación de resultados visible — al principio de una ejecución, o para un símbolo cuyo historial de presentaciones no llega tan atrás. Compruébalo siempre.


Cuando no ves dividendos

Una ejecución que no aplica nada explica por qué en el panel de salida:

El registro diceSignificado
loaded 3/5 symbol(s); 2 without calendar dataSe imprime al inicio: cuántos símbolos obtuvieron un calendario
none of 5 symbol(s) returned calendar data: …Ningún símbolo de la ejecución tiene datos de calendario — no se puede aplicar nada
no calendar data on the serverLa tarea de calendario no se ha ejecutado para ese símbolo
calendar has no dividend or split historyNo paga dividendos, sin splits registrados
… ex-dates 2019-05-10..2020-05-08Paga, pero no dentro de tu rango de fechas probado
N with no ex-date, which can never be appliedHay historial pero no se puede situar en el reloj
no positions were held …No había nada abierto cuando venció una acción

Si no aparece ninguna línea [calendar], los datos de calendario no se adjuntaron a la ejecución: revisa el interruptor Include calendar data de la pestaña Symbol & data.


Véase también

9 min de lecturaSugerir una edición ↗
Datos de calendario — Documentación (1.0 (latest)) | QuantCraft