title: LineRoot API Reference description: Base class for line-based time-series data structures
LineRoot API Reference¶
LineRoot is the foundational base class for all line-based objects in Backtrader. It provides the core interface for time-series data management, period handling, and operator overloading for arithmetic and comparison operations.
Class Hierarchy¶
classDiagram
LineRoot <|-- LineSingle
LineRoot <|-- LineMultiple
LineRootMixin <|-- LineRoot
BaseMixin <|-- LineRoot
LineSingle <|-- LineBuffer
LineMultiple <|-- LineSeries
LineMultiple <|-- Indicator
LineMultiple <|-- Strategy
LineBuffer <|-- LinesOperation
LineBuffer <|-- LineOwnOperation
class LineRoot {
<<abstract>>
- _minperiod: int
- _opstage: int
- _OwnerCls: type
- IndType: int
- StratType: int
- ObsType: int
- prenext()
- nextstart()
- next()
- preonce()
- once()
- setminperiod()
- updateminperiod()
- qbuffer()
- minbuffer()
}
class LineRootMixin {
<<mixin>>
- donew()
- _owner: object
}
class LineSingle {
- addminperiod()
- incminperiod()
}
class LineMultiple {
- lines: Lines
- _ltype: int
- _clock: object
- _lineiterators: dict
- reset()
- size()
}
class LineBuffer {
- array: array
- _idx: int
- mode: int
- home()
- forward()
- rewind()
- __getitem__()
- __setitem__()
}
```bash
## Core Concepts
### Line System Overview
The Line system is Backtrader's core data structure for time-series manipulation:
- **Index 0 always points to the current value**- No need to track indices manually
- **Positive indices access past values**- `data.close[-1]` is the previous bar
- **Negative indices access future values**- Used in specific scenarios like replay
- **Automatic period management**- Objects wait for minimum data before calculating
### Operation Stages
LineRoot implements a two-stage operation system:
- **Stage 1 (`_opstage = 1`)**: Construction phase - Creates lazy evaluation objects
- **Stage 2 (`_opstage = 2`)**: Execution phase - Returns actual values during backtesting
## Class Attributes
### `_minperiod`
```python
obj._minperiod # int: Minimum periods needed before valid output
```bash
The minimum number of bars required before the object produces valid output.
```python
# SMA with period 20 needs 20 bars
sma = bt.indicators.SMA(period=20)
print(sma._minperiod) # 20
```bash
### `_opstage`
```python
obj._opstage # int: Current operation stage (1 or 2)
```bash
Controls whether operations create objects (stage 1) or return values (stage 2).
### `_OwnerCls`
```python
obj._OwnerCls # type: Expected owner class type
```bash
Specifies the class type that should own this object. Used by `findowner()`.
### Type Constants
```python
LineRoot.IndType # 0 - Indicator type
LineRoot.StratType # 1 - Strategy type
LineRoot.ObsType # 2 - Observer type
```bash
Used to identify object types in the line hierarchy.
## Period Management Methods
### `setminperiod()`
```python
obj.setminperiod(minperiod: int) -> None
```bash
Directly set the minimum period requirement.
- *Parameters:**
- `minperiod`: Minimum number of bars needed
- *Use Case:** Override indicator requirements in strategies:
```python
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = bt.indicators.SMA(period=50)
# Don't wait for full SMA - start after 20 bars
self.sma.setminperiod(20)
```bash
### `updateminperiod()`
```python
obj.updateminperiod(minperiod: int) -> None
```bash
Update minimum period to the maximum of current and provided value.
- *Parameters:**
- `minperiod`: Proposed minimum period
- *Example:** Used internally when indicators chain:
```python
class MyIndicator(bt.Indicator):
def __init__(self):
self.sma1 = bt.indicators.SMA(period=10)
self.sma2 = bt.indicators.SMA(period=20)
# _minperiod automatically becomes max(10, 20) = 20
```bash
### `addminperiod()`
```python
obj.addminperiod(minperiod: int) -> None
```bash
Add to minimum period (with overlap adjustment). Subtracts 1 to account for overlapping periods.
- *Note:** Implementation differs between `LineSingle` and `LineMultiple`.
### `incminperiod()`
```python
obj.incminperiod(minperiod: int) -> None
```bash
Increment minimum period without considerations (no overlap adjustment).
## Execution Phase Methods
### `prenext()`
```python
obj.prenext() -> None
```bash
Called during the minimum period phase when not enough data is available yet.
- *Override** to customize pre-period behavior:
```python
class MyIndicator(bt.Indicator):
def prenext(self):
# Called each bar until minperiod is reached
print(f"Accumulating data: {len(self)} bars")
```bash
### `nextstart()`
```python
obj.nextstart() -> None
```bash
Called once when minimum period is first satisfied, before normal `next()` calls.
- *Default:** Automatically calls `next()`.
- *Override** for special startup behavior:
```python
def nextstart(self):
print(f"Indicator ready! First valid value: {self.line[0]}")
self.next() # Continue with normal next()
```bash
### `next()`
```python
obj.next() -> None
```bash
Called for each bar after minimum period is satisfied.
- *Override** to implement calculation logic:
```python
def next(self):
self.line[0] = self.data.close[0] *2
```bash
### `preonce()` and `once()`
```python
obj.preonce(start: int, end: int) -> None
obj.once(start: int, end: int) -> None
```bash
Called during vectorized (`once`) mode for batch processing.
- *Parameters:**
- `start`: Starting index
- `end`: Ending index
- *Override** for optimized calculations:
```python
def once(self, start, end):
# Vectorized calculation from start to end
for i in range(start, end):
self.line.array[i] = self.data.close.array[i] *2
```bash
### `oncestart()`
```python
obj.oncestart(start: int, end: int) -> None
```bash
Called once in `once` mode when minimum period is first satisfied.
## Arithmetic Operators
LineRoot implements operator overloading for creating lazy-evaluated operations:
### Basic Arithmetic
```python
# Addition
result = data.close + data.open
result = data.close + 10
result = 10 + data.close # __radd__
# Subtraction
result = data.close - data.open
result = data.close - 10
result = 10 - data.close # __rsub__
# Multiplication
result = data.close* 2
# Division
result = data.close / 2
# Power
result = data.close ** 2
# Absolute value
result = abs(-data.close)
# Negation
result = -data.close
```bash
### Comparison Operators
```python
# Comparisons create boolean line operations
cross_up = data.close > data.high
cross_down = data.close < data.low
equals = data.close == data.open
not_equals = data.close != data.open
```bash
## Line Management
### `lines` Attribute (LineMultiple)
```python
obj.lines # Lines collection
```bash
Container for all line objects in a multi-line object.
```python
# Access lines by index
first_line = obj.lines[0]
# Access lines by name (if aliased)
close_line = data.lines.close
# Get number of lines
num_lines = len(obj.lines)
```bash
### `linealias` Descriptor
Provides named access to lines:
```python
class MyIndicator(bt.Indicator):
lines = ('signal', 'trend')
# Access via alias
indicator.lines.signal[0]
indicator.lines.trend[0]
```bash
### `size()`
```python
obj.size() -> int
```bash
Return the number of lines in this object.
```python
data = bt.feeds.YahooFinanceData(dataname='AAPL')
print(data.size()) # Number of data lines (OHLCV)
```bash
## Buffer Management
### `qbuffer()`
```python
obj.qbuffer(savemem: int = 0) -> None
```bash
Change lines to implement minimum-size queue buffer scheme for memory efficiency.
- *Parameters:**
- `savemem`: Memory savings level (0 = normal, higher = more aggressive)
```python
# Apply to all data feeds for large backtests
for data in cerebro.datas:
data.qbuffer(savemem=1)
```bash
### `minbuffer()`
```python
obj.minbuffer(size: int) -> None
```bash
Notify object of minimum buffer size requirement.
## Owner Relationships
### `_owner` Attribute
```python
obj._owner # Reference to owning object
```bash
Set automatically via `findowner()` during construction.
### Owner Finding (LineRootMixin)
```python
@classmethod
def donew(cls, *args, **kwargs):
"""Create instance with owner finding logic"""
```bash
The `LineRootMixin.donew()` method:
1. Creates the object instance
2. Calls `metabase.findowner()` to locate the owner in the call stack
3. Sets `_owner` attribute
### Owner Types
| Object Type | Typical Owner |
|-------------|---------------|
| Indicator | Strategy or another Indicator |
| Observer | Strategy or Cerebro |
| Analyzer | Strategy |
| Data Feed | Cerebro |
## Type Constants
```python
# Object type identification
LineRoot.IndType # 0 - For indicators
LineRoot.StratType # 1 - For strategies
LineRoot.ObsType # 2 - For observers
```bash
Used in `_ltype` attribute to identify object type:
```python
if obj._ltype == LineRoot.IndType:
# This is an indicator
pass
```bash
## Line Access Patterns
### Current Value (Index 0)
```python
# Current bar's close price
current_close = data.close[0]
# Current indicator value
current_sma = self.sma[0]
```bash
### Past Values (Positive Indices)
```python
# Previous close
prev_close = data.close[-1]
# Close 5 bars ago
close_5_ago = data.close[-5]
```bash
### Setting Values
```python
# In indicator's next()
self.signal[0] = 1 if data.close[0] > data.close[-1] else -1
```bash
### Checking Data Availability
```python
def next(self):
# Always check length before accessing past values
if len(self.data) >= 2:
change = self.data.close[0] - self.data.close[-1]
```bash
## LineSingle vs LineMultiple
### LineSingle
Base for single-line objects like `LineBuffer`:
```python
class LineSingle(LineRoot):
def addminperiod(self, minperiod):
self._minperiod += minperiod - 1
def incminperiod(self, minperiod):
self._minperiod += minperiod
```bash
### LineMultiple
Base for multi-line objects like `Indicator`, `Strategy`:
```python
class LineMultiple(LineRoot):
def __init__(self):
self._ltype = None
self.lines = Lines()
self._clock = None
self._lineiterators = {}
def reset(self):
self._stage1()
self.lines.reset()
```bash
## Operation Methods (Internal)
### `_stage1()` / `_stage2()`
```python
obj._stage1() # Set operation stage to 1 (construction)
obj._stage2() # Set operation stage to 2 (execution)
```bash
### `_operation()`
```python
obj._operation(other, operation, r=False, intify=False)
```bash
Internal method for two-operand operations.
### `_operationown()`
```python
obj._operationown(operation)
```bash
Internal method for single-operand operations.
## Boolean Context
```python
# LineRoot objects support boolean evaluation
if data.close > data.close[-1]:
# This works because __bool__ checks current value
pass
# Direct evaluation
if self.cross:
# True if cross line has non-zero value
pass
```bash
## Usage Examples
### Creating a Custom Indicator
```python
import backtrader as bt
class PriceChange(bt.Indicator):
lines = ('change', 'pct_change')
def __init__(self):
# Requires at least 2 bars
self.addminperiod(2)
def next(self):
self.lines.change[0] = self.data.close[0] - self.data.close[-1]
if self.data.close[-1] != 0:
self.lines.pct_change[0] = (
self.lines.change[0] / self.data.close[-1] * 100
)
```bash
### Using Period Management
```python
class AdaptiveStrategy(bt.Strategy):
def __init__(self):
self.fast_sma = bt.indicators.SMA(period=10)
self.slow_sma = bt.indicators.SMA(period=50)
# Override: don't wait for slow SMA
self.setminperiod(10) # Start when fast SMA is ready
def next(self):
if len(self) >= self.slow_sma._minperiod:
# Both SMAs are ready
pass
```bash
### Line Operations
```python
class Momentum(bt.Indicator):
lines = ('momentum',)
def __init__(self, period=14):
# Using arithmetic operators creates line operations
self.lines.momentum = self.data.close - self.data.close(-period)
```bash
## Performance Considerations
1. **Minimize `len()` calls in hot paths**- Cache lengths when possible
2.**Use `once()` mode for vectorized operations**- Faster than `next()`
3.**Use `qbuffer()` for long backtests**- Reduces memory usage
4.**Avoid deep indicator nesting** - Each level adds overhead
## Common Patterns
### Cross Detection
```python
class CrossOver(bt.Indicator):
lines = ('cross',)
def __init__(self):
self.lines.cross = bt.indicators.CrossOver(
self.data.close, self.data.close(-1))
```bash
### Multi-Line Indicator
```python
class BollingerBands(bt.Indicator):
lines = ('mid', 'top', 'bot')
params = (('period', 20), ('devfactor', 2.0))
def __init__(self):
self.lines.mid = bt.indicators.SMA(self.data, period=self.p.period)
self.lines.top = self.lines.mid + self.p.devfactor *bt.indicators.StandardDeviation(self.data, period=self.p.period)
self.lines.bot = self.lines.mid - self.p.devfactor* bt.indicators.StandardDeviation(self.data, period=self.p.period)
```bash
## Related Classes
- **LineBuffer**: Circular buffer storage for single lines
- **LineSeries**: Multi-line time-series collections
- **LineIterator**: Base for iteration logic (indicators, strategies)
- **Lines**: Container class for multiple line objects
- **LineAlias**: Descriptor for named line access
## Source Files
- `backtrader/lineroot.py`: Core LineRoot implementation
- `backtrader/linebuffer.py`: LineBuffer (single line storage)
- `backtrader/lineseries.py`: LineSeries (multi-line collections)
- `backtrader/lineiterator.py`: LineIterator (iteration logic)
- `backtrader/metabase.py`: BaseMixin and owner finding