title: Filter API description: Complete Filter class API reference


Filter API

Filters in Backtrader transform or filter data bars before they are processed by strategies. Filters can be applied to data feeds to perform operations like filling missing bars, calculating derived data types (Heikin Ashi, Renko), and filtering by session times.

Class Definition

class backtrader.Filter(ParameterizedBase):
    """Base class for data filters."""

```bash

## Core Methods and Lifecycle

### `__init__(self, data, **kwargs)`

Initialize the filter with the data feed to be filtered.

```python
def __init__(self, data, **kwargs):
    super().__init__(**kwargs)

```bash

### `__call__(self, data)`

Process a data bar through the filter. This is the main entry point called for each bar.

```python
def __call__(self, data):
    if self._firsttime:
        self.nextstart(data)
        self._firsttime = False
    self.next(data)

```bash

- *Return Values**:
- `False`: Stream was not modified (bar accepted)
- `True`: Stream was modified (bar filtered/rejected)

### `nextstart(self, data)`

Called on the first bar before filtering starts. Override this method to perform one-time initialization.

```python
def nextstart(self, data):
    """Override for initialization logic."""
    pass

```bash

### `next(self, data)`

Process each data bar. Subclasses must override this method to implement filtering logic.

```python
def next(self, data):
    """Override to implement filter logic."""
    pass

```bash

### `last(self, data)`

Called when data is no longer producing bars. Can be called multiple times to deliver extra bars.

```python
def last(self, data):
    """Override to deliver final bars."""
    return False  # True if something delivered

```bash

## Filter Lifecycle

```mermaid
stateDiagram-v2
    [*] --> __init__: Filter Created
    __init__ --> __call__: First Bar
    __call__ --> nextstart: _firsttime=True
    nextstart --> __call__: Initialization Done
    __call__ --> next: Process Bar
    next --> __call__: Return Result
    __call__ --> last: No More Data
    last --> [*]: Cleanup

```bash

## Integration with Data Feeds

Filters are applied to data feeds using the `addfilter` method:

```python
import backtrader as bt

# Create data feed

data = bt.feeds.GenericCSVData(dataname='data.csv')

# Apply filter

data.addfilter(bt.filters.HeikinAshi())

# Add to cerebro

cerebro.adddata(data)

```bash

## Built-in Filters

### CalendarDays

Fills missing calendar days in trading day data.

```python
class backtrader.filters.CalendarDays(ParameterizedBase):
    """Bar Filler to add missing calendar days to trading days."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `fill_price` | float/None | None | Price to fill gaps (>0: use value, 0/None: last close, -1: mid of H-L) |

| `fill_vol` | float | NaN | Value for missing volume |

| `fill_oi` | float | NaN | Value for missing open interest |

- *Example**:

```python

# Fill missing weekends and holidays

data = bt.feeds.GenericCSVData(dataname='data.csv')
data.addfilter(bt.filters.CalendarDays(
    fill_price=None,  # Use last close price
    fill_vol=0.0,     # Zero volume for filled bars

))
cerebro.adddata(data)

```bash

### SessionFilter

Filters out intraday bars that fall outside regular session times (pre/post market data).

```python
class backtrader.filters.SessionFilter(ParameterizedBase):
    """Filter out bars outside regular session times."""

```bash

- *Example**:

```python

# Only keep bars during regular trading hours

data = bt.feeds.GenericCSVData(
    dataname='intraday.csv',
    sessionstart=datetime.time(9, 30),
    sessionend=datetime.time(16, 0),
)
data.addfilter(bt.filters.SessionFilter())
cerebro.adddata(data)

```bash

### SessionFiller

Fills missing bars over gaps within a trading session.

```python
class backtrader.filters.SessionFiller(ParameterizedBase):
    """Bar Filler to add missing bars over gaps in a session."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `fill_price` | float/None | None | Price to fill gaps (None: last close) |

| `fill_vol` | float | NaN | Value for missing volume |

| `fill_oi` | float | NaN | Value for missing open interest |

| `skip_first_fill` | bool | True | Don't fill from session start to first bar |

- *Example**:

```python

# Fill missing 5-minute bars within trading session

data = bt.feeds.GenericCSVData(
    dataname='intraday.csv',
    timeframe=bt.TimeFrame.Minutes,
    compression=5,
    sessionstart=datetime.time(9, 30),
    sessionend=datetime.time(16, 0),
)
data.addfilter(bt.filters.SessionFiller(
    fill_price=None,
    fill_vol=0.0,
))
cerebro.adddata(data)

```bash

### DataFilter

Generic filter that uses a callable function to filter bars.

