title: LineBuffer API description: Complete LineBuffer class API reference for time-series data storage and circular buffer operations


LineBuffer API

The LineBuffer class is the core data structure for storing time-series data in Backtrader. It implements a circular buffer with an innovative indexing scheme where index 0 always points to the current active value, enabling intuitive data access without explicit index tracking. This ~1950-line implementation provides the foundation for all data feeds, indicators, and strategy calculations.

Class Definition

class backtrader.LineBuffer(LineSingle, LineRootMixin):
    """Circular buffer for time-series data with index-0-current semantics."""

```bash

## Core Architecture

### Circular Buffer Design

LineBuffer uses a circular buffer implementation with a unique indexing approach:

- **Index 0**always points to the current (active) value
- **Positive indices**fetch past values (left-hand side): `[1]` = next future bar
- **Negative indices** fetch future values (right-hand side): `[-1]` = previous bar

```mermaid
graph TD
    subgraph "LineBuffer Memory Layout"
        A["Physical Array: [v0, v1, v2, v3, v4, v5, ...]"]
        B["Logical View: [..., -2, -1, 0, 1, 2, ...]"]
    end

    C["_idx: Current position in physical array"]
    D["lencount: Number of logical bars"]

    C -->|"points to"| A

    D -->|"represents"| B

    E["Buffer Operations"] --> F["forward: advance _idx"]
    E --> G["backwards: rewind _idx"]
    E --> H["home: reset to beginning"]

    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#f0e1ff
    style D fill:#f0e1ff

```bash

### Memory Layout

```python

# Example: 5 bars of data, current position at bar 3

# Physical array:  [10.0, 11.0, 12.0, 13.0, 14.0]

# idx=0   idx=1   idx=2   idx=3   idx=4

# Logical access:

# data[0]  = 13.0  (current bar, _idx=3)

# data[-1] = 12.0  (previous bar, _idx-1=2)

# data[-2] = 11.0  (2 bars ago, _idx-2=1)

# data[1]  = 14.0  (next bar, _idx+1=4) - if extended

```bash

## Buffer Modes

### UnBounded Mode (Default)

Stores all data from beginning to end. Memory usage grows linearly with data length.

```python
buf = LineBuffer()

# buf.mode == LineBuffer.UnBounded (0)

# All historical data is kept

```bash

- *Use Case**: Standard backtesting, when memory is not constrained.

### QBuffer Mode (Queued Buffer)

Memory-efficient mode that keeps only the most recent `maxlen` values.

```python
buf = LineBuffer()
buf.qbuffer(savemem=1, extrasize=0)  # Keep maxlen most recent values

# buf.mode == LineBuffer.QBuffer (1)

```bash

- *Parameters**:
- `savemem`: Enable cache mode (>0 enables)
- `extrasize`: Extra slots for resampling/replay operations

- *Use Case**: Long backtests with many indicators to reduce memory footprint.

### minbuffer Method

Ensures minimum buffer size for data access requirements.

```python
buf.minbuffer(size=100)  # Ensure at least 100 slots available

```bash

## Core Attributes

| Attribute | Type | Description |

|-----------|------|-------------|

| `_idx` | int | Current logical position in buffer (-1 when empty) |

| `lencount` | int | Number of bars stored (length of buffer) |

| `array` | array.array | Internal storage (type 'd' for double) |

| `_minperiod` | int | Minimum period before valid values |

| `mode` | int | Buffer mode (UnBounded=0, QBuffer=1) |

| `maxlen` | int | Maximum length in QBuffer mode |

| `extension` | int | Size of lookahead extension |

| `bindings` | list | Bound lines for value propagation |

| `_clock` | object | Time reference for synchronization |

## Index Operations

### `idx` Property

Get or set the current index position with QBuffer awareness.

```python
current_idx = buf.idx  # Get current position

buf.idx = new_idx      # Set new position

```bash
In QBuffer mode at `lenmark`, the index stays at 0 unless `force=True`.

### `get_idx() / set_idx(idx, force=False)`

Lower-level index control.

```python
buf.set_idx(10)           # Normal set

buf.set_idx(10, force=True)  # Force set even in QBuffer at lenmark

```bash

## Data Access

### `__getitem__(ago)`

Primary method for data access with relative indexing.

```python

# Current value

value = buf[0]

# Historical values (negative indices)

prev = buf[-1]     # 1 bar ago

prev_5 = buf[-5]   # 5 bars ago

# Future values (positive indices, if extended)

next_val = buf[1]  # Next bar

