Get Backtest Results
Retrieve detailed results and performance metrics for a completed backtest.
Endpoint
GET /api/backtests/:id
Authentication
Requires authentication via Bearer token.
Authorization: Bearer <access_token>
Request
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Backtest ID |
Response
Success (200 OK)
{
"id": "507f1f77bcf86cd799439013",
"algorithmId": "507f1f77bcf86cd799439011",
"userId": "507f191e810c19729de860ea",
"status": "completed",
"progress": 100,
"startDate": "2024-01-01T00:00:00Z",
"endDate": "2024-01-31T23:59:59Z",
"initialBalance": 100000,
"finalBalance": 115000,
"symbols": ["NSE:RELIANCE", "NSE:TCS"],
"results": {
"totalTrades": 45,
"winningTrades": 28,
"losingTrades": 17,
"totalProfit": 18000,
"totalLoss": 3000,
"netProfit": 15000,
"winRate": 62.22,
"profitFactor": 6.0,
"sharpeRatio": 1.8,
"maxDrawdown": 5.2,
"maxDrawdownPercent": 5.2,
"averageWin": 642.86,
"averageLoss": 176.47,
"largestWin": 2500,
"largestLoss": 500,
"averageTradeDuration": 180,
"expectancy": 333.33,
"returnOnInvestment": 15.0
},
"equityCurve": [
{ "date": "2024-01-01", "balance": 100000 },
{ "date": "2024-01-02", "balance": 101500 },
{ "date": "2024-01-03", "balance": 103000 },
"..."
],
"trades": [
{
"symbol": "NSE:RELIANCE",
"entryTime": "2024-01-02T10:15:00Z",
"entryPrice": 2500,
"exitTime": "2024-01-02T14:30:00Z",
"exitPrice": 2550,
"quantity": 10,
"side": "long",
"profit": 500,
"profitPercent": 2.0,
"exitReason": "take_profit"
},
"..."
],
"createdAt": "2024-01-15T11:00:00Z",
"completedAt": "2024-01-15T11:05:00Z"
}
Running Backtest
If backtest is still running:
{
"id": "507f1f77bcf86cd799439013",
"status": "running",
"progress": 45,
"message": "Processing historical data..."
}
Failed Backtest
If backtest failed:
{
"id": "507f1f77bcf86cd799439013",
"status": "failed",
"error": "Insufficient historical data for NSE:RELIANCE",
"createdAt": "2024-01-15T11:00:00Z",
"failedAt": "2024-01-15T11:02:00Z"
}
Errors
| Status | Code | Message |
|---|---|---|
| 401 | UNAUTHORIZED | Authentication required |
| 403 | FORBIDDEN | Access denied (not your backtest) |
| 404 | NOT_FOUND | Backtest not found |
Examples
Get Backtest Results
cURL:
curl -X GET https://api.x3algo.com/api/backtests/507f1f77bcf86cd799439013 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
JavaScript:
const backtestId = '507f1f77bcf86cd799439013'
const response = await fetch(
`https://api.x3algo.com/api/backtests/${backtestId}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
)
const backtest = await response.json()
if (backtest.status === 'completed') {
console.log('Win Rate:', backtest.results.winRate + '%')
console.log('Profit Factor:', backtest.results.profitFactor)
console.log('Net Profit:', backtest.results.netProfit)
} else {
console.log('Status:', backtest.status)
console.log('Progress:', backtest.progress + '%')
}
Python:
backtest_id = '507f1f77bcf86cd799439013'
response = requests.get(
f'https://api.x3algo.com/api/backtests/{backtest_id}',
headers={'Authorization': f'Bearer {access_token}'}
)
backtest = response.json()
if backtest['status'] == 'completed':
results = backtest['results']
print(f"Win Rate: {results['winRate']}%")
print(f"Profit Factor: {results['profitFactor']}")
print(f"Net Profit: {results['netProfit']}")
Analyze Equity Curve
JavaScript:
const backtest = await getBacktestResults(backtestId)
// Calculate drawdown periods
const equityCurve = backtest.equityCurve
let peak = equityCurve[0].balance
let maxDrawdown = 0
for (const point of equityCurve) {
if (point.balance > peak) {
peak = point.balance
}
const drawdown = ((peak - point.balance) / peak) * 100
if (drawdown > maxDrawdown) {
maxDrawdown = drawdown
}
}
console.log('Max Drawdown:', maxDrawdown.toFixed(2) + '%')
Analyze Trade Distribution
JavaScript:
const backtest = await getBacktestResults(backtestId)
// Group trades by exit reason
const tradesByReason = backtest.trades.reduce((acc, trade) => {
acc[trade.exitReason] = (acc[trade.exitReason] || 0) + 1
return acc
}, {})
console.log('Trades by Exit Reason:', tradesByReason)
// Calculate average profit by symbol
const profitBySymbol = backtest.trades.reduce((acc, trade) => {
if (!acc[trade.symbol]) {
acc[trade.symbol] = { total: 0, count: 0 }
}
acc[trade.symbol].total += trade.profit
acc[trade.symbol].count++
return acc
}, {})
for (const [symbol, data] of Object.entries(profitBySymbol)) {
const avgProfit = data.total / data.count
console.log(`${symbol}: ${avgProfit.toFixed(2)} avg profit`)
}
Performance Metrics Explained
| Metric | Description | Good Value |
|---|---|---|
| Win Rate | Percentage of winning trades | > 50% |
| Profit Factor | Total profit / Total loss | > 1.5 |
| Sharpe Ratio | Risk-adjusted returns | > 1.0 |
| Max Drawdown | Largest peak-to-trough decline | < 20% |
| Expectancy | Average profit per trade | > 0 |
| ROI | Return on investment | > 10% annually |
Trade Fields
| Field | Description |
|---|---|
| symbol | Trading symbol |
| entryTime | Entry timestamp |
| entryPrice | Entry price |
| exitTime | Exit timestamp |
| exitPrice | Exit price |
| quantity | Position size |
| side | long or short |
| profit | Profit/loss amount |
| profitPercent | Profit/loss percentage |
| exitReason | Why position closed: take_profit, stop_loss, trailing_stop, opposite_signal, time_exit |
Notes
- Results are cached for 30 days
- Equity curve shows daily balance
- All trades include entry/exit details
- Slippage is included in calculations
- Commission/fees are applied if configured