DocsThe Signal Language (DSL)Data accessors (bar, option, time)
The Signal Language (DSL)Reference

Data accessors (bar, option, time)

A standalone signal reads market data through three accessors: bar.* (1-minute OHLCV), option.* (interpolated IV and greeks, plus single-contract selection), and time.* (deterministic NYSE-calendar signals). Every accessor method returns a Signal on the same spine, so you can combine them in one expression.

Updated Jul 20266 min read

Every method returns a Float64 Signal, nullable, timestamps tz-aware in America/New_York, on the 09:31-to-close spine. Every method takes an optional sampling= or bucketing= (mutually exclusive) and an optional per-call symbol= override. bar and option query market data; time computes calendar attributes with no data query. Member access is dot-plus-call: write bar.close(), never a bare close.

For a terse per-method lookup of signatures and defaults, see the data accessor reference. This page is the tour.

How accessors work

All three accessors return Signals on one spine: 09:31 to market close, NYSE trading days only, timestamps in America/New_York. Values are Float64 and nullable, so missing data is null rather than a dropped row. Because bar and option share the equity-option spine, a mixed expression like bar.close() / option.iv(30) aligns on the union spine.

Every method accepts two more optional arguments beyond its own:

  • sampling= or bucketing=, mutually exclusive on a single call. sampling selects a point-in-time value; bucketing aggregates a window. Both are covered in the execution model.
  • symbol=, a per-call override of the context symbol (case-insensitive). It applies to that one call only and does not mutate the context, which is what makes a cross-symbol expression work: bar.close() / bar.close(symbol="QQQ").

Positional rule: dte is the only positional argument (on option.*). Once you pass any argument by name, every following argument must be named too.

The available symbols and their history are listed on the data coverage page. time.* works for any symbol because it reads the calendar, not a data table.

bar: equity bars

bar reads 1-minute OHLCV. Five methods, each returning a Float64 Signal:

  • bar.open(): the bar open. This is the spot price used in the IV and greeks pipeline.
  • bar.high(): the highest traded price in the bar.
  • bar.low(): the lowest traded price in the bar.
  • bar.close(): the closing (last) price of the bar.
  • bar.volume(): total share volume in the bar.

Each timestamp is the bar's close time, and the bar covers [t − 1min, t]. On a normal session 09:31 is the first bar and 16:00 is the last; on an early-close day the last bar is 13:00. bar methods take symbol=, sampling=, bucketing=, split_adjusted=, and dividend_adjusted= (no dte or type).

// Intraday high-low range on the 1-minute equity spine
bar.high() - bar.low()
// Daily close, sampled at each session close
bar.close(sampling=sampling(period="day", time="close"))

vwap is not exposed: it is null on zero-volume bars and adds ambiguity downstream. Use bar.close() as a price proxy when you need one.

Split and dividend adjustment

bar prices are split- and dividend-adjusted by default. Every bar.* method takes two boolean parameters, split_adjusted and dividend_adjusted, both defaulting true. Set either to false to read raw, as-printed prices.

For how the adjusted series is constructed, and the look-ahead trap it introduces, see stock price adjustments.

option: surface IV and greeks

option reads the volatility surface. Six methods, each returning a Float64 Signal on the equity-option spine:

option.delta(), option.gamma(), option.theta(), option.vega(), option.rho(), option.iv().

All six share the same contract-selection arguments:

  • dte: Int, required, the first positional argument. Non-negative calendar days to expiration.
  • type: "call" or "put", default "call".
  • Exactly one of delta (a number; default resolves to the 0.5-delta, roughly at-the-money, contract, or -0.5 for a put) or moneyness (log-moneyness ln(K/forward) centered on the implied forward, where 0.0 is at-the-money-forward). delta and moneyness are mutually exclusive.
  • sampling=, bucketing=, symbol=, as above.

option.iv adds two arguments:

  • iv_type: "bid", "mid", or "ask", default "mid".
  • use_ex_earn_iv: Bool, default false. When true it returns the earnings-adjusted IV, which removes the variance attributable to a pending earnings event. It is null only for a symbol with no earnings calendar (SPY and other broad-market ETFs), and wherever the underlying regular IV is itself null. For a contract expiring before the next earnings date, or when the earnings-IV decomposition cannot be resolved, it returns the regular IV rather than going null.
// 30-DTE mid IV, sampled to daily
option.iv(30, sampling=sampling(period="day", time="close"))