```bash

- *Performance**: Hot path optimized with pre-calculated indices and fast NaN detection.

### `get(ago=0, size=1)`

Get a slice of values relative to current position.

```python

# Get last 5 values including current

last_5 = buf.get(ago=0, size=5)  # [t-4, t-3, t-2, t-1, t]

# Get 3 values starting from 2 bars ago

slice_vals = buf.get(ago=-2, size=3)  # [t-4, t-3, t-2]

```bash

### `getzero(idx=0, size=1)`

Get slice relative to physical array start.

```python

# Get first 10 values from physical start

first_10 = buf.getzero(idx=0, size=10)

# Get values from specific physical index

middle_vals = buf.getzero(idx=50, size=10)

```bash

### `getzeroval(idx=0)`

Get single value from physical array index.

```python
val = buf.getzeroval(100)  # Get value at physical index 100

```bash

### `plot(idx=0, size=None)`

Get all data for plotting, defaulting to entire buffer.

```python

# Get all data for plotting

all_data = buf.plot()

# Get specific range

range_data = buf.plot(idx=10, size=100)

```bash

### `plotrange(start, end)`

Get a specific slice of the buffer.

```python
subset = buf.plotrange(100, 200)  # Indices 100-199

```bash

## Data Modification

### `__setitem__(ago, value)`

Set value at relative position with binding propagation.

```python
buf[0] = 100.0       # Set current value

buf[-1] = 99.0       # Modify previous value

```bash

- *Features**:
- Automatic array expansion for out-of-bounds write
- NaN/None handling with default values
- Binding execution for synchronized lines
- Datetime line validation (values >= 1.0)

### `set(value, ago=0)`

Explicit set method with same behavior as `__setitem__`.

```python
buf.set(100.0, ago=0)  # Equivalent to buf[0] = 100.0

```bash

## Buffer Navigation

### `home()`

Reset to beginning state.

```python
buf.home()

# After: buf.idx = -1, buf.lencount = 0

# Buffer content preserved, use buflen() for actual size

```bash

### `forward(value=NAN, size=1)`

Advance buffer by specified number of positions.

```python

# Advance one position

buf.forward()

# Advance multiple positions

buf.forward(size=10)

# Advance with default value

buf.forward(value=0.0, size=5)

```bash

- *Behavior**:
- Increases `_idx` and `lencount`
- Appends new values to array
- Respects clock synchronization for non-indicators

### `backwards(size=1, force=False)`

Rewind buffer, reducing size.

```python

# Rewind one position

buf.backwards()

# Rewind multiple positions

buf.backwards(size=10)

# Force rewind regardless of minperiod

buf.backwards(size=5, force=True)

```bash

### `rewind(size=1)`

Decrease idx and lencount without modifying array.

```python
buf.rewind(5)  # Logical rewind only

```bash

### `advance(size=1)`

Increase idx and lencount without modifying array.

```python
buf.advance(5)  # Logical advance only

```bash

### `extend(value=float('nan'), size=0)`

Extend buffer for lookahead operations.

```python

# Extend buffer for 5 future bars

buf.extend(size=5)

# Extend with specific default value

buf.extend(value=0.0, size=10)

```bash

## Buffer Information

### `__len__()`

Return logical length (lencount).

```python
length = len(buf)  # Returns buf.lencount

```bash

- *Performance**: Direct attribute access, optimized hot path.

### `buflen()`

Return actual physical buffer size.

```python
physical_size = buf.buflen()

# Returns: len(buf.array) - buf.extension

```bash
Difference from `len()`:

- `len()`: Logical bars processed
- `buflen()`: Physical storage capacity minus extension

## Bindings

### `addbinding(binding)`

Add another LineBuffer for synchronized value updates.

```python
buf1 = LineBuffer()
buf2 = LineBuffer()
buf1.addbinding(buf2)

# Now setting buf1[0] also sets buf2[0]

buf1[0] = 100.0

# buf2[0] is now also 100.0

```bash

### `bind2lines(binding)`

Bind to a line by name or index.

```python

# Bind by name

buf.bind2lines('close')

# Bind by index

buf.bind2lines(0)

```bash

### `oncebinding()`

Execute all bindings in runonce mode.

```python

# Called internally during batch processing

buf.oncebinding()

```bash

## Line Delay Operations

### `__call__(ago=None)`

Create delayed line objects for time-shifted access.

```python

# Get current value

current = buf()  # Same as buf[0]

# Create delayed line (lookback)

delayed = buf(-5)  # Returns _LineDelay object

