stock/docs/source/backtesting.rst
2026-02-27 03:17:12 +08:00

433 lines
10 KiB
ReStructuredText

Backtesting System
==================
OpenClaw includes a comprehensive backtesting engine for testing strategies against historical data.
Overview
--------
The backtesting system simulates trading using historical data to evaluate strategy performance before risking real capital.
Key Features
~~~~~~~~~~~~
* Historical simulation with accurate price data
* Multiple strategy support
* Performance analytics and metrics
* Risk-adjusted returns calculation
* Trade-by-trade analysis
Quick Start
-----------
Basic Backtest
~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.engine import BacktestEngine
from datetime import datetime, timedelta
# Create engine
engine = BacktestEngine()
# Configure backtest
engine.configure(
symbols=["AAPL"],
start_date=datetime(2023, 1, 1),
end_date=datetime(2023, 12, 31),
initial_capital=10000.0
)
# Run backtest
results = engine.run()
# Print summary
print(f"Total Return: {results.total_return:.2%}")
print(f"Sharpe Ratio: {results.sharpe_ratio:.2f}")
Advanced Configuration
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.engine import BacktestEngine
from openclaw.strategy.trend_following import TrendFollowingStrategy
# Create engine with custom strategy
engine = BacktestEngine()
strategy = TrendFollowingStrategy(
sma_period=50,
position_size=0.1
)
engine.configure(
symbols=["AAPL", "MSFT", "GOOGL"],
strategy=strategy,
start_date="2023-01-01",
end_date="2023-12-31",
initial_capital=100000.0,
commission=0.001, # 0.1% per trade
slippage=0.0005 # 0.05% slippage
)
results = engine.run()
Backtest Engine
---------------
Configuration
~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.engine import BacktestEngine
engine = BacktestEngine()
# Set parameters
engine.configure(
# Required
symbols=["AAPL", "MSFT"],
start_date="2023-01-01",
end_date="2023-12-31",
# Optional
initial_capital=10000.0,
strategy=None, # Use default strategy
commission=0.001,
slippage=0.0005,
enable_caching=True
)
Running Backtests
~~~~~~~~~~~~~~~~~
.. code-block:: python
# Run single backtest
results = engine.run()
# Run with progress callback
def on_progress(progress: float):
print(f"Progress: {progress:.0%}")
results = engine.run(progress_callback=on_progress)
# Run multiple backtests (parameter sweep)
param_grid = {
"sma_period": [20, 50, 200],
"position_size": [0.05, 0.1, 0.2]
}
results = engine.run_sweep(param_grid)
Performance Metrics
-------------------
Basic Metrics
~~~~~~~~~~~~~
* **Total Return**: Overall percentage return
* **Annualized Return**: Return adjusted to yearly basis
* **Volatility**: Standard deviation of returns
* **Sharpe Ratio**: Risk-adjusted return metric
* **Max Drawdown**: Largest peak-to-trough decline
* **Win Rate**: Percentage of winning trades
Advanced Metrics
~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.analyzer import BacktestAnalyzer
analyzer = BacktestAnalyzer(results)
# Get all metrics
metrics = analyzer.calculate_metrics()
print(f"Sortino Ratio: {metrics.sortino_ratio:.2f}")
print(f"Calmar Ratio: {metrics.calmar_ratio:.2f}")
print(f"Omega Ratio: {metrics.omega_ratio:.2f}")
print(f"Profit Factor: {metrics.profit_factor:.2f}")
print(f"Expectancy: ${metrics.expectancy:.2f}")
Trade Analysis
~~~~~~~~~~~~~~
.. code-block:: python
# Get individual trades
trades = results.trades
for trade in trades[:5]: # First 5 trades
print(f"Date: {trade.date}")
print(f"Symbol: {trade.symbol}")
print(f"Side: {trade.side}")
print(f"Entry: ${trade.entry_price:.2f}")
print(f"Exit: ${trade.exit_price:.2f}")
print(f"PnL: ${trade.pnl:.2f}")
# Trade statistics
stats = analyzer.get_trade_statistics()
print(f"Avg winning trade: ${stats.avg_winner:.2f}")
print(f"Avg losing trade: ${stats.avg_loser:.2f}")
print(f"Largest winner: ${stats.max_winner:.2f}")
print(f"Largest loser: ${stats.max_loser:.2f}")
Visualization
-------------
Equity Curve
~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.analyzer import BacktestAnalyzer
analyzer = BacktestAnalyzer(results)
# Plot equity curve
analyzer.plot_equity_curve(
filename="equity_curve.png",
show_drawdowns=True
)
Drawdown Analysis
~~~~~~~~~~~~~~~~~
.. code-block:: python
# Plot drawdown chart
analyzer.plot_drawdown(
filename="drawdown.png"
)
# Get drawdown statistics
dd_stats = analyzer.get_drawdown_statistics()
print(f"Max drawdown: {dd_stats.max_drawdown:.2%}")
print(f"Avg drawdown: {dd_stats.avg_drawdown:.2%}")
print(f"Max duration: {dd_stats.max_duration} days")
Monthly Returns
~~~~~~~~~~~~~~~
.. code-block:: python
# Plot monthly returns heatmap
analyzer.plot_monthly_returns(
filename="monthly_returns.png"
)
Trade Distribution
~~~~~~~~~~~~~~~~~~
.. code-block:: python
# Plot trade distribution
analyzer.plot_trade_distribution(
filename="trade_dist.png"
)
Multi-Symbol Backtests
----------------------
Portfolio Backtest
~~~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.engine import BacktestEngine
engine = BacktestEngine()
engine.configure(
symbols=["AAPL", "MSFT", "GOOGL", "AMZN", "META"],
weights="equal", # Equal weighting
start_date="2023-01-01",
end_date="2023-12-31",
initial_capital=100000.0
)
results = engine.run()
# Per-symbol results
for symbol in results.symbol_results:
result = results.symbol_results[symbol]
print(f"{symbol}: {result.total_return:.2%}")
Custom Weights
~~~~~~~~~~~~~~
.. code-block:: python
# Custom portfolio weights
engine.configure(
symbols=["AAPL", "MSFT", "GOOGL"],
weights={
"AAPL": 0.5,
"MSFT": 0.3,
"GOOGL": 0.2
}
)
Strategy Development
--------------------
Creating Custom Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.strategy.base import Strategy, Signal
from typing import List, Dict
import pandas as pd
class MyCustomStrategy(Strategy):
"""Custom trading strategy."""
def __init__(self, param1: float = 1.0, param2: int = 10):
super().__init__()
self.param1 = param1
self.param2 = param2
def generate_signals(
self,
data: pd.DataFrame
) -> List[Signal]:
"""Generate trading signals."""
signals = []
# Your strategy logic here
for i in range(len(data)):
if self.should_buy(data, i):
signals.append(Signal(
date=data.index[i],
action="buy",
confidence=0.8
))
elif self.should_sell(data, i):
signals.append(Signal(
date=data.index[i],
action="sell",
confidence=0.8
))
return signals
def should_buy(self, data: pd.DataFrame, index: int) -> bool:
"""Buy condition."""
# Implement buy logic
return False
def should_sell(self, data: pd.DataFrame, index: int) -> bool:
"""Sell condition."""
# Implement sell logic
return False
Strategy Optimization
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.optimizer import StrategyOptimizer
optimizer = StrategyOptimizer()
# Define parameter grid
param_grid = {
"sma_fast": [10, 20, 30],
"sma_slow": [50, 100, 200],
"position_size": [0.05, 0.1, 0.15]
}
# Run optimization
best_params = optimizer.optimize(
strategy_class=TrendFollowingStrategy,
param_grid=param_grid,
metric="sharpe_ratio", # Optimize for Sharpe
data=data
)
print(f"Best parameters: {best_params}")
Walk-Forward Analysis
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from openclaw.backtest.walk_forward import WalkForwardTester
wf_tester = WalkForwardTester()
results = wf_tester.run(
strategy=strategy,
data=data,
train_size=252, # 1 year training
test_size=63, # 3 months testing
step_size=63 # Move forward 3 months at a time
)
print(f"Average in-sample Sharpe: {results.avg_in_sample_sharpe:.2f}")
print(f"Average out-of-sample Sharpe: {results.avg_out_sample_sharpe:.2f}")
Data Handling
-------------
Data Sources
~~~~~~~~~~~~
.. code-block:: python
from openclaw.data.sources import YahooFinanceData
# Use Yahoo Finance data
data_source = YahooFinanceData()
# Fetch historical data
data = data_source.get_data(
symbols=["AAPL"],
start_date="2023-01-01",
end_date="2023-12-31",
interval="1d"
)
Custom Data
~~~~~~~~~~~
.. code-block:: python
# Use custom data
import pandas as pd
custom_data = pd.read_csv("my_data.csv", index_col=0, parse_dates=True)
engine = BacktestEngine()
engine.set_data(custom_data)
engine.configure(
symbols=["CUSTOM"],
start_date="2023-01-01",
end_date="2023-12-31"
)
Best Practices
--------------
1. **Out-of-sample testing**: Reserve data for final validation
2. **Transaction costs**: Always include realistic commissions and slippage
3. **Multiple regimes**: Test across different market conditions
4. **Robustness checks**: Sensitivity analysis on parameters
5. **Risk metrics**: Focus on risk-adjusted returns, not just total return
6. **Realistic assumptions**: Account for market impact and liquidity
Common Pitfalls
---------------
* **Overfitting**: Too many parameters optimized on limited data
* **Look-ahead bias**: Using future information in strategy logic
* **Survivorship bias**: Testing only on currently active companies
* **Data mining**: Testing too many strategies on same data
* **Ignoring costs**: Not accounting for fees and slippage