title: Code Style Guide description: Python code formatting and style conventions for Backtrader


Code Style Guide

This guide covers the code formatting and style conventions used in the Backtrader project. Following these guidelines ensures consistent, readable, and maintainable code.

Table of Contents

Formatting Rules

Line Length

  • Maximum line length: 124 characters

  • Soft limit: 100 characters (preferred for readability)

  • Reasoning: Balance between readability and practical data structure definitions

Indentation

  • Spaces: 4 spaces (Python standard)

  • Tabs: Never use tabs

Trailing Whitespace

  • No trailing whitespace allowed

  • Enforced by pre-commit hooks

Line Endings

  • Unix-style (LF): Required

  • Windows-style (CRLF): Automatically converted to LF

Blank Lines

  • Top-level: 2 blank lines between class/function definitions

  • Inside class: 1 blank line between method definitions

  • Inside function: Use blank lines sparingly to separate logical sections

Example


# Good: Proper spacing and line length

class MyIndicator(bt.Indicator):
    """A custom indicator for demonstration."""

    lines = ('signal',)
    params = (
        ('period', 14),
        ('threshold', 0.5),
    )

    def __init__(self):

# Calculate the indicator value
        self.lines.signal = bt.indicators.RSI(self.data, period=self.p.period)

```bash

## Import Order Conventions

### Standard Order (isort with Black profile)

1. Standard library imports
2. Third-party imports
3. Local application imports
4. Relative imports (from current package)

### Formatting Rules

- **Grouping**: Separate groups with blank line
- **Sorting**: Alphabetical within each group
- **Line length**: Use `ruff format` for automatic wrapping

### Example

```python

# Standard library

import datetime
from pathlib import Path

# Third-party

import numpy as np
import pandas as pd

# Local application

from backtrader.indicators import Indicator
from backtrader.lineseries import LineSeries

# Relative (from current package)

from .utils import calculate_value

```bash

### Import Aliases

Follow these conventions for common libraries:

```python
import backtrader as bt
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

```bash

### Wildcard Imports

- *Avoid wildcard imports** except for specific cases:

```python

# Allowed in __init__.py for exposing public API

from .indicator import *
from .observers import *

# Never in regular modules

# from indicators import *# BAD

```bash

## Type Hint Guidelines

### When to Use Type Hints

- *Required for**:
- Public API methods
- Complex function signatures
- Functions returning non-obvious types
- Class method parameters

- *Optional for**:
- Private methods (prefixed with `_`)
- Simple, obvious cases
- Performance-critical code (type hints have overhead)

### Basic Syntax

```python
def calculate_sma(period: int, data: list[float]) -> float:
    """Calculate Simple Moving Average."""
    return sum(data[:period]) / period

```bash

### Common Types

```python
from typing import Optional, List, Dict, Union, Callable

def process_data(
    data: pd.DataFrame,
    period: int = 14,
    callback: Optional[Callable[[float], None]] = None,
) -> Dict[str, float]:
    """Process data with optional callback."""
    pass

```bash

### Type Hints for Backtrader

```python
from backtrader import LineLike, StrategyBase

def register_indicator(
    owner: LineIterator,
    indicator: Indicator,
) -> None:
    """Register an indicator with its owner."""
    pass

```bash

### Type Checking

Run mypy to verify type hints:

```bash
mypy backtrader/

```bash

## Docstring Conventions

### Style: Google-style

Use Google-style docstrings for all public classes, methods, and functions.

### Function/Method Docstrings

```python
def calculate_rsi(prices: list[float], period: int = 14) -> list[float]:
    """Calculate Relative Strength Index.

    The RSI is a momentum indicator that measures the magnitude of recent
    price changes to evaluate overbought or oversold conditions.

    Args:
        prices: List of price values.
        period: Number of periods for calculation. Defaults to 14.

    Returns:
        List of RSI values. Same length as input prices.

    Raises:
        ValueError: If period is less than 2 or prices list is empty.

    Example:
        >>> calculate_rsi([100, 102, 98, 105], period=3)
        [None, None, 50.0, 75.0]
    """
    if period < 2:
        raise ValueError(f"Period must be at least 2, got {period}")