# delayed[0] now gives buf[-5]

# Create forwarded line (lookahead)

forwarded = buf(5)  # Returns _LineForward object

```bash

## Datetime Operations

### `datetime(ago=0, tz=None, naive=True)`

Get datetime value at specified offset.

```python

# Current datetime

dt = buf.datetime()

# Datetime 5 bars ago with timezone

from pytz import UTC
dt = buf.datetime(ago=-5, tz=UTC, naive=False)

# Cached access (fast path for ago=0, tz=None)

dt = buf.datetime()

```bash

- *Features**:
- Caching for common case (ago=0, tz=None)
- Fallback to default datetime for NaN/None
- Timezone support via `tz` parameter

### `date(ago=0, tz=None, naive=True)`

Get date component of datetime value.

```python
date_obj = buf.date()

# Returns datetime.date object

```bash

### `time(ago=0, tz=None, naive=True)`

Get time component of datetime value.

```python
time_obj = buf.time()

# Returns datetime.time object

```bash

### `dt(ago=0)`

Shorthand for `datetime()`.

```python
dt = buf.dt()  # Same as buf.datetime()

```bash

### `tm(ago=0)`, `tm_raw(ago=0)`

Get struct_time for strftime formatting.

```python
tm = buf.tm()       # Naive timezone

tm = buf.tm_raw()   # With timezone info

```bash

## Time Comparison Methods

### `tm_lt/le/eq/gt/ge(other, ago=0)`

Compare time values with another line.

```python
if buf.tm_eq(other_line):

# Same time
    pass

if buf.tm_lt(other_line, ago=-1):

# This buffer's previous time is earlier
    pass

```bash

## Performance Optimizations

### Pre-calculated Flags

```python

# Initialization-time calculations

self._is_indicator = ...      # Cached indicator check

self._is_datetime_line = ...  # Cached datetime line check

self._default_value = ...     # Cached default value

```bash

- *Benefit**: Eliminates repeated `hasattr` and `isinstance` calls in hot paths.

### Fast NaN Detection

```python
def _is_nan_or_none(value):
    return value is None or value != value  # NaN != NaN

```bash

- *Benefit**: 10x faster than `math.isnan(value)`.

### Direct Attribute Access

```python

# Before (with hasattr checks)

if hasattr(self, '_idx'):
    idx = self._idx

# After (guaranteed by __init__)

idx = self._idx

```bash

- *Benefit**: Eliminates dictionary lookup overhead.

### Slice Deletion

```python

# Batch deletion instead of loop

del arr[max(0, arr_len - size):]  # Single operation

# vs

for _ in range(size): arr.pop()   # Multiple operations

```bash

- *Benefit**: O(n) vs O(n*size) for backward operations.

### Performance Comparison

| Operation | Before Optimization | After Optimization | Improvement |

|-----------|---------------------|-------------------|-------------|

| `__len__()` | 0.611s | ~0.05s | 92% faster |

| NaN check | isinstance + isnan | `value != value` | 10x faster |

| Index access | hasattr + dict lookup | Direct attribute | 3-5x faster |

| backwards | Loop pop | Slice delete | 5-10x faster |

## exactbars Parameter Effects

The `exactbars` parameter in Cerebro controls memory usage:

| Value | Memory Mode | Behavior |

|-------|-------------|----------|

| `False` or `0` | Standard | All data kept, preload/runonce enabled |

| `True` or `1` | Minimum | Only current bar kept, disables preload/runonce/plotting |

| `-1` | Moderate | Data/indicators kept, sub-indicator internals discarded |

| `-2` | Selective | Strategy-level data kept, unused sub-indicators discarded |

```python

# Example in Cerebro

cerebro = bt.Cerebro(exactbars=-1)

```bash

- *Effects on LineBuffer**:
- Controls `qbuffer()` activation
- Affects `minbuffer()` behavior
- Influences preload/runonce mode selection

## Line Subclasses

### LineActions

Base class for multi-line objects (indicators, observers).

```python
class LineActions(LineBuffer, LineActionsMixin, ParamsMixin):
    """Multi-line container with parameter support."""

```bash

- *Key Features**:
- Multiple output lines
- Parameter management
- Auto-assignment of data feeds
- Indicator registration

### LinesOperation

Binary operations between line objects.

```python
result = LinesOperation(line1, line2, operator.sub)

# result[0] = line1[0] - line2[0]

```bash

- *Features**:
- Element-wise binary operations
- Reverse operation support
- Parent indicator tracking for runonce

### LineOwnOperation

Unary operations on line objects.

```python
result = LineOwnOperation(line, operator.neg)

