Skip to content

Price data and bars

Read the current bar or earlier values to build a trading rule. A bar is one interval of price data.

Read a value

Series Value
open[0], high[0], low[0], close[0] Current bar's prices.
volume[0] Current bar's volume.
timestamp[0] Current bar timestamp.
close[1] Previous bar close.
hl2[0] Average of current high and low.
hlc3[0] Average of current high, low and close.

[N] means N bars ago. Negative indices are not allowed. close is also callable: close() requests an exit; close[0] reads data.

Wait for enough history

def on_tick():
    if len(close) < 20:
        return
    if position.is_flat and close[0] > mean(close, 20):
        long(10)

Reading past available history raises a history error. A guard makes warmup explicit. Do not use future bars to decide a historical trade.

For another timeframe, len(security("SPY", "1h")) can guard unavailable history only after timing is valid. Every read needs the current decision time and datetime source timestamps with no nulls and compatible timezones. Missing timing raises the essential TS-TF-NO-TIMESTAMP error, even in non-strict runs; it is not an empty history or a false trading condition. Repair the source or dispatch clock. See the hourly worked example for which completed bar is visible, when a resulting order fills, and how an async host keeps existing holdings managed while new decisions are blocked.

Use an indicator

Pass the series to a supported indicator, such as sma(close, 20). Many indicator results are series too, so [0] selects the current value. Check indicator usage before mixing scalars and series.

For exact available data functions, see the price-data reference.

Count the values you need

Index zero is the newest available bar. Reading close[1] needs at least two bars; reading close[20] needs at least 21. A full 20-bar window uses offsets 0 through 19. The current value is included in mean(close, 20) and highest(high, 20).

def on_tick():
    if len(close) < 2:
        return
    price_is_rising = close[0] > close[1]
    if position.is_flat and price_is_rising:
        long(10)

len(close) reports the available dispatch history. History length does not prove every field is valid; price/volume inputs must also be appropriate for the selected instrument. A history error and a valid condition that returns false are different outcomes.

Bound a long run's memory

Set retention in the parameter dictionary returned by on_start():

def on_start():
    return {"max_bar_history": 5000, "max_bars_per_symbol": {"TCS": 1000}}, {}

max_bar_history limits the shared observed-bar stream used by close and the other price proxies. Its default is 200,000 feed bars. max_bars_per_symbol optionally limits the per-symbol history lookup indexes; a positive integer applies to every symbol, or a dictionary sets individual limits. Unspecified symbols use the global limit. Limits must be positive integers, not booleans.

In this example get_historical_bars("TCS", 1500) can return at most the most recent 1,000 retained TCS observations, while the shared stream can hold 5,000 feed bars. A request returns the observations available so far, never future bars or an automatic rescan of the complete backtest. Size your limits for indicator lookbacks and warmup, and check the returned length. Timeframe queries prefer retained bars of that timeframe; when none exist they retain the single-resolution fallback to the symbol's available bars. A fallback is not timeframe aggregation: its BarSeries.timeframe reports the actual resolution, not the missing requested one. Strict consumers such as security and sizing reject a resolution mismatch. Mixed-resolution fallback data cannot be combined into a falsely labeled series. For example, requesting 1d from a 1m-only local stream still gives legacy history readers minute observations labeled 1m; it cannot supply a daily indicator or daily sizing history.

When supplying model Bar objects, pass the wrapper timestamp and mode. If the dataframe also contains timestamps, they must agree with the wrapper's instant. Each symbol has at most one row in a bar. Duplicate symbol rows or conflicting times raise before execution instead of letting scalar reads silently select one observation. Wrapper-only timestamps remain supported for existing feeds.

Bar.from_polars and CSV loading retain an optional timeframe column. Every row in one timestamp group must have the same nonempty label, for example 1d. Without that column, the existing 1m default remains; daily spacing alone does not establish a daily availability contract. For a daily CSV, include timestamp,symbol,open,high,low,close,volume,timeframe with 1d on each row, then run atom run my_strategy.py --data daily.csv. The default synthetic CLI stream is not a source of daily candles.

Prices, volume and full bars

hl2 and hlc3 are derived prices in the same units as close. ohlc4 averages open, high, low and close. Volume is a quantity field, not a price, and its unit depends on the data source. bars supplies the full OHLCV bundle for functions such as ATR that need more than one price series.

In explicit multi-symbol context, select the series you mean, for example close["TCS"][0], instead of relying on an ambiguous bare close. For a second symbol or timeframe, use security and its separate history window. A daily history window does not count intraday bars.

When constructing a model BarSeries, an explicit portfolio mode stays a portfolio even with one remaining symbol. Integrations can therefore preserve a dictionary of per-symbol results as the universe shrinks. Multiple symbols require portfolio mode. Choose Mode.SINGLE explicitly when one symbol should return a scalar or one series rather than a per-symbol dictionary. Previously a one-symbol Mode.PORTFOLIO construction was silently collapsed to single mode. See Alpha Lab inputs for its ordered history requirements and single-versus-portfolio result shapes.

In a local backtest, an explicit list request such as get_historical_bars(["TCS", "INFY"], 100, timeframe="1d") returns up to 100 observed bars per available instrument. Import the helper from atom.helpers.historical when using it explicitly. It retains portfolio mode for a one-name list, omits names without history and returns None if no requested name has data. It does not fill gaps or manufacture a shared trading calendar.

Raw and adjusted model data

Model Bar and BarSeries default to adjusted=False. Set adjusted=True only when the supplied prices already include the data provider's split or dividend adjustments; the flag does not adjust prices itself. Symbol extraction, resampling and local historical queries preserve it. Combining raw and adjusted observations raises rather than silently treating them as one price history.

For example, an adjusted portfolio history extracted with history["TCS"] still reports adjusted=True, with unchanged TCS prices. Resampling that child to five minutes retains the same flag. Before 4.11.0, extraction and local history could incorrectly report those numbers as raw; rebuild affected inputs.

Model field lists are chronological, unlike injected close[0], which is newest. They are detached views of the current dataframe. Changing a returned list does not change the model; replacing history.df or editing its columns changes the next property read. Keep symbols, timestamp, timeframe and mode consistent with the replacement frame: mutation does not rerun construction validation.

For instance, after reading values = history.close, changing values[-1] does not edit prices. To transform prices deliberately, replace the appropriate dataframe column and read history.close again. The old persistent property cache could return stale numbers after that replacement; 4.11.0 no longer does.

Editor declarations

atom-stubs.pyi is the canonical declaration file for injected names. Its types include callable close, explicit symbol selection and datetime timestamps. An editor cannot infer bare globals simply from an arbitrarily named file beside a strategy. A standalone checker can expose this file as atom_vocab.pyi in its configured stub path and use a typing.TYPE_CHECKING-only import of those names; the runtime still supplies the real globals. Host editors may integrate the same declarations directly. close() can return None when it closes a portfolio; do not use that return value as confirmation of fills.