title: LineSeries Time Series API description: Complete Backtrader LineSeries API Reference
LineSeries Time Series API¶
LineSeries is the core class for managing multi-line time-series data in Backtrader. It provides a unified time-series data access interface for data feeds, indicators, observers, and more, supporting historical data access, slicing operations, pandas conversion, and more.
Class Hierarchy¶
LineRoot (base class for all line objects)
LineSingle (single line object)
LineBuffer (circular buffer implementation)
LineMultiple (multiple line object)
LineSeries (multi-line time series)
Indicator (technical indicator base class)
DataSeries (data feed base class)
Strategy (strategy base class)
```bash
## Core Concepts
### Line (线条)
Line is the basic unit for storing time-series data in Backtrader. It uses a circular buffer implementation with the following characteristics:
- **Index 0 always points to the current value**: The latest data value is always at index 0
- **Positive indices access historical data**: `data[-1]` gets the previous value, `data[-2]` gets the value before that
- **Negative indices access future data**: `data[1]` can access the next value in replay or live scenarios
- **Automatic memory management**: Control memory usage through qbuffer mode
### Time Index Pattern
```bash
Historical Data Current Future Data
| | |
v v v
... [-3] [-2] [-1] [0] [1] [2] ...
Previous Bar Current Bar
```bash
## LineSeries Class
### Class Definition
```python
class backtrader.LineSeries(LineMultiple, LineSeriesMixin, ParamsMixin):
"""Base class for objects managing multiple time-series lines."""
```bash
### Core Attributes
| Attribute | Type | Description |
|-----------|------|-------------|
| `lines` | Lines | Container object storing all LineBuffer instances |
| `plotinfo` | PlotInfoObj | Plotting configuration object |
| `plotlines` | PlotLinesObj | Line plotting configuration |
| `csv` | bool | Whether CSV export is supported |
### Line Operation Attributes
| Attribute | Return Value | Description |
|-----------|--------------|-------------|
| `array` | array | Underlying array of the first line |
| `line` | LineBuffer | First line (shortcut for single-line indicators) |
| `l` | Lines | Alias for lines |
## Time Series Operations
### Data Access
```python
class MyStrategy(bt.Strategy):
def next(self):
# Current value (index 0)
current_close = self.data.close[0]
current_sma = self.sma[0]
# Historical values (negative indices)
prev_close = self.data.close[-1] # Previous bar
prev_close_2 = self.data.close[-2] # Two bars ago
prev_close_5 = self.data.close[-5] # Five bars ago
# Future values (positive indices, valid only in replay/live scenarios)
# next_close = self.data.close[1]
```bash
### Data Length
```python
def next(self):
# Get data length (number of loaded bars)
current_bar = len(self.data)
total_bars = len(self)
# Check if there's enough historical data
if len(self.data) >= 20:
# Can calculate 20-period indicator
pass
```bash
### Time Operations
```python
def next(self):
# Get current bar time (multiple methods)
dt_num = self.data.datetime[0] # Internal numeric format
dt = self.data.datetime.datetime(0) # datetime object
dt_date = self.data.datetime.date(0) # date object
dt_time = self.data.datetime.time(0) # time object
# Previous bar time
prev_dt = self.data.datetime.datetime(-1)
```bash
## Data Access Pattern Table
| Expression | Meaning | Use Case |
|------------|---------|----------|
| `data[0]` | Current bar value | Get latest data |
| `data[-1]` | Previous bar value | Get last historical value |
| `data[-n]` | n bars ago value | Get value n periods ago |
| `data[1]` | Next bar value | Future values in replay/live scenarios |
| `len(data)` | Data length | Get number of loaded bars |
| `data.array` | Underlying array | Direct access to complete data |
## Slicing and Indexing
### get() Method
Get a slice of data at a specified position and size:
```python
def next(self):
# Get the last 3 bars' close prices
# Returns: [close[-2], close[-1], close[0]]
recent_3 = self.data.close.get(ago=0, size=3)
# Get the last 5 bars from current position
last_5 = self.data.close.get(ago=-4, size=5)
# Usage
avg_price = sum(recent_3) / len(recent_3)
```bash
### Slicing Operations
```python
def next(self):
# Get array slice (based on internal array index)
# Note: This directly operates on the underlying array
array_data = self.data.close.array
# Common pattern: Get the most recent N values
recent_values = array_data[-self.p.period:]
```bash
## Alignment and Synchronization
### Multiple Data Source Synchronization
When using multiple data sources, Backtrader automatically aligns them:
```python
cerebro = bt.Cerebro()
# Add multiple data sources
cerebro.adddata(daily_data, name='daily')
cerebro.adddata(weekly_data, name='weekly')
class MyStrategy(bt.Strategy):
def next(self):
# Data sources are automatically aligned by date
# When weekly data has a new bar, next() is called for both
daily_len = len(self.data0) # Daily data length
weekly_len = len(self.data1) # Weekly data length
# Access different data sources
if self.data0.close[0] > self.data1.close[0]:
self.buy(data=self.data0)
```bash
### Data Source Access Methods
```python
class MyStrategy(bt.Strategy):
def __init__(self):
# Method 1: Access by index
self.data0 = self.datas[0]
self.data1 = self.datas[1]
# Method 2: Access by name (requires name to be set)
self.daily = self.getdatabyname('daily')
self.weekly = self.getdatabyname('weekly')
```bash
## Period and Timeframe Handling
### TimeFrame Constants
```python
# TimeFrame definitions
TimeFrame.Ticks # 1 - Tick data
TimeFrame.MicroSeconds # 2 - Microseconds
TimeFrame.Seconds # 3 - Seconds
TimeFrame.Minutes # 4 - Minutes
TimeFrame.Days # 5 - Days
TimeFrame.Weeks # 6 - Weeks
TimeFrame.Months # 7 - Months
TimeFrame.Years # 8 - Years
TimeFrame.NoTimeFrame # 9 - No timeframe
```bash
### Getting Data Source Timeframe
```python
class MyStrategy(bt.Strategy):
def next(self):
# Get timeframe type
tf = self.data._timeframe
comp = self.data._compression
# Determine data type
if tf == bt.TimeFrame.Days:
if comp == 1:
print("Daily data")
elif comp == 7:
print("Weekly data (7-day compression)")
```bash
### TimeFrame Methods
```python
# Get timeframe name
name = bt.TimeFrame.getname(bt.TimeFrame.Days)
# Returns: 'Day'
name = bt.TimeFrame.getname(bt.TimeFrame.Minutes, 5)
# Returns: 'Minutes'
# Get name from constant
name = bt.TimeFrame.TName(bt.TimeFrame.Days)
# Returns: 'Days'
# Get constant from name
tf = bt.TimeFrame.TFrame('Days')
# Returns: TimeFrame.Days (5)
```bash
## Relationship with Pandas
### Convert to pandas Series
```python
import backtrader as bt
import pandas as pd
class MyStrategy(bt.Strategy):
def stop(self):
# Get complete data array
close_array = self.data.close.array
# Create pandas Series
# Note: Need to manually build date index
dates = []
for i in range(len(self.data)):
dt = self.data.datetime.date(i)
dates.append(dt)
df = pd.DataFrame({
'close': close_array[:len(dates)],
}, index=dates)
df.index.name = 'date'
```bash
### Create Data Feed from pandas
```python
import pandas as pd
import backtrader as bt
# Create DataFrame
df = pd.DataFrame({
'datetime': pd.date_range('2020-01-01', periods=100),
'open': np.random.randn(100).cumsum() + 100,
'high': np.random.randn(100).cumsum() + 102,
'low': np.random.randn(100).cumsum() + 98,
'close': np.random.randn(100).cumsum() + 100,
'volume': np.random.randint(1000, 10000, 100),
})
# Set index
df.set_index('datetime', inplace=True)
# Create data feed
data = bt.feeds.PandasData(dataname=df)
```bash
### PandasData Parameter Mapping
```python
class CustomPandasData(bt.feeds.PandasData):
# Custom column name mapping
lines = ('close', 'volume', 'custom_line')
# Parameter mapping
params = (
('datetime', None), # None = use index
('open', 'open_price'),
('high', 'high_price'),
('low', 'low_price'),
('close', 'close_price'),
('volume', 'vol'),
('openinterest', None), # None = column doesn't exist
)
```bash
## Common Usage Patterns
### Pattern 1: Access Historical Data to Calculate Indicators
```python
class CustomIndicator(bt.Indicator):
lines = ('value',)
params = (('period', 20),)
def __init__(self):
super().__init__()
self.addminperiod(self.p.period)
def next(self):
# Calculate average of the last N periods
total = 0.0
for i in range(self.p.period):
total += self.data.close[-i]
self.lines.value[0] = total / self.p.period
```bash
### Pattern 2: Compare Current and Previous Values
```python
def next(self):
# Check if close price has risen consecutively
if (self.data.close[0] > self.data.close[-1] and
self.data.close[-1] > self.data.close[-2] and
self.data.close[-2] > self.data.close[-3]):
# 3 consecutive bars rising
self.buy()
```bash
### Pattern 3: Conditional Access to Avoid Out of Bounds
```python
def next(self):
# Ensure there's enough historical data
if len(self.data) < self.p.period:
return
# Safe access to historical data
prev_close = self.data.close[-self.p.period]
# Or use minperiod
if len(self.data) > self.p.period:
# Sufficient data here
pass
```bash
### Pattern 4: Get Complete Historical Data
```python
def next(self):
# Method 1: Use array attribute
all_closes = self.data.close.array
# Method 2: Loop to get values
closes = []
for i in range(len(self.data)):
closes.append(self.data.close[i - len(self.data) + 1])
# Method 3: Use getzero
all_data = self.data.close.getzero(0, len(self.data))
```bash
### Pattern 5: Multi-Line Indicator Access
```python
class BollingerBands(bt.Indicator):
lines = ('mid', 'top', 'bot')
params = (('period', 20), ('devfactor', 2.0))
def next(self):
# Access different output lines
mid = self.lines.mid[0]
top = self.lines.top[0]
bot = self.lines.bot[0]
# Or access by name
mid = self.mid[0]
top = self.top[0]
bot = self.bot[0]
```bash
## LineSeries Methods
### Length Operations
#### `len(self)`
Returns the length of the LineSeries (number of processed data points).
```python
current_length = len(self.indicator)
```bash
#### `size(self)`
Returns the number of lines (excluding extra lines).
```python
num_lines = self.indicator.size()
```bash
### Index Operations
#### `__getitem__(self, key)`
Gets a value from the primary line.
```python
value = self.indicator[0] # Current value
value = self.indicator[-1] # Previous value
```bash
#### `__call__(self, ago=None, line=-1)`
Returns a delayed line or value at specified index/name.
```python
# Return current line
current = self.indicator()
# Return line delayed by 3 periods
delayed = self.indicator(ago=3)
# Return current value of specified line
value = self.indicator(line='close')
```bash
### Buffer Operations
#### `qbuffer(self, savemem=0)`
Enable queued buffer mode to save memory.
```python
# Only keep recent data
self.data.qbuffer(savemem=1000)
# For indicators
self.sma.qbuffer()
```bash
#### `minbuffer(self, size)`
Set minimum buffer size.
```python
# Ensure at least 100 data points
self.indicator.minbuffer(100)
```bash
### Navigation Operations
#### `home(self)`
Reset all lines to the starting position.
```python
self.indicator.home()
```bash
#### `rewind(self, size=1)`
Rewind by the specified number of positions.
```python
self.indicator.rewind(5) # Rewind 5 positions
```bash
#### `advance(self, size=1)`
Advance by the specified number of positions.
```python
self.indicator.advance(1) # Advance 1 position
```bash
#### `forward(self, value=0.0, size=1)`
Advance all lines and fill with values.
```python
self.indicator.forward(size=1)
```bash
#### `backwards(self, size=1, force=False)`
Move all lines backward.
```python
self.indicator.backwards(size=1)
```bash
#### `reset(self)`
Reset all lines to initial state.
```python
self.indicator.reset()
```bash
#### `extend(self, value=0.0, size=0)`
Extend all lines.
```python
self.indicator.extend(size=10)
```bash
### Line Operations
#### `_getline(self, line, minusall=False)`
Get a line by name or index.
```python
# By index
line = self.indicator._getline(0)
# By name
line = self.indicator._getline('close')
# Use minusall parameter
line = self.indicator._getline(-1, minusall=True) # Last line
```bash
## Performance Optimization
### Using array Attribute
For scenarios requiring access to all data, use the array attribute directly:
```python
def next(self):
# Fast access to all data
data_array = self.data.close.array
# Use NumPy operations (if imported)
import numpy as np
mean = np.mean(data_array[-20:])
```bash
### Enable Cache Mode
For long-running backtests:
```python
# Set in Cerebro
cerebro = bt.Cerebro()
# Enable cache for data source
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data)
data.qbuffer(savemem=1000) # Only keep last 1000 bars
```bash
### Use runonce Mode
```python
# Batch processing mode, faster
cerebro.run(runonce=True)
```bash
## Complete Examples
### Example 1: Custom Multi-Line Indicator
```python
import backtrader as bt
class MultiOutputIndicator(bt.Indicator):
"""
Custom indicator: Price channel
Returns three lines: upper, middle, lower
"""
lines = ('upper', 'middle', 'lower')
params = (('period', 20),)
plotinfo = dict(
subplot=False, # Plot on main chart
)
def __init__(self):
super().__init__()
self.addminperiod(self.p.period)
def next(self):
# Calculate middle band (SMA)
total = 0.0
high_max = self.data.high[-self.p.period]
low_min = self.data.low[-self.p.period]
for i in range(self.p.period):
price = (self.data.high[-i] + self.data.low[-i] + self.data.close[-i]) / 3
total += price
if self.data.high[-i] > high_max:
high_max = self.data.high[-i]
if self.data.low[-i] < low_min:
low_min = self.data.low[-i]
self.lines.middle[0] = total / self.p.period
self.lines.upper[0] = high_max
self.lines.lower[0] = low_min
class MyStrategy(bt.Strategy):
def __init__(self):
# Create custom indicator
self.channel = MultiOutputIndicator(self.data, period=20)
# Built-in indicator
self.sma = bt.indicators.SMA(self.data.close, period=20)
def next(self):
# Access multiple lines of custom indicator
if self.data.close[0] > self.channel.upper[0]:
# Price breaks above upper band
self.buy()
elif self.data.close[0] < self.channel.lower[0]:
# Price breaks below lower band
self.sell()
```bash
### Example 2: Historical Data Analysis
```python
class AnalysisStrategy(bt.Strategy):
def __init__(self):
self.sma20 = bt.indicators.SMA(self.data.close, period=20)
self.sma50 = bt.indicators.SMA(self.data.close, period=50)
def next(self):
# Get list of last N values
recent_20 = self.data.close.get(ago=0, size=20)
# Calculate custom statistics
avg = sum(recent_20) / len(recent_20)
# Check trend
if self.sma20[0] > self.sma20[-1] > self.sma20[-2]:
# MA rising
pass
# Time-based analysis
current_dt = self.data.datetime.datetime(0)
if current_dt.hour >= 9 and current_dt.hour < 15:
# Trading hours
pass
```bash
### Example 3: Multi-Timeframe Analysis
```python
class MultiTimeFrameStrategy(bt.Strategy):
def __init__(self):
# Get data from different timeframes
self.daily = self.data0 # Daily
self.weekly = self.data1 # Weekly
# Create indicators for each timeframe
self.daily_sma = bt.indicators.SMA(self.daily.close, period=20)
self.weekly_sma = bt.indicators.SMA(self.weekly.close, period=20)
def next(self):
# Check multi-timeframe alignment
if len(self.weekly) > len(self.weekly_sma):
# Weekly indicator valid
if self.daily.close[0] > self.daily_sma[0]:
if self.weekly.close[0] > self.weekly_sma[0]:
# Both timeframes trend aligned
self.buy(data=self.daily)
```bash
## Common Pitfalls
1. **Index out of bounds**: Check `len(data)` before accessing historical data
2. **Minimum period**: Use `addminperiod()` to ensure indicators have enough data
3. **Future data leakage**: Avoid using `data[1]` and other future data in backtests
4. **Data alignment**: Multiple data sources may not be perfectly aligned
5. **Time handling**: datetime is in internal numeric format, needs conversion
## Related Documentation
- [Data Feeds API](data-feeds.md) - Data source configuration
- [Indicator API](indicator.md) - Indicator development
- [Strategy API](strategy.md) - Strategy development
- [Cerebro API](cerebro.md) - Backtesting engine