# result[0] = -line[0]

```bash

- *Features**:
- Element-wise unary operations
- Optimized once() implementation

### _LineDelay / _LineForward

Time-shifted line access.

```python
delayed = buf(-5)  # _LineDelay for lookback

forwarded = buf(5)  # _LineForward for lookahead

```bash

## Usage Examples

### Basic Buffer Operations

```python
import backtrader as bt

# Create and initialize buffer

buf = bt.LineBuffer()
buf.home()  # Reset to beginning

# Add data

for i in range(10):
    buf.forward()
    buf[0] = float(i *10)

# Access data

print(buf[0])    # Current: 90.0

print(buf[-1])   # Previous: 80.0

print(buf[-5])   # 5 bars ago: 50.0

# Get slice

last_5 = buf.get(ago=0, size=5)  # [50.0, 60.0, 70.0, 80.0, 90.0]

```bash

### QBuffer Mode for Memory Efficiency

```python

# Create indicator with QBuffer mode

class MyIndicator(bt.Indicator):
    lines = ('value',)
    params = (('period', 20),)

    def __init__(self):

# Enable QBuffer mode for memory efficiency
        self.line.qbuffer(savemem=self.p.period)

    def next(self):

# Only last 'period' values kept in memory
        self.lines.value[0] = sum(self.data.get(ago=0, size=self.p.period)) / self.p.period

```bash

### Creating Delayed Lines

```python
class MyStrategy(bt.Strategy):
    def __init__(self):

# Create delayed lines
        self.close_5 = self.data.close(-5)  # Close 5 bars ago
        self.close_20 = self.data.close(-20)  # Close 20 bars ago

    def next(self):

# Access delayed values
        if self.data.close[0] > self.close_5[0]*1.02:

# Price increased more than 2% in 5 bars
            self.buy()

```bash

### Line Operations

```python
class MyStrategy(bt.Strategy):
    def __init__(self):

# Binary operations create LinesOperation objects
        self.price_range = self.data.high - self.data.low
        self.avg_price = (self.data.open + self.data.close) / 2

# Unary operations create LineOwnOperation objects
        self.neg_close = -self.data.close
        self.abs_change = abs(self.data.close - self.data.open)

    def next(self):

# Use computed lines
        if self.price_range[0] > self.avg_price[0]* 0.05:

# High volatility day
            pass

```bash

## Memory Management Best Practices

### 1. Use QBuffer for Long Backtests

```python
class MyStrategy(bt.Strategy):
    def __init__(self):

# Enable QBuffer for indicators that don't need full history
        self.sma = bt.indicators.SMA(self.data.close, period=20)
        self.sma.line.qbuffer(savemem=20)  # Keep only 20 values

```bash

### 2. Leverage exactbars Parameter

```python

# For memory-constrained long backtests

cerebro = bt.Cerebro(exactbars=-1)  # Keep data/indicators, discard sub-indicator internals

```bash

### 3. Use get() for Slices

```python

# Efficient: single slice operation

last_10 = data.close.get(ago=0, size=10)

# Avoid: multiple individual accesses

last_10 = [data.close[i] for i in range(-9, 1)]

```bash

## Advanced Topics

### Custom Buffer Implementation

```python
class CustomBuffer(bt.LineBuffer):
    def __init__(self):
        super().__init__()
        self.custom_attr = None

    def forward(self, value=float('nan'), size=1):

# Custom forward logic
        super().forward(value, size)

# Additional processing

```bash

### Binding Multiple Lines

```python

# Synchronize multiple lines

primary = bt.LineBuffer()
secondary1 = bt.LineBuffer()
secondary2 = bt.LineBuffer()

primary.addbinding(secondary1)
primary.addbinding(secondary2)

# One update affects all

primary[0] = 100.0

# secondary1[0] == 100.0

# secondary2[0] == 100.0

```bash

### Runonce Mode Compatibility

LineBuffer supports both per-bar (`next()`) and batch (`once()`) calculation modes:

```python
def once(self, start, end):

# Batch processing for performance
    dst = self.array
    src = self.data.array

    for i in range(start, end):
        dst[i] = self._calculate(src, i)

```bash

## See Also

- [Indicator API](indicator.md) - Using LineBuffer in indicators
- [Data Feeds API](data-feeds.md) - LineBuffer in data sources
- [Strategy API](strategy.md) - Accessing data in strategies
- [LineSeries Documentation](../user_guide/basic_concepts.md) - Line system concepts