```python
class backtrader.filters.DataFilter(AbstractDataBase):
    """Filter bars based on a callable function."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `funcfilter` | callable | None | Function returning True to accept, False to reject |

- *Example**:

```python

# Define filter function

def my_filter(data):
    """Reject bars with zero volume."""
    return data.volume[0] > 0

# Apply filter

data = bt.feeds.GenericCSVData(dataname='data.csv')
wrapped_data = bt.filters.DataFilter(dataname=data)
wrapped_data.p.funcfilter = my_filter
cerebro.adddata(wrapped_data)

```bash

### HeikinAshi

Remodels OHLC data into Heikin Ashi candlesticks.

```python
class backtrader.filters.HeikinAshi:
    """Remodels OHLC to Heikin Ashi candlesticks."""

```bash

- *Heikin Ashi Formula**:
- Close = (Open + High + Low + Close) / 4
- Open = (Previous Open + Previous Close) / 2
- High = max(Open, Close, High)
- Low = min(Open, Close, Low)

- *Example**:

```python
data = bt.feeds.GenericCSVData(dataname='data.csv')
data.addfilter(bt.filters.HeikinAshi())
cerebro.adddata(data)

```bash

### Renko

Converts price data into Renko bricks.

```python
class backtrader.filters.Renko(Filter):
    """Modify data stream to draw Renko bars (or bricks)."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `hilo` | bool | False | Use high/low instead of close |

| `size` | float/None | None | Size of each brick |

| `autosize` | float | 20.0 | Divisor for auto-calculating brick size |

| `dynamic` | bool | False | Recalculate size for each brick |

| `align` | float | 1.0 | Alignment factor for price boundaries |

| `roundstart` | bool | True | Round initial start value |

- *Example**:

```python

# Create 10-point Renko bricks

data = bt.feeds.GenericCSVData(dataname='data.csv')
data.addfilter(bt.filters.Renko(
    size=10.0,  # 10-point bricks
    hilo=False,  # Use close price

))
cerebro.adddata(data)

```bash

### DataFiller

Fills gaps in data feeds based on timeframe and session settings.

```python
class backtrader.filters.DataFiller(AbstractDataBase):
    """Fill gaps in source data."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `fill_price` | float/None | None | Price to fill gaps |

| `fill_vol` | float | NaN | Value for missing volume |

| `fill_oi` | float | NaN | Value for missing open interest |

- *Example**:

```python

# Wrap data feed with DataFiller

data = bt.feeds.GenericCSVData(dataname='data.csv')
filled_data = bt.filters.DataFiller(dataname=data)
cerebro.adddata(filled_data)

```bash

### DaySplitterClose

Splits daily bars into two parts for replay simulation.

```python
class backtrader.filters.DaySplitterClose(ParameterizedBase):
    """Splits daily bar into OHLX and CCCC ticks."""

```bash

- *Parameters**:

| Parameter | Type | Default | Description |

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

| `closevol` | float | 0.5 | Percentage of volume for closing tick (0.0-1.0) |

- *Example**:

```python

# Split daily bars for replay

data = bt.feeds.GenericCSVData(dataname='daily.csv')
data.addfilter(bt.filters.DaySplitterClose(closevol=0.5))
cerebro.replaydata(data)  # Use with replaydata

```bash

### BarReplayerOpen (DayStepsFilter)

Splits bars into open and OHLC parts to simulate replay.

```python
class backtrader.filters.BarReplayerOpen:
    """Split bar into Open and OHLC parts."""

```bash

- *Example**:

```python
data = bt.feeds.GenericCSVData(dataname='data.csv')
data.addfilter(bt.filters.BarReplayerOpen())
cerebro.adddata(data)

```bash

## Custom Filter Development

### Simple Filter (No Stack Management)

For filters that only need to accept/reject bars:

```python
class VolumeFilter(bt.Filter):
    """Filter out bars with volume below threshold."""

    params = (('min_volume', 1000),)

    def next(self, data):
        if data.volume[0] < self.p.min_volume:
            data.backwards()  # Remove bar from stream
            return True  # Signal stream was modified
        return False  # Bar accepted

```bash

### Complex Filter (Stack Management)

For filters that modify or add bars:

```python
class PriceModifier(bt.Filter):
    """Modify close price based on formula."""

    params = (('factor', 1.01),)  # 1% increase

    def next(self, data):

# Modify the close price
        data.close[0] *= self.p.factor

# Adjust high if needed
        if data.close[0] > data.high[0]:
            data.high[0] = data.close[0]
        return False  # Stream length unchanged

```bash

### Filter with State

