Skip to content
All library documents

Backtesting and Optimizing a Statistical Arbitrage Spread Strategy

Notebook vn.py

Summary

The document demonstrates a vn.py workflow for backtesting a statistical arbitrage strategy on a two-leg futures spread. It defines a spread as the price difference between two futures contracts, sets the backtest interval and trading assumptions, loads market data, runs the strategy, calculates results and statistics, and displays a chart. It also shows how to inspect recorded trades.

For parameter search, the example sets Sharpe ratio as the optimization target and varies the Bollinger window and deviation settings. It then invokes both genetic-algorithm and brute-force optimization. The example specifies a historical period and assumptions such as zero commission and slippage, but reports no performance results. It therefore illustrates setup and workflow rather than establishing that the strategy is profitable. The document does not discuss data quality, out-of-sample validation, or the risks of selecting parameters based on historical results.

Key ideas

  • A two-contract spread can be represented as one leg's price minus the other's.
  • The backtesting engine is configured with market interval, dates, capital, contract size, and price tick assumptions.
  • The workflow runs a strategy, calculates statistics, and can display a chart and individual trades.
  • Parameter optimization can target Sharpe ratio and use either genetic or brute-force search.
  • The example provides no results or validation of the strategy's performance.

Tags

Full text
# backtesting


```python
from vnpy.trader.optimize import OptimizationSetting
from vnpy_spreadtrading.backtesting import BacktestingEngine
from vnpy_spreadtrading.strategies.statistical_arbitrage_strategy import (
    StatisticalArbitrageStrategy
)
from vnpy_spreadtrading.base import LegData, SpreadData
from datetime import datetime
```

```python
spread = SpreadData(
    name="IF-Spread",
    legs=[LegData("IF1911.CFFEX"), LegData("IF1912.CFFEX")],
    variable_symbols={"A": "IF1911.CFFEX", "B": "IF1912.CFFEX"},
    variable_directions={"A": 1, "B": -1},
    price_formula="A-B",
    trading_multipliers={"IF1911.CFFEX": 1, "IF1912.CFFEX": 1},
    active_symbol="IF1911.CFFEX",
    min_volume=1,
    compile_formula=False                          # 回测时不编译公式,compile_formula传False,从而支持多进程优化
)
```

```python
engine = BacktestingEngine()
engine.set_parameters(
    spread=spread,
    interval="1m",
    start=datetime(2019, 6, 10),
    end=datetime(2019, 11, 10),
    rate=0,
    slippage=0,
    size=300,
    pricetick=0.2,
    capital=1_000_000,
)
engine.add_strategy(StatisticalArbitrageStrategy, {})
```

```python
engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()
```

```python
for trade in engine.trades.values():
    print(trade)
```

```python
setting = OptimizationSetting()
setting.set_target("sharpe_ratio")
setting.add_parameter("boll_window", 10, 30, 1)
setting.add_parameter("boll_dev", 1, 3, 1)

engine.run_ga_optimization(setting)
```

```python
engine.run_bf_optimization(setting)
```

Shown in full with attribution under the source's licence. Licence: MIT

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.