# Implementation...

```bash

### Class Docstrings

```python
class CustomIndicator(bt.Indicator):
    """A custom technical indicator for trend analysis.

    This indicator combines multiple moving averages to identify
    trend direction and strength.

    Attributes:
        lines: Contains 'trend' line for output.
        params: Configuration parameters.

    Example:
        >>> cerebro = bt.Cerebro()
        >>> cerebro.addstrategy(MyStrategy)
        >>> cerebro.run()
    """

```bash

### Module Docstrings

```python
"""Custom indicators module.

This module contains custom technical indicators that extend
the standard Backtrader indicator library.

Typical usage:
    from backtrader.indicators.custom import CustomIndicator
    cerebro.addindicator(CustomIndicator)
"""

```bash

## Comment Standards

### Language: English Only

- *All code comments must be in English**. This ensures consistency across the international codebase.

```python

# Good

# Calculate the signal based on price momentum

signal = self.data.close[0] - self.data.close[-1]

# Bad

# 根据价格动量计算信号

signal = self.data.close[0] - self.data.close[-1]

```bash

### When to Comment

- *DO comment**:
- Complex algorithms
- Non-obvious business logic
- Workarounds for bugs/issues
- Performance-critical sections
- Public API documentation

- *DON'T comment**:
- Obvious code (self-documenting)
- Outdated information
- Copy-pasted code without adjustment

### Comment Style

```python

# Single-line comments explain why, not what

# BAD:

# Increment counter

counter += 1

# GOOD:

# Reset counter after reaching threshold to prevent overflow

counter = 0 if counter >= MAX_THRESHOLD else counter + 1

```bash

### TODO/FIXME Comments

```python

# TODO: Add support for multiple timeframes

# FIXME: This fails when data contains NaN values

# HACK: Temporary workaround for upstream bug in numpy 1.x

# NOTE: Performance optimization opportunity in hot path

```bash

### Block Comments

```python

# The following calculation implements the EMA formula:

# EMA(today) = Value(today) *k + EMA(yesterday)*(1 - k)

# where k = 2 / (period + 1)
#

# This implementation matches the behavior of pandas.ewm()

k = 2 / (period + 1)
ema_today = current_value*k + ema_yesterday*(1 - k)

```bash

## Naming Conventions

### General Rules

Follow PEP 8 naming conventions:

| Type | Convention | Example |

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

| Module | `lowercase_with_underscores` | `linebuffer.py` |

| Class | `CapitalizedWords` | `LineIterator` |

| Function | `lowercase_with_underscores` | `calculate_sma()` |

| Method | `lowercase_with_underscores` | `get_value()` |

| Constant | `UPPERCASE_WITH_UNDERSCORES` | `MAX_PERIOD` |

| Variable | `lowercase_with_underscores` | `close_price` |

| Private | `_leading_underscore` | `_internal_method()` |

| Protected | `__double_underscore` | `__private_attr` |

### Backtrader-specific Names

```python

# Lines (output series)

class MyIndicator(bt.Indicator):
    lines = ('signal', 'trend')  # lowercase, tuple

# Parameters

params = (
    ('period', 14),           # lowercase
    ('use_threshold', True),
)

# Accessing

self.p.period      # Parameter access

self.lines.signal  # Line access

```bash

### Booleans

Use `is_` or `has_` prefix for boolean variables:

```python
is_valid = True
has_data = False
should_recalculate = True

```bash

### Avoid Single-letter Names

Except for loop variables and mathematical notation:

```python

# Good

for index in range(len(data)):
    price = data[index]

# Acceptable

for i, price in enumerate(data):
    pass

# Bad (unclear meaning)

x = calculate()
y = process(x)

```bash

## Code Quality Tools

### pyupgrade

Automatically upgrade Python syntax to newer versions:

```bash