```python
class RangeFilter(bt.Filter):
    """Filter bars based on average true range."""

    params = (('period', 14), ('threshold', 2.0),)

    def __init__(self, data, **kwargs):
        super().__init__(data, **kwargs)
        self.true_ranges = []

    def next(self, data):
        if len(data) < 2:
            return False

        tr = max(
            data.high[0] - data.low[0],
            abs(data.high[0] - data.close[-1]),
            abs(data.low[0] - data.close[-1])
        )
        self.true_ranges.append(tr)

        if len(self.true_ranges) > self.p.period:
            self.true_ranges.pop(0)

            avg_tr = sum(self.true_ranges) / len(self.true_ranges)
            current_range = data.high[0] - data.low[0]

            if current_range > avg_tr *self.p.threshold:
                data.backwards()
                return True

        return False

```bash

## Filter Return Values

Understanding the return value is critical for filter behavior:

| Return | Meaning | Effect |

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

| `False` | Stream unchanged | Bar is accepted/processed normally |

| `True` | Stream modified | Bar was filtered out or modified |

## Common Patterns

### Filtering by Time

```python
class AfterHoursFilter(bt.Filter):
    """Remove pre-market and after-hours bars."""

    params = (('start', datetime.time(9, 30)),
              ('end', datetime.time(16, 0)),)

    def next(self, data):
        current_time = data.datetime.time(0)
        if not (self.p.start <= current_time <= self.p.end):
            data.backwards()
            return True
        return False

```bash

### Data Transformation

```python
class LogTransform(bt.Filter):
    """Apply log transformation to prices."""

    def next(self, data):
        import math
        data.open[0] = math.log(data.open[0])
        data.high[0] = math.log(data.high[0])
        data.low[0] = math.log(data.low[0])
        data.close[0] = math.log(data.close[0])
        return False

```bash

### Conditional Bar Addition

```python
class AddSignalBar(bt.Filter):
    """Add a signal bar when conditions are met."""

    def next(self, data):
        if len(data) < 10:
            return False

# Check for crossover condition
        if data.close[0] > data.close[-1]:

# Add a signal bar
            bar = [float('NaN')]* data.size()
            bar[data.DateTime] = data.datetime[0]
            for i in [data.Open, data.High, data.Low, data.Close]:
                bar[i] = data.close[0]
            bar[data.Volume] = 0.0
            data._add2stack(bar)

        return False

```bash

## Full Example: Custom Filter

```python
import backtrader as bt
from datetime import datetime

class VolatilityFilter(bt.Filter):
    """Filter out low volatility periods."""

    params = (
        ('period', 20),
        ('std_threshold', 0.02),  # 2% of price
    )

    def __init__(self, data, **kwargs):
        super().__init__(data, **kwargs)
        self.closes = []

    def next(self, data):
        self.closes.append(data.close[0])

# Need enough data
        if len(self.closes) < self.p.period:
            return False

# Keep only recent data
        if len(self.closes) > self.p.period:
            self.closes.pop(0)

# Calculate standard deviation
        import statistics
        mean = statistics.mean(self.closes)
        std = statistics.stdev(self.closes)
        cv = std / mean if mean != 0 else 0

# Filter if coefficient of variation is below threshold
        if cv < self.p.std_threshold:
            data.backwards()
            return True

        return False


# Usage

class MyStrategy(bt.Strategy):
    def __init__(self):
        self.sma = bt.indicators.SMA(self.data.close, period=20)

    def next(self):
        if not self.position:
            if self.data.close[0] > self.sma[0]:
                self.buy()


# Create cerebro

cerebro = bt.Cerebro()

# Add data with filter

data = bt.feeds.GenericCSVData(
    dataname='stock_data.csv',
    fromdate=datetime(2020, 1, 1),
    todate=datetime(2023, 12, 31),
)
data.addfilter(VolatilityFilter(period=20, std_threshold=0.02))
cerebro.adddata(data)

# Add strategy

cerebro.addstrategy(MyStrategy)

# Run

result = cerebro.run()

```bash

## Best Practices

1. **Always call `super().__init__()`**when overriding `__init__`

2.**Return correct values**: `True` if stream modified, `False` otherwise

1. **Use `data.backwards()`**to remove bars from stream

4.**Use `data._add2stack()`**to add bars to stream
5.**Check `len(data)`**before accessing historical values
6.**Consider performance**for large datasets - filters are called for every bar
7.**Test filters** with simple data before applying to complex strategies

## Next Steps

- [Strategy API](strategy.md) - Strategy development
- [Data Feeds API](data-feeds.md) - Data source configuration
- [Indicators API](indicator.md) - Technical indicators