# Your first backtest

This example buys one unit when a fast EMA crosses above a slow EMA, closes it on the reverse crossing, and prints performance and trades. It uses bundled historical GOOG data.

## 1. Install

Use Python 3.12–3.14. From a terminal, install the source repository into your environment:

```sh
git clone https://github.com/lucidcog/openbacktesting.git
cd openbacktesting
python -m pip install .
```

If you already use this checkout with uv, `uv sync --locked` is sufficient. An installed package includes the bundled sample data; the script below can run outside the repository.

## 2. Save and run this script

Save as `first_backtest.py`, then run `python first_backtest.py` (or `uv run --locked python first_backtest.py` in the checkout).

```python
from openbacktesting import Backtest, Strategy
from openbacktesting.test import GOOG


class EmaCross(Strategy):
    fast_length = 10
    slow_length = 30

    def init(self):
        self.fast = self.ta.ema(self.close, length=self.fast_length)
        self.slow = self.ta.ema(self.close, length=self.slow_length)

    def next(self):
        if len(self.close) < 2:
            return
        if not (self.fast.valid[-2:].all() and self.slow.valid[-2:].all()):
            return
        if self.ta.is_crossover(self.fast, self.slow) and not self.position:
            self.buy(qty=1)
        elif self.ta.is_crossunder(self.fast, self.slow) and self.position:
            self.position.close()


bt = Backtest(GOOG, EmaCross, cash=10_000, commission=0.002,
              end_policy="close_at_last_close")
result = bt.run()
print(result[["Return [%]", "# Trades", "Max. Drawdown [%]"]])
print(result["_trades"][["EntryTime", "ExitTime", "Size", "PnL"]].tail())
print(result["_equity_curve"].tail())
```

## 3. Understand the run

- `init()` registers both indicators once. In `next()`, their handles reveal only history through the current bar.
- `next()` runs from the first bar. The guards wait for two valid observations before comparing crossings.
- `buy(qty=1)` requests one asset unit. The default timing executes eligible market orders at the next open; `position.close()` likewise submits exit orders.
- `commission=0.002` charges 0.2% on executions. `close_at_last_close` closes remaining holdings at the final close; the default policy instead leaves them open and valued at that price.
- `run()` returns a pandas Series. Percentage statistics use percentage points; `# Trades` counts closed fragments.

## 4. Change one thing

Run the same strategy with another slow period using `bt.run(slow_length=50)`. To use your own prices, replace `GOOG` with a DataFrame following the [market-data requirements](https://docs.openbacktesting.com/reference/data/index.md). To expose validated settings, replace class defaults with [inputs](https://docs.openbacktesting.com/reference/inputs/index.md).

## Next steps

[Strategy lifecycle](https://docs.openbacktesting.com/reference/strategy/index.md) · [All Backtest options](https://docs.openbacktesting.com/reference/core/index.md) · [Orders and stops](https://docs.openbacktesting.com/reference/trading/index.md) · [Indicator catalog](https://docs.openbacktesting.com/reference/indicators/index.md) · [Read results](https://docs.openbacktesting.com/guides/results/index.md)
