Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.darvas.app/llms.txt

Use this file to discover all available pages before exploring further.

What is a Series?

A Series is a named array that grows bar by bar. You write a value at the current bar with .set() and read past values with .get(offset). Unlike regular JavaScript variables that reset on each onBar call, a Series persists across bars.
const prevEma = Series("prev-ema");

onBar(() => {
  const ema = ta.ema(ctx.close, 14);
  const prev = prevEma.get(1); // EMA from previous bar

  // Detect EMA crossing above its own prior value
  if (!na(prev) && ema > prev) {
    plot("Signal", ctx.low() - ctx.atr); // placeholder
  }

  prevEma.set(ema); // store for next bar
});

API reference

Series(name)
Series object
Create a named series. Must be called at the top level (not inside onBar). Up to 64 series per script.
const s = Series("my-series");
s.set(value)
void
Store value at the current bar index. Call inside onBar.
s.set(ctx.close());
</ParamField>

<ParamField path="s.get(offset?)" type="number">
Read the value at `offset` bars back. `offset = 0` (default) is the current bar. Returns `NaN` for bars where `.set()` was never called.
```js
const current = s.get();    // current bar
const prev    = s.get(1);   // 1 bar ago
const twoBack = s.get(2);   // 2 bars ago
s.length
number
Number of bars where .set() has been called.

Budget

  • Maximum 64 Series per script (shared with any hidden series created internally by ta.barssince and ta.cum).
  • Each series consumes roughly 8 KB per bar in the window.
  • Total budget across all series: ~512 KB per script.
Creating Series inside onBar mid-execution is allowed syntactically, but the series will have no backfill - it starts empty from the bar it was first created. For correct historical replay, always create Series at the top level.

Worked example: crossover detection

// Detect when the fast EMA crosses above the slow EMA
const fastSeries = Series("fast-ema");
const slowSeries = Series("slow-ema");

plot("Fast EMA", null, { color: "#22c55eFF" });
plot("Slow EMA", null, { color: "#f59e0bFF" });
plot("Cross", null, { style: "arrowup", colorUp: "#22c55eFF", size: 10 });

onBar(() => {
  const fast = ta.ema(ctx.close, 9);
  const slow = ta.ema(ctx.close, 21);

  const prevFast = fastSeries.get(1);
  const prevSlow = slowSeries.get(1);

  fastSeries.set(fast);
  slowSeries.set(slow);

  plot("Fast EMA", fast);
  plot("Slow EMA", slow);

  // Crossover: fast was below slow, now above
  const crossover = !na(prevFast) && !na(prevSlow) && prevFast < prevSlow && fast > slow;
  plot("Cross", crossover ? ctx.low() * 0.999 : NaN);
});

No-backfill behavior

A Series created mid-script (e.g. inside a conditional block on bar 500) has no values for bars 0-499. s.get(offset) returns NaN for those bars. Plan accordingly using na(s.get(...)) guards.

NaN handling

Series.get() returns NaN during warmup.

Aggregators

ta.barssince and ta.cum use internal series with a key.

API limits

64-series budget shared with stateful aggregators.