Sufficient Data
Use at least 1 year of data for meaningful results. More for stable strategies.
Test trading strategies against historical data to evaluate performance before live trading.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /backtests | Yes | Create backtest |
GET | /backtests | Yes | List backtests |
GET | /backtests/{id} | Yes | Get backtest details |
POST | /backtests/{id}/run | Yes | Execute backtest |
GET | /backtests/{id}/trades | Yes | Get trade history |
DELETE | /backtests/{id} | Yes | Delete backtest |
GET | /backtests/comparison | Yes | Compare strategies |
POST /api/v1/backtestsAuthorization: Bearer <token>Content-Type: application/json
{ "strategy_id": "550e8400-e29b-41d4-a716-446655440000", "name": "AAPL RSI Backtest 2024", "symbols": ["AAPL"], "start_date": "2024-01-01", "end_date": "2024-12-31", "initial_capital": 100000, "interval": "1d"}{ "id": "660e8400-e29b-41d4-a716-446655440001", "strategy_id": "550e8400-e29b-41d4-a716-446655440000", "name": "AAPL RSI Backtest 2024", "symbols": ["AAPL"], "start_date": "2024-01-01", "end_date": "2024-12-31", "initial_capital": 100000.00, "interval": "1d", "status": "PENDING", "results": null, "error_message": null, "created_at": "2025-12-17T10:30:00Z", "completed_at": null}| Field | Type | Required | Description |
|---|---|---|---|
strategy_id | UUID | Yes | Strategy to backtest |
name | string | Yes | Descriptive name |
symbols | array | Yes | Stock symbols to test |
start_date | date | Yes | Backtest start date |
end_date | date | Yes | Backtest end date |
initial_capital | number | Yes | Starting capital ($) |
interval | string | No | Data interval (default: 1d) |
| Interval | Best For |
|---|---|
1m | Scalping strategies |
5m, 15m | Day trading |
1h | Intraday swing |
1d | Swing/position trading |
Execute a pending backtest.
POST /api/v1/backtests/660e8400-e29b-41d4-a716-446655440001/runAuthorization: Bearer <token>{ "id": "660e8400-e29b-41d4-a716-446655440001", "status": "COMPLETED", "results": { "total_return": 15250.00, "total_return_pct": 15.25, "win_rate": 62.5, "total_trades": 24, "winning_trades": 15, "losing_trades": 9, "profit_factor": 1.85, "max_drawdown": 8.2, "sharpe_ratio": 1.42, "final_capital": 115250.00, "avg_trade_duration": "5.2 days", "best_trade": 2150.00, "worst_trade": -850.00, "avg_win": 1520.00, "avg_loss": -620.00 }, "completed_at": "2025-12-17T10:31:00Z"}| Status | Description |
|---|---|
| PENDING | Created, not yet run |
| RUNNING | Currently executing |
| COMPLETED | Successfully finished |
| FAILED | Error during execution |
| Metric | Description | Good Value |
|---|---|---|
total_return | Absolute profit/loss ($) | Positive |
total_return_pct | Percentage return | > 0% |
win_rate | Winning trades / total trades | > 50% |
profit_factor | Gross profit / gross loss | > 1.5 |
max_drawdown | Largest peak-to-trough decline | < 20% |
sharpe_ratio | Risk-adjusted returns | > 1.0 |
total_trades | Number of completed trades | Strategy-dependent |
avg_trade_duration | Average holding period | Strategy-dependent |
Retrieve the complete trade history for a backtest.
GET /api/v1/backtests/660e8400-e29b-41d4-a716-446655440001/tradesAuthorization: Bearer <token>{ "trades": [ { "id": "770e8400-e29b-41d4-a716-446655440002", "backtest_id": "660e8400-e29b-41d4-a716-446655440001", "timestamp": "2024-01-15T14:30:00Z", "action": "BUY", "symbol": "AAPL", "price": 185.50, "quantity": 50, "position_before": 0, "position_after": 50, "cash_before": 100000.00, "cash_after": 90725.00, "trade_value": 9275.00, "commission": 0.00, "pnl": 0.00, "signal_reason": "RSI < 30 (oversold)" }, { "id": "770e8400-e29b-41d4-a716-446655440003", "timestamp": "2024-02-01T10:15:00Z", "action": "SELL", "symbol": "AAPL", "price": 192.00, "quantity": 50, "position_before": 50, "position_after": 0, "cash_before": 90725.00, "cash_after": 100325.00, "trade_value": 9600.00, "pnl": 325.00, "signal_reason": "RSI > 70 (overbought)" } ], "total": 24}| Field | Description |
|---|---|
timestamp | When trade occurred |
action | BUY or SELL |
symbol | Stock symbol |
price | Execution price |
quantity | Shares traded |
position_before/after | Position size change |
cash_before/after | Cash balance change |
pnl | Profit/loss on this trade |
signal_reason | Why trade was triggered |
Compare multiple strategies on the same symbol and time period.
GET /api/v1/backtests/comparison?symbol=AAPL&start_date=2024-01-01&end_date=2024-12-31Authorization: Bearer <token>{ "symbol": "AAPL", "period": "2024-01-01 to 2024-12-31", "strategies": [ { "strategy_id": "550e8400-...", "strategy_name": "RSI Mean Reversion", "total_return_pct": 15.25, "win_rate": 62.5, "sharpe_ratio": 1.42, "max_drawdown": 8.2, "total_trades": 24 }, { "strategy_id": "550e8400-...", "strategy_name": "MACD Crossover", "total_return_pct": 12.80, "win_rate": 58.3, "sharpe_ratio": 1.28, "max_drawdown": 10.5, "total_trades": 18 } ], "best_by_return": "RSI Mean Reversion", "best_by_sharpe": "RSI Mean Reversion", "best_by_win_rate": "RSI Mean Reversion"}Run multiple strategy-symbol combinations efficiently.
POST /api/v1/backtests/batchAuthorization: Bearer <token>Content-Type: application/json
{ "name": "Q4 2024 Strategy Comparison", "strategy_ids": [ "550e8400-e29b-41d4-a716-446655440000", "550e8400-e29b-41d4-a716-446655440001" ], "symbols": ["AAPL", "MSFT", "GOOGL"], "start_date": "2024-10-01", "end_date": "2024-12-31", "initial_capital": 100000, "interval": "1d"}This creates 6 backtests (2 strategies × 3 symbols).
GET /api/v1/backtests/batch/{batch_id}/progressAuthorization: Bearer <token>{ "batch_id": "880e8400-...", "name": "Q4 2024 Strategy Comparison", "status": "RUNNING", "total_backtests": 6, "completed_backtests": 4, "failed_backtests": 0, "progress_pct": 66.7}GET /api/v1/backtests/matrix?batch_id={batch_id}Authorization: Bearer <token>{ "strategies": ["RSI Mean Reversion", "MACD Crossover"], "symbols": ["AAPL", "MSFT", "GOOGL"], "matrix": { "RSI Mean Reversion": { "AAPL": { "return_pct": 15.2, "sharpe": 1.42 }, "MSFT": { "return_pct": 12.1, "sharpe": 1.15 }, "GOOGL": { "return_pct": 18.5, "sharpe": 1.65 } }, "MACD Crossover": { "AAPL": { "return_pct": 12.8, "sharpe": 1.28 }, "MSFT": { "return_pct": 10.5, "sharpe": 1.05 }, "GOOGL": { "return_pct": 14.2, "sharpe": 1.35 } } }}const api = axios.create({ baseURL: 'http://localhost:8501/api/v1', headers: { 'Authorization': `Bearer ${token}` }});
// Create and run backtestasync function runBacktest(strategyId, symbol, startDate, endDate) { // Create backtest const { data: backtest } = await api.post('/backtests', { strategy_id: strategyId, name: `${symbol} Backtest`, symbols: [symbol], start_date: startDate, end_date: endDate, initial_capital: 100000, interval: '1d' });
console.log(`Created backtest: ${backtest.id}`);
// Run backtest const { data: result } = await api.post(`/backtests/${backtest.id}/run`);
// Display results console.log(`\nResults for ${symbol}:`); console.log(` Return: ${result.results.total_return_pct.toFixed(2)}%`); console.log(` Win Rate: ${result.results.win_rate.toFixed(1)}%`); console.log(` Sharpe: ${result.results.sharpe_ratio.toFixed(2)}`); console.log(` Max Drawdown: ${result.results.max_drawdown.toFixed(1)}%`); console.log(` Trades: ${result.results.total_trades}`);
return result;}
// Analyze tradesasync function analyzeTrades(backtestId) { const { data } = await api.get(`/backtests/${backtestId}/trades`);
const trades = data.trades; const wins = trades.filter(t => t.pnl > 0); const losses = trades.filter(t => t.pnl < 0);
console.log(`\nTrade Analysis:`); console.log(` Total trades: ${trades.length}`); console.log(` Wins: ${wins.length} (${(wins.length/trades.length*100).toFixed(1)}%)`); console.log(` Losses: ${losses.length}`);
// Average win/loss if (wins.length > 0) { const avgWin = wins.reduce((sum, t) => sum + t.pnl, 0) / wins.length; console.log(` Avg Win: $${avgWin.toFixed(2)}`); } if (losses.length > 0) { const avgLoss = losses.reduce((sum, t) => sum + t.pnl, 0) / losses.length; console.log(` Avg Loss: $${avgLoss.toFixed(2)}`); }
return trades;}
// Run batch comparisonasync function compareStrategies(strategyIds, symbols) { // Create batch const { data: batch } = await api.post('/backtests/batch', { name: 'Strategy Comparison', strategy_ids: strategyIds, symbols: symbols, start_date: '2024-01-01', end_date: '2024-12-31', initial_capital: 100000 });
// Run batch await api.post(`/backtests/batch/${batch.id}/run`);
// Poll for completion let progress; do { await new Promise(r => setTimeout(r, 2000)); const { data } = await api.get(`/backtests/batch/${batch.id}/progress`); progress = data; console.log(`Progress: ${progress.progress_pct.toFixed(0)}%`); } while (progress.status === 'RUNNING');
// Get matrix const { data: matrix } = await api.get(`/backtests/matrix?batch_id=${batch.id}`); return matrix;}import requestsimport timefrom typing import List, Dict
class BacktestRunner: def __init__(self, base_url: str, token: str): self.base_url = base_url self.headers = {"Authorization": f"Bearer {token}"}
def create_and_run( self, strategy_id: str, symbol: str, start_date: str, end_date: str, initial_capital: float = 100000 ) -> Dict: """Create and execute a backtest.""" # Create response = requests.post( f"{self.base_url}/backtests", headers=self.headers, json={ "strategy_id": strategy_id, "name": f"{symbol} Backtest", "symbols": [symbol], "start_date": start_date, "end_date": end_date, "initial_capital": initial_capital, "interval": "1d" } ) response.raise_for_status() backtest = response.json()
# Run response = requests.post( f"{self.base_url}/backtests/{backtest['id']}/run", headers=self.headers ) response.raise_for_status() return response.json()
def run_batch( self, strategy_ids: List[str], symbols: List[str], start_date: str, end_date: str ) -> Dict: """Run batch backtest and wait for completion.""" # Create batch response = requests.post( f"{self.base_url}/backtests/batch", headers=self.headers, json={ "name": "Batch Comparison", "strategy_ids": strategy_ids, "symbols": symbols, "start_date": start_date, "end_date": end_date, "initial_capital": 100000 } ) response.raise_for_status() batch = response.json()
# Run requests.post( f"{self.base_url}/backtests/batch/{batch['id']}/run", headers=self.headers )
# Wait for completion while True: response = requests.get( f"{self.base_url}/backtests/batch/{batch['id']}/progress", headers=self.headers ) progress = response.json() print(f"Progress: {progress['progress_pct']:.0f}%")
if progress['status'] != 'RUNNING': break time.sleep(2)
# Get matrix response = requests.get( f"{self.base_url}/backtests/matrix", headers=self.headers, params={"batch_id": batch['id']} ) return response.json()
# Usagerunner = BacktestRunner("http://localhost:8501/api/v1", token)
# Single backtestresult = runner.create_and_run( strategy_id="550e8400-...", symbol="AAPL", start_date="2024-01-01", end_date="2024-12-31")
print(f"Return: {result['results']['total_return_pct']:.2f}%")print(f"Sharpe: {result['results']['sharpe_ratio']:.2f}")
# Batch comparisonmatrix = runner.run_batch( strategy_ids=["strategy1", "strategy2"], symbols=["AAPL", "MSFT", "GOOGL"], start_date="2024-01-01", end_date="2024-12-31")
# Find best strategy-symbol combobest = Nonebest_return = float('-inf')for strategy, symbols in matrix['matrix'].items(): for symbol, metrics in symbols.items(): if metrics['return_pct'] > best_return: best_return = metrics['return_pct'] best = (strategy, symbol)
print(f"Best: {best[0]} on {best[1]} ({best_return:.1f}%)")Sufficient Data
Use at least 1 year of data for meaningful results. More for stable strategies.
Walk-Forward Testing
Use optimization with walk-forward analysis to avoid overfitting.
Multiple Symbols
Test on multiple symbols to ensure strategy robustness.
Compare Metrics
Don’t focus only on returns. Consider Sharpe ratio and max drawdown.