Because the earnings-adjusted value is null on a symbol with no earnings, fall back to standard IV where you need a value at every timestamp:

// 30-DTE ex-earnings mid IV, falling back to standard mid IV where absent
coalesce(option.iv(30, use_ex_earn_iv=true), option.iv(30))

Option values are 2D-interpolated surface values

Every option.* read is a 2D-interpolated surface value, not a single listed contract's quote. The engine fetches four bracketing contracts (two DTE levels by two delta or moneyness levels), interpolates the delta or moneyness dimension at each DTE level, then interpolates across DTE. When the surface is sparse or the fit cannot be resolved, the result is null, never extrapolated.

When you need one real, tradable contract rather than a surface value, use option.contract(...).

option.contract(...): selecting one real contract

option.contract(...) returns a Table (a ContractTable), not a Signal. It selects, for each minute on the equity-option spine, the single real contract whose dte and one of delta / moneyness / strike best match your targets within thresholds. A minute with no in-threshold contract yields null columns.

Project one numeric column with field access, which gives you a Signal:

// Per-minute mid premium of the 30-DTE 30-delta call
option.contract(dte=30, delta=0.30).mid

The full column surface, the matching thresholds, and cross-leg use are documented on signals inside a backtest. This page covers only the standalone shape.

time: deterministic calendar signals

time computes calendar attributes from the NYSE session schedule. No data query runs. Every method takes no positional arguments (except the windowed functions below), returns the attribute of each spine timestamp, and supports optional sampling, bucketing, and symbol.

Method
Range
Notes
time.day_of_week()
1–7
ISO weekday, 1 = Monday
time.day_of_month()
1–31
Calendar day within the month
time.day_of_year()
1–366
Ordinal day within the year
time.week_of_month()
1–5
Monday-anchored week within the month
time.month()
1–12
Calendar month
time.quarter()
1–4
Calendar quarter (unrelated to period="quarter" sampling)
time.hour()
0–23
Hour in Eastern Time
time.minute()
0–59
Minute within the hour
time.minute_of_day()
minutes since ET midnight
571 at 09:31
time.is_week_end()
0 or 1
1 on the last NYSE trading day of the ISO week
time.is_month_end()
0 or 1
1 on the last NYSE trading day of the month

Four windowed functions take a window size and a keyword-only past= flag:

  • time.trading_days(calendar_days, *, past=false): NYSE trading days inside a calendar-day window. calendar_days=0 returns 1.0. Constant within a session (it depends on the date, not the intraday time).
  • time.trading_minutes(calendar_days=0, *, past=false): trading minutes relative to the current bar, current bar always included. With calendar_days=0, past=false it is the minutes remaining to close; with past=true it is the minutes elapsed from open.
  • time.calendar_days(trading_days, *, past=false): the inverse of trading_days. Returns null if the target trading day falls outside the available calendar. trading_days=0 returns 0.0.
  • time.session_fraction(*, past=true): the fraction of the session elapsed by default, or remaining with past=false. Range [0, 1], current bar included.
// Fraction of the session still remaining, on the 1-minute spine
time.session_fraction(past=false)

Why time.* may look forward

past=false is permitted on the windowed time functions because they read the deterministic public NYSE calendar, not market prices. No-lookahead governs market data, and a public schedule is not market data, so a forward-looking calendar count does not violate it.

Both session_fraction and trading_minutes scale off the session's total bar count, total_bars: 390 on a normal day (09:31–16:00) and 210 on an early-close day (09:31–13:00).

What's computed but not readable

The methodology computes more upstream than the accessors expose today. Each quantity below is named, but no accessor returns it. Naming an accessor here would mean inventing one, so this list names the quantity and where it is documented, not an accessor.

Quantity
Readable via an accessor today
Where it's documented
Implied forward and forward price
No
Carry and borrow
No
Implied forward, carry and rates
Risk-free-rate series
No. The word "risk-free" surfaces only inside option.rho, which is a greek, not a rate accessor
Implied forward, carry and rates
Dividend-escrow spot and adjusted spot
No
Dealer exposure (GEX and VEX)
No
Second-order and cross greeks (vanna)
No
Earnings metrics (implied move, event vol, next earnings date)
No. The only earnings surface today is the use_ex_earn_iv flag on option.iv
IV quality and provenance flags
No
Arbitrage-free IV surface
No
Implied volatility

If you need one of these today, the value is not reachable from a signal.

Was this page helpful?