Skip to content

Market data, timeframes, and instruments

View Markdown

Backtest accepts a pandas DataFrame for one asset. Load and clean your data before constructing the backtest.

Timeframe · TimeframeUnit · DataContext · InstrumentSpec

Load a CSV

import pandas as pd

data = pd.read_csv("prices.csv", parse_dates=["Date"], index_col="Date")
data = data.sort_index()
data = data[["Open", "High", "Low", "Close", "Volume"]]

The CSV is your input file. For a ready-to-run alternative, use from openbacktesting.test import GOOG and data = GOOG.copy().

Requirement Reason
Exact column names Open, High, Low, Close The broker reads these prices. Add Volume for volume indicators.
Finite, positive OHLC; valid high/low ranges Orders cannot be matched against missing or invalid prices.
Increasing, unique index Each row represents one bar in chronological order. A DatetimeIndex gives meaningful timestamps and calendar statistics.
Extra numeric columns allowed Access them with self.data.MyColumn or bind by name to indicators.
No mutation after indicator registration Preprocess before the run so indicators and execution use consistent prices.

Read data inside a strategy

self.open, high, low, close, and volume are read-only handles. self.hl2=(H+L)/2, self.hlc3=(H+L+C)/3, and self.hlcc4=(H+L+2*C)/4. During next(), [-1] means the current bar and [-2] the previous bar. During init(), the full input history is visible.

Save handles such as self.saved_close = self.close to keep following the bar cursor. .s, .df, and numpy.asarray(handle) produce snapshots of currently visible data; a saved pandas object does not grow on later bars. Arithmetic also produces ordinary arrays. Register calculations through self.ind if they must be read through a handle.

self.data_index is the visible index; self.df exposes the visible data table. self.pip is a data-derived estimate, not an exchange tick rule.

Specify timeframe and timezone

from openbacktesting import DataContext, Timeframe
from openbacktesting.test import GOOG

data = GOOG.copy()
data.index = data.index.tz_localize("America/New_York")
context = DataContext(Timeframe.days(1), "America/New_York")

Pass context=context to Backtest, or to runtime.run where it is required. The index must be timezone-aware. The context converts display timezones while preserving instants; it does not resample bars. Use the actual timeframe of your input data. self.timeframe is None if no context is supplied.

Construct intervals with Timeframe.seconds/minutes/hours/days/weeks/months(multiplier). Multipliers are positive integers; months are calendar intervals, not a fixed number of seconds. TimeframeUnit supplies the corresponding enum values when constructing Timeframe(unit, multiplier) directly.

Timeframe

Timeframe(unit, multiplier)

Timeframe value; the host validates data-source support, and not all combinations are guaranteed to have market data.

Attribute Type Meaning
unit TimeframeUnit TimeframeUnit for this interval.
multiplier int Positive integer count of timeframe units.

Timeframe.seconds

Timeframe.seconds(value)

Create a seconds timeframe.

Timeframe.snapshot

Timeframe.snapshot()

Return independent plain unit/multiplier values.

Timeframe.hours

Timeframe.hours(value)

Normalize hours to minutes; 24 hours is not a daily timeframe.

Timeframe.days

Timeframe.days(value)

Create a calendar-day timeframe.

Timeframe.minutes

Timeframe.minutes(value)

Create a minutes timeframe.

Timeframe.weeks

Timeframe.weeks(value)

Create a calendar-week timeframe.

Timeframe.months

Timeframe.months(value)

Create a calendar-month timeframe.

TimeframeUnit

Time-bar units; calendar units are not implicitly converted to fixed seconds.

Member Value
TimeframeUnit.SECOND second
TimeframeUnit.MINUTE minute
TimeframeUnit.DAY day
TimeframeUnit.WEEK week
TimeframeUnit.MONTH month

DataContext

DataContext(timeframe, timezone)

Describe the existing market-data timeframe and shared timezone for charts, strategy calendars, and statistics.

Attribute Type Meaning
timeframe Timeframe Declared Timeframe of the supplied bars.
timezone str Timezone used to display/group aware timestamps.

DataContext.snapshot

DataContext.snapshot()

Return independent plain values without reusing mutable request objects.

Specify asset quantity and price rules

from openbacktesting.broker import InstrumentSpec

instrument = InstrumentSpec(symbol="ASSET", currency="USD",
                            quantity_step=0.001, min_quantity=0.001,
                            min_notional=5, price_tick=0.01)

Pass instrument=instrument to the run. qty=0.1 remains one tenth of a unit; prices and indicators are not scaled. Defaults use whole units (quantity_step=1), minimum quantity equal to the step, no price tick, and no minimum notional. Direct quantities and all supplied prices must match configured grids; budget-based sizing can round quantity toward zero. Price grids also apply to OHLC data. Labels do not perform currency conversion.

With an explicit price_tick, use instrument.round_price_to_tick(price) to obtain a grid-aligned price before setting protection. quantity(value, floor=True) can round units; validate_data(frame) checks the data against instrument rules.

InstrumentSpec

broker.InstrumentSpec(
    symbol='asset',
    quantity_step=1,
    min_quantity=None,
    price_tick=None,
    min_notional=0,
    currency=None,
)

Actual asset quantity and price grids; currency is only a label.

Attribute Type Meaning
symbol str Asset label; does not fetch market data.
quantity_step float Minimum increment in asset units.
min_quantity float \| None Minimum quantity for opening exposure.
price_tick float \| None Optional price increment; None leaves prices ungridded.
min_notional float Minimum opening notional in settlement currency.
currency str \| None Settlement-currency label; no exchange-rate conversion.

InstrumentSpec.price

InstrumentSpec.price(value, *, side=None)

Validate requested prices strictly; normalize execution prices in the adverse direction.

InstrumentSpec.quantity

InstrumentSpec.quantity(value, *, floor=False, opening=True)

Direct requests must lie on the grid; budgeted quantities may round toward zero.

InstrumentSpec.validate_data

InstrumentSpec.validate_data(frame)

Trading market data must be ordered, unique, and have valid prices; explicit ticks must not be silently corrected.

InstrumentSpec.round_price_to_tick

InstrumentSpec.round_price_to_tick(value)

Round prices to the nearest explicit tick; half ticks round to the higher price.

Backtest · Indicator handles · User manual