mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-01 17:17:24 -04:00
docs: update /results endpoint documentation for date range support
Update API_REFERENCE.md to reflect the new date range query functionality in the /results endpoint: - Replace 'date' parameter with 'start_date' and 'end_date' - Document single-date vs date range response formats - Add period metrics calculations (period return, annualized return) - Document default behavior (last 30 days) - Update error responses for new validation rules - Update Python and TypeScript client examples - Add edge trimming behavior documentation
This commit is contained in:
340
API_REFERENCE.md
340
API_REFERENCE.md
@@ -343,70 +343,43 @@ Poll every 10-30 seconds until `status` is `completed`, `partial`, or `failed`.
|
||||
|
||||
### GET /results
|
||||
|
||||
Get trading results grouped by day with daily P&L metrics and AI reasoning.
|
||||
Get trading results with optional date range and portfolio performance metrics.
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `job_id` | string | No | Filter by job UUID |
|
||||
| `date` | string | No | Filter by trading date (YYYY-MM-DD) |
|
||||
| `start_date` | string | No | Start date (YYYY-MM-DD). If provided alone, acts as single date. If omitted, defaults to 30 days ago. |
|
||||
| `end_date` | string | No | End date (YYYY-MM-DD). If provided alone, acts as single date. If omitted, defaults to today. |
|
||||
| `model` | string | No | Filter by model signature |
|
||||
| `reasoning` | string | No | Include AI reasoning: `none` (default), `summary`, or `full` |
|
||||
| `job_id` | string | No | Filter by job UUID |
|
||||
| `reasoning` | string | No | Include reasoning: `none` (default), `summary`, or `full`. Ignored for date range queries. |
|
||||
|
||||
**Response (200 OK) - Default (no reasoning):**
|
||||
**Breaking Change:**
|
||||
- The `date` parameter has been removed. Use `start_date` and/or `end_date` instead.
|
||||
- Requests using `date` will receive `422 Unprocessable Entity` error.
|
||||
|
||||
**Default Behavior:**
|
||||
- If no dates provided: Returns last 30 days (configurable via `DEFAULT_RESULTS_LOOKBACK_DAYS`)
|
||||
- If only `start_date`: Single-date query (end_date = start_date)
|
||||
- If only `end_date`: Single-date query (start_date = end_date)
|
||||
- If both provided and equal: Single-date query (detailed format)
|
||||
- If both provided and different: Date range query (metrics format)
|
||||
|
||||
**Response - Single Date (detailed):**
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 2,
|
||||
"count": 1,
|
||||
"results": [
|
||||
{
|
||||
"date": "2025-01-15",
|
||||
"model": "gpt-4",
|
||||
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"starting_position": {
|
||||
"holdings": [],
|
||||
"cash": 10000.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"daily_metrics": {
|
||||
"profit": 0.0,
|
||||
"return_pct": 0.0,
|
||||
"days_since_last_trading": 0
|
||||
},
|
||||
"trades": [
|
||||
{
|
||||
"action_type": "buy",
|
||||
"symbol": "AAPL",
|
||||
"quantity": 10,
|
||||
"price": 150.0,
|
||||
"created_at": "2025-01-15T14:30:00Z"
|
||||
}
|
||||
],
|
||||
"final_position": {
|
||||
"holdings": [
|
||||
{"symbol": "AAPL", "quantity": 10}
|
||||
],
|
||||
"cash": 8500.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"metadata": {
|
||||
"total_actions": 1,
|
||||
"session_duration_seconds": 45.2,
|
||||
"completed_at": "2025-01-15T14:31:00Z"
|
||||
},
|
||||
"reasoning": null
|
||||
},
|
||||
{
|
||||
"date": "2025-01-16",
|
||||
"model": "gpt-4",
|
||||
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"job_id": "550e8400-...",
|
||||
"starting_position": {
|
||||
"holdings": [
|
||||
{"symbol": "AAPL", "quantity": 10}
|
||||
],
|
||||
"holdings": [{"symbol": "AAPL", "quantity": 10}],
|
||||
"cash": 8500.0,
|
||||
"portfolio_value": 10100.0
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"daily_metrics": {
|
||||
"profit": 100.0,
|
||||
@@ -441,226 +414,79 @@ Get trading results grouped by day with daily P&L metrics and AI reasoning.
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK) - With Summary Reasoning:**
|
||||
**Response - Date Range (metrics):**
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 1,
|
||||
"results": [
|
||||
{
|
||||
"date": "2025-01-15",
|
||||
"model": "gpt-4",
|
||||
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"starting_position": {
|
||||
"holdings": [],
|
||||
"cash": 10000.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"daily_metrics": {
|
||||
"profit": 0.0,
|
||||
"return_pct": 0.0,
|
||||
"days_since_last_trading": 0
|
||||
},
|
||||
"trades": [
|
||||
{
|
||||
"action_type": "buy",
|
||||
"symbol": "AAPL",
|
||||
"quantity": 10,
|
||||
"price": 150.0,
|
||||
"created_at": "2025-01-15T14:30:00Z"
|
||||
}
|
||||
"start_date": "2025-01-16",
|
||||
"end_date": "2025-01-20",
|
||||
"daily_portfolio_values": [
|
||||
{"date": "2025-01-16", "portfolio_value": 10100.0},
|
||||
{"date": "2025-01-17", "portfolio_value": 10250.0},
|
||||
{"date": "2025-01-20", "portfolio_value": 10500.0}
|
||||
],
|
||||
"final_position": {
|
||||
"holdings": [
|
||||
{"symbol": "AAPL", "quantity": 10}
|
||||
],
|
||||
"cash": 8500.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"metadata": {
|
||||
"total_actions": 1,
|
||||
"session_duration_seconds": 45.2,
|
||||
"completed_at": "2025-01-15T14:31:00Z"
|
||||
},
|
||||
"reasoning": "Analyzed AAPL earnings report showing strong Q4 results. Bought 10 shares at $150 based on positive revenue guidance and expanding margins."
|
||||
"period_metrics": {
|
||||
"starting_portfolio_value": 10000.0,
|
||||
"ending_portfolio_value": 10500.0,
|
||||
"period_return_pct": 5.0,
|
||||
"annualized_return_pct": 45.6,
|
||||
"calendar_days": 5,
|
||||
"trading_days": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200 OK) - With Full Reasoning:**
|
||||
**Period Metrics Calculations:**
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 1,
|
||||
"results": [
|
||||
{
|
||||
"date": "2025-01-15",
|
||||
"model": "gpt-4",
|
||||
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"starting_position": {
|
||||
"holdings": [],
|
||||
"cash": 10000.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"daily_metrics": {
|
||||
"profit": 0.0,
|
||||
"return_pct": 0.0,
|
||||
"days_since_last_trading": 0
|
||||
},
|
||||
"trades": [
|
||||
{
|
||||
"action_type": "buy",
|
||||
"symbol": "AAPL",
|
||||
"quantity": 10,
|
||||
"price": 150.0,
|
||||
"created_at": "2025-01-15T14:30:00Z"
|
||||
}
|
||||
],
|
||||
"final_position": {
|
||||
"holdings": [
|
||||
{"symbol": "AAPL", "quantity": 10}
|
||||
],
|
||||
"cash": 8500.0,
|
||||
"portfolio_value": 10000.0
|
||||
},
|
||||
"metadata": {
|
||||
"total_actions": 1,
|
||||
"session_duration_seconds": 45.2,
|
||||
"completed_at": "2025-01-15T14:31:00Z"
|
||||
},
|
||||
"reasoning": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "You are a trading agent. Current date: 2025-01-15..."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'll analyze market conditions for AAPL..."
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"name": "search",
|
||||
"content": "AAPL Q4 earnings beat expectations..."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Based on positive earnings, I'll buy AAPL..."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
- `period_return_pct` = ((ending - starting) / starting) × 100
|
||||
- `annualized_return_pct` = ((ending / starting) ^ (365 / calendar_days) - 1) × 100
|
||||
- `calendar_days` = Calendar days from start_date to end_date (inclusive)
|
||||
- `trading_days` = Number of actual trading days with data
|
||||
|
||||
**Response Fields:**
|
||||
**Edge Trimming:**
|
||||
|
||||
**Top-level:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `count` | integer | Number of trading days returned |
|
||||
| `results` | array[object] | Array of day-level trading results |
|
||||
If requested range extends beyond available data, the response is trimmed to actual data boundaries:
|
||||
|
||||
**Day-level fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `date` | string | Trading date (YYYY-MM-DD) |
|
||||
| `model` | string | Model signature |
|
||||
| `job_id` | string | Simulation job UUID |
|
||||
| `starting_position` | object | Portfolio state at start of day |
|
||||
| `daily_metrics` | object | Daily performance metrics |
|
||||
| `trades` | array[object] | All trades executed during the day |
|
||||
| `final_position` | object | Portfolio state at end of day |
|
||||
| `metadata` | object | Session metadata |
|
||||
| `reasoning` | null\|string\|array | AI reasoning (based on `reasoning` parameter) |
|
||||
- Request: `start_date=2025-01-10&end_date=2025-01-20`
|
||||
- Available: 2025-01-15, 2025-01-16, 2025-01-17
|
||||
- Response: `start_date=2025-01-15`, `end_date=2025-01-17`
|
||||
|
||||
**starting_position fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `holdings` | array[object] | Stock positions at start of day (from previous day's ending) |
|
||||
| `cash` | float | Cash balance at start of day |
|
||||
| `portfolio_value` | float | Total portfolio value at start (cash + holdings valued at current prices) |
|
||||
**Error Responses:**
|
||||
|
||||
**daily_metrics fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `profit` | float | Dollar amount gained/lost from previous close (portfolio appreciation/depreciation) |
|
||||
| `return_pct` | float | Percentage return from previous close |
|
||||
| `days_since_last_trading` | integer | Number of calendar days since last trading day (1=normal, 3=weekend, 0=first day) |
|
||||
|
||||
**trades fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `action_type` | string | Trade type: `buy`, `sell`, or `no_trade` |
|
||||
| `symbol` | string\|null | Stock symbol (null for `no_trade`) |
|
||||
| `quantity` | integer\|null | Number of shares (null for `no_trade`) |
|
||||
| `price` | float\|null | Execution price per share (null for `no_trade`) |
|
||||
| `created_at` | string | ISO 8601 timestamp of trade execution |
|
||||
|
||||
**final_position fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `holdings` | array[object] | Stock positions at end of day |
|
||||
| `cash` | float | Cash balance at end of day |
|
||||
| `portfolio_value` | float | Total portfolio value at end (cash + holdings valued at closing prices) |
|
||||
|
||||
**metadata fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `total_actions` | integer | Number of trades executed during the day |
|
||||
| `session_duration_seconds` | float\|null | AI session duration in seconds |
|
||||
| `completed_at` | string\|null | ISO 8601 timestamp of session completion |
|
||||
|
||||
**holdings object:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `symbol` | string | Stock symbol |
|
||||
| `quantity` | integer | Number of shares held |
|
||||
|
||||
**reasoning field:**
|
||||
- `null` when `reasoning=none` (default) - no reasoning included
|
||||
- `string` when `reasoning=summary` - AI-generated 2-3 sentence summary of trading strategy
|
||||
- `array` when `reasoning=full` - Complete conversation log with all messages, tool calls, and responses
|
||||
|
||||
**Daily P&L Calculation:**
|
||||
|
||||
Daily profit/loss is calculated by valuing the previous day's ending holdings at current day's opening prices:
|
||||
|
||||
1. **First trading day**: `daily_profit = 0`, `daily_return_pct = 0` (no previous holdings to appreciate/depreciate)
|
||||
2. **Subsequent days**:
|
||||
- Value yesterday's ending holdings at today's opening prices
|
||||
- `daily_profit = today_portfolio_value - yesterday_portfolio_value`
|
||||
- `daily_return_pct = (daily_profit / yesterday_portfolio_value) * 100`
|
||||
|
||||
This accurately captures portfolio appreciation from price movements, not just trading decisions.
|
||||
|
||||
**Weekend Gap Handling:**
|
||||
|
||||
The system correctly handles multi-day gaps (weekends, holidays):
|
||||
- `days_since_last_trading` shows actual calendar days elapsed (e.g., 3 for Monday following Friday)
|
||||
- Daily P&L reflects cumulative price changes over the gap period
|
||||
- Holdings chain remains consistent (Monday starts with Friday's ending positions)
|
||||
| Status | Scenario | Response |
|
||||
|--------|----------|----------|
|
||||
| 404 | No data matches filters | `{"detail": "No trading data found for the specified filters"}` |
|
||||
| 400 | Invalid date format | `{"detail": "Invalid date format. Expected YYYY-MM-DD"}` |
|
||||
| 400 | start_date > end_date | `{"detail": "start_date must be <= end_date"}` |
|
||||
| 400 | Future dates | `{"detail": "Cannot query future dates"}` |
|
||||
| 422 | Using old `date` param | `{"detail": "Parameter 'date' has been removed. Use 'start_date' and/or 'end_date' instead."}` |
|
||||
|
||||
**Examples:**
|
||||
|
||||
All results for a specific job (no reasoning):
|
||||
Single date query:
|
||||
```bash
|
||||
curl "http://localhost:8080/results?job_id=550e8400-e29b-41d4-a716-446655440000"
|
||||
curl "http://localhost:8080/results?start_date=2025-01-16&model=gpt-4"
|
||||
```
|
||||
|
||||
Results for a specific date with summary reasoning:
|
||||
Date range query:
|
||||
```bash
|
||||
curl "http://localhost:8080/results?date=2025-01-16&reasoning=summary"
|
||||
curl "http://localhost:8080/results?start_date=2025-01-16&end_date=2025-01-20&model=gpt-4"
|
||||
```
|
||||
|
||||
Results for a specific model with full reasoning:
|
||||
Default (last 30 days):
|
||||
```bash
|
||||
curl "http://localhost:8080/results?model=gpt-4&reasoning=full"
|
||||
curl "http://localhost:8080/results"
|
||||
```
|
||||
|
||||
Combine filters:
|
||||
With filters:
|
||||
```bash
|
||||
curl "http://localhost:8080/results?job_id=550e8400-e29b-41d4-a716-446655440000&date=2025-01-16&model=gpt-4&reasoning=summary"
|
||||
curl "http://localhost:8080/results?job_id=550e8400-...&start_date=2025-01-16&end_date=2025-01-20"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1049,13 +875,23 @@ class AITraderServerClient:
|
||||
return status
|
||||
time.sleep(poll_interval)
|
||||
|
||||
def get_results(self, job_id=None, date=None, model=None):
|
||||
"""Query results with optional filters."""
|
||||
params = {}
|
||||
def get_results(self, start_date=None, end_date=None, job_id=None, model=None, reasoning="none"):
|
||||
"""Query results with optional filters and date range.
|
||||
|
||||
Args:
|
||||
start_date: Start date (YYYY-MM-DD) or None
|
||||
end_date: End date (YYYY-MM-DD) or None
|
||||
job_id: Job ID filter
|
||||
model: Model signature filter
|
||||
reasoning: Reasoning level (none/summary/full)
|
||||
"""
|
||||
params = {"reasoning": reasoning}
|
||||
if start_date:
|
||||
params["start_date"] = start_date
|
||||
if end_date:
|
||||
params["end_date"] = end_date
|
||||
if job_id:
|
||||
params["job_id"] = job_id
|
||||
if date:
|
||||
params["date"] = date
|
||||
if model:
|
||||
params["model"] = model
|
||||
|
||||
@@ -1095,6 +931,13 @@ job = client.trigger_simulation(end_date="2025-01-31", models=["gpt-4"])
|
||||
result = client.wait_for_completion(job["job_id"])
|
||||
results = client.get_results(job_id=job["job_id"])
|
||||
|
||||
# Get results for date range
|
||||
range_results = client.get_results(
|
||||
start_date="2025-01-16",
|
||||
end_date="2025-01-20",
|
||||
model="gpt-4"
|
||||
)
|
||||
|
||||
# Get reasoning logs (summaries only)
|
||||
reasoning = client.get_reasoning(job_id=job["job_id"])
|
||||
|
||||
@@ -1161,13 +1004,17 @@ class AITraderServerClient {
|
||||
|
||||
async getResults(filters: {
|
||||
jobId?: string;
|
||||
date?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
model?: string;
|
||||
reasoning?: string;
|
||||
} = {}) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.jobId) params.set("job_id", filters.jobId);
|
||||
if (filters.date) params.set("date", filters.date);
|
||||
if (filters.startDate) params.set("start_date", filters.startDate);
|
||||
if (filters.endDate) params.set("end_date", filters.endDate);
|
||||
if (filters.model) params.set("model", filters.model);
|
||||
if (filters.reasoning) params.set("reasoning", filters.reasoning);
|
||||
|
||||
const response = await fetch(
|
||||
`${this.baseUrl}/results?${params.toString()}`
|
||||
@@ -1220,6 +1067,13 @@ const job3 = await client.triggerSimulation("2025-01-31", {
|
||||
const result = await client.waitForCompletion(job1.job_id);
|
||||
const results = await client.getResults({ jobId: job1.job_id });
|
||||
|
||||
// Get results for date range
|
||||
const rangeResults = await client.getResults({
|
||||
startDate: "2025-01-16",
|
||||
endDate: "2025-01-20",
|
||||
model: "gpt-4"
|
||||
});
|
||||
|
||||
// Get reasoning logs (summaries only)
|
||||
const reasoning = await client.getReasoning({ jobId: job1.job_id });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user