# Upgrade to Python 3.8+ syntax

pyupgrade --py38-plus backtrader/

# Upgrade to Python 3.11+ syntax

pyupgrade --py311-plus backtrader/

```bash

- *What it does**:
- Converts `%` formatting to f-strings
- Replaces `super()` calls
- Modernizes type hints
- Removes unnecessary `object` inheritance

### ruff

Fast Python linter and formatter:

```bash

# Check for issues

ruff check backtrader/

# Auto-fix issues

ruff check --fix backtrader/

# Format code

ruff format backtrader/

```bash

- *Configuration** (pyproject.toml):

```toml
[tool.ruff]
line-length = 121
target-version = "py38"

[tool.ruff.lint]
select = ["E", "F"]
ignore = ["E501"]  # Line length handled by formatter

```bash

### isort

Import statement organizer:

```bash

# Sort imports

isort backtrader/

# Check without modifying

isort --check-only backtrader/

```bash

- *Configuration** (pyproject.toml):

```toml
[tool.isort]
profile = "black"
line_length = 121

```bash

### mypy

Static type checker:

```bash

# Run type checking

mypy backtrader/

# Check specific file

mypy backtrader/indicators/sma.py

```bash

- *Configuration**(pyproject.toml):

```toml
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
check_untyped_defs = true
ignore_missing_imports = true

```bash

### black

Code formatter (note: project uses ruff-format for consistency):

```bash

# Format with Black (if needed)

black --line-length 124 backtrader/

```bash

## Pre-commit Hooks

### Installation

```bash

# Install pre-commit framework

pip install pre-commit

# Install hooks in your repository

pre-commit install

# Run manually on all files

pre-commit run --all-files

```bash

### Hook Configuration

The project uses `.pre-commit-config.yaml` with the following hooks:

1.**pyupgrade**: Auto-upgrade Python syntax

1. **ruff**: Linting and formatting
2. **trailing-whitespace**: Remove trailing spaces
3. **end-of-file-fixer**: Ensure newline at EOF
4. **check-yaml/check-json**: Validate YAML and JSON files
5. **debug-statements**: Prevent debugger commits

### Using Pre-commit

```bash

# Automatic: Runs on every git commit

git commit -m "feat: Add new indicator"

# Manual: Run on all files

pre-commit run --all-files

# Run on specific files

pre-commit run --files backtrader/indicators/*.py

# Skip hooks (not recommended)

git commit --no-verify -m "WIP"

```bash

### Git Setup (Makefile)

```bash

# Setup git hooks automatically

make git-setup

# This creates a pre-commit hook that runs:

make pre-commit

```bash

### Pre-commit Output

```bash
$ git commit -m "Add new feature"

Trim trailing whitespace.................................................Passed
Fix end of files.........................................................Passed
Check Yaml..............................................................Passed
pyupgrade...............................................................Passed
ruff-format............................................................Passed
ruff-lint................................................................Passed
[dev abc1234] Add new feature
 1 file changed, 42 insertions(+)

```bash

## Quick Reference

### Before Committing

```bash

# Format and check your code

bash scripts/optimize_code.sh

# Or manually

pyupgrade --py38-plus backtrader/
isort backtrader/
ruff format backtrader/
ruff check --fix backtrader/

# Run tests

pytest tests/ -n 4 -v

```bash

### IDE Configuration

- *VS Code** (.vscode/settings.json):

```json
{
  "python.formatting.provider": "none",
  "editor.formatOnSave": true,
  "[python]": {
    "editor.defaultFormatter": "charliemarsh.ruff"
  },
  "ruff.lineLength": 124,
  "ruff.organizeImports": true
}

```bash

- *PyCharm**:
- Enable "Ruff" plugin
- Set line length to 124
- Enable "Optimize imports on save"

## See Also

- [Development Setup](setup.md)
- [Testing Guide](testing.md)
- [Contributing Guidelines](contributing.md)
- [Architecture Overview](../architecture/overview.md)