ChartnautDocs

Reference data

dataset(...) gives a script reference data that is not market prices: the economic calendar and daily rates and yields. You declare a handle once at module scope and read it on every bar. Indicators, definitions and studies all use it.

const cal = dataset("econ.calendar", {
  where: { country: { fromInstrument: true }, localImportance: { gte: 3 } },
  lookahead: "1d",
});

export function onBar(ctx) {
  // Stand aside from 15 minutes before to 30 minutes after an important release that is still happening.
  if (cal.within({ before: "15m", after: "30m" }).some((r) => r.state === "scheduled")) return;
  // ...
}

Every read is as known at that bar

A read is made at the end of the bar being computed. Inside a timeframe handler it is the end of the bucket being handled. On a bar that has not closed yet (a live chart's forming bar, or a run fetched up to now) it is now. The handle answers with what was known at that moment:

  • A release is visible once it was listed, which is never more than 7 days before its time.

  • Its forecast is visible once published, and its previous figure once printed.

  • A postponement or cancellation takes effect from when it was known.

  • A rate or yield is visible from when it was published.

A backtest sees exactly what a live chart saw at the same moment, so a filter you test on history behaves the same way live. Where history cannot say when something became known, the platform uses the latest safe time, so a backtest may know less than a live chart, never more.

Declaring a handle

dataset(name, options) must be called at module scope with literal arguments.

Option

Meaning

where

Which rows. Required. Keys are the dataset's filterable fields

lookback

How far back reads reach, e.g. "1d". Default "7d"

lookahead

How far ahead reads reach. The calendar allows up to "7d", its listing horizon. Rates have none: a value exists once published

A where value is one of these:

  • a literal, meaning equals: { country: "US" };

  • an array, meaning any of: { code: ["US.NFP", "US.CPI.YOY"] };

  • one operator: { importance: { gte: 2 } }, { code: { in: [...] } }, or { country: { fromInstrument: true } } for the economies that move the chart's instrument.

Values are checked when the handle is declared:

  • countries are two-letter economy codes (US, EU for the euro area, UK, JP); case does not matter, and GB, USA and EZ are read as UK, US and EU;

  • importance and localImportance are 1, 2 or 3, or low, medium or high;

  • categories and rate families are their listed names;

  • codes look like US.NFP or GBOND.US10Y.

Anything else is refused, and so is a code or economy the data does not have: a filter never silently matches nothing.

Settings cannot appear in where. To filter by a setting, read everything the setting can reach and filter the rows in the script:

const cal = dataset("econ.calendar", { where: { country: { fromInstrument: true } } });
export function onBar(ctx) {
  const soon = cal.within({ after: "1h" }).filter((r) => r.localImportance >= ctx.params.minImportance);
}

Plans set how many handles one script may declare: Free 1, Starter 2, Pro 4, Ultra 8. An indicator that reads a dataset can be used as a dependency like any other: it reads the same data wherever it runs.

Reading

Read

Returns

h.last

The newest row whose time has passed

h.next

The earliest row still ahead (always undefined without a lookahead)

h.value

The main value of last (rates: the rate); undefined for the calendar

h.within({ before, after })

Rows whose time is in [now - before, now + after], oldest first

h.between(from, to)

Rows whose time is in [from, to] (unix seconds)

h.history(n)

The last n rows whose time has passed, oldest first

h.updatesThisBar

What changed since the previous bar: { key, kinds, row }. On the first bar after a gap (a weekend, a session break) it includes what changed while no bar was open

h.asOf(t)

The same reads as known at an earlier instant t

h.valueAt(t)

asOf(t).value

h.coverage

{ from, to, truncated }: the span reads cover right now

last, next, value and history skip rows that are withdrawn, cancelled or rescheduled. A replacement counts once it is listed. within, between and updatesThisBar return every row, so check row.state === "scheduled" before treating a row as a release that is happening.

Rows are frozen and hold only their dataset's fields. Reading any other name (row.actual, row.forecast, row.time) throws; an optional field without a value (estimate, previous, rescheduledTo) is undefined.

Times are unix seconds, like ctx.time. A millisecond timestamp, a Date or a date string is refused. within takes { before, after } and nothing else, history(n) takes a whole number, and between(from, to) takes two times in order.

within spans may not exceed the handle's lookback and lookahead: asking for more throws, so declare what you read. asOf(t) may look back but never past the bar being computed.

In studies

A study has no current bar, so it reads at a time with h.asOf(t). ctx.event.knownAt is the end of the bar the event fired on: the moment the definition decided, with what it knew then. Inside scope(ctx) read at knownAt and never later; onEvent may read later times to measure an outcome. See scope.

A time past now (the latest moment the run's data covers) has not happened yet. h.asOf(t) and h.valueAt(t) there return undefined and the event is marked not ready, the same as a forward candle read past the last bar, so its result is computed on a run after that moment rather than stored from a calendar that can still change. Check for it: const later = cal.asOf(t); if (!later) return;.

const cal = dataset("econ.calendar", { where: { country: { fromInstrument: true }, localImportance: { gte: 3 } } });
export function scope(ctx) {
  const n = cal.asOf(ctx.event.knownAt)?.next;
  const hours = n ? (n.scheduledAt - ctx.event.knownAt) / 3600 : Infinity;
  return { newsSoon: hours <= 1 ? "within 1h" : "later" };
}