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
30 KiB
AI-Trader-Server API Reference
Complete reference for the AI-Trader-Server REST API service.
Base URL: http://localhost:8080 (default)
API Version: 1.0.0
Endpoints
POST /simulate/trigger
Trigger a new simulation job for a specified date range and models.
Supports three operational modes:
- Explicit date range: Provide both
start_dateandend_date - Single date: Set
start_date=end_date - Resume mode: Set
start_datetonullto continue from each model's last completed date
Request Body:
{
"start_date": "2025-01-16",
"end_date": "2025-01-17",
"models": ["gpt-4", "claude-3.7-sonnet"],
"replace_existing": false
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
start_date |
string | null | No | Start date in YYYY-MM-DD format. If null, enables resume mode (each model continues from its last completed date). Defaults to null. |
end_date |
string | Yes | End date in YYYY-MM-DD format. Required - cannot be null or empty. |
models |
array[string] | No | Model signatures to run. If omitted or empty array, uses all enabled models from server config. |
replace_existing |
boolean | No | If false (default), skips already-completed model-days (idempotent). If true, re-runs all dates even if previously completed. |
Response (200 OK):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"total_model_days": 4,
"message": "Simulation job created with 2 trading dates"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
job_id |
string | Unique UUID for this simulation job |
status |
string | Job status: pending, running, completed, partial, or failed |
total_model_days |
integer | Total number of model-day combinations to execute |
message |
string | Human-readable status message |
Error Responses:
400 Bad Request - Invalid parameters or validation failure
{
"detail": "Invalid date format: 2025-1-16. Expected YYYY-MM-DD"
}
400 Bad Request - Another job is already running
{
"detail": "Another simulation job is already running or pending. Please wait for it to complete."
}
500 Internal Server Error - Server configuration issue
{
"detail": "Server configuration file not found: configs/default_config.json"
}
503 Service Unavailable - Price data download failed
{
"detail": "Failed to download any price data. Check ALPHAADVANTAGE_API_KEY."
}
Validation Rules:
- Date format: Must be YYYY-MM-DD
- Date validity: Must be valid calendar dates
- Date order:
start_datemust be <=end_date(whenstart_dateis not null) - end_date required: Cannot be null or empty string
- Future dates: Cannot simulate future dates (must be <= today)
- Date range limit: Maximum 30 days (configurable via
MAX_SIMULATION_DAYS) - Model signatures: Must match models defined in server configuration
- Concurrency: Only one simulation job can run at a time
Behavior:
- Validates date range and parameters
- Determines which models to run (from request or server config)
- Resume mode (if
start_dateis null):- For each model, queries last completed simulation date
- If no previous data exists (cold start), uses
end_dateas single-day simulation - Otherwise, resumes from day after last completed date
- Each model can have different resume start dates
- Idempotent mode (if
replace_existing=false, default):- Queries database for already-completed model-day combinations in date range
- Skips completed model-days, only creates tasks for gaps
- Returns error if all requested dates are already completed
- Checks for missing price data in date range
- Downloads missing data if
AUTO_DOWNLOAD_PRICE_DATA=true(default) - Identifies trading dates with complete price data (all symbols available)
- Creates job in database with status
pending(only for model-days that will actually run) - Starts background worker thread
- Returns immediately with job ID
Examples:
Single day, single model:
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{
"start_date": "2025-01-16",
"end_date": "2025-01-16",
"models": ["gpt-4"]
}'
Date range, all enabled models:
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{
"start_date": "2025-01-16",
"end_date": "2025-01-20"
}'
Resume from last completed date:
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{
"start_date": null,
"end_date": "2025-01-31",
"models": ["gpt-4"]
}'
Idempotent simulation (skip already-completed dates):
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{
"start_date": "2025-01-16",
"end_date": "2025-01-20",
"models": ["gpt-4"],
"replace_existing": false
}'
Re-run existing dates (force replace):
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{
"start_date": "2025-01-16",
"end_date": "2025-01-20",
"models": ["gpt-4"],
"replace_existing": true
}'
GET /simulate/status/{job_id}
Get status and progress of a simulation job.
URL Parameters:
| Parameter | Type | Description |
|---|---|---|
job_id |
string | Job UUID from trigger response |
Response (200 OK):
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "running",
"progress": {
"total_model_days": 4,
"completed": 2,
"failed": 0,
"pending": 2
},
"date_range": ["2025-01-16", "2025-01-17"],
"models": ["gpt-4", "claude-3.7-sonnet"],
"created_at": "2025-01-16T10:00:00Z",
"started_at": "2025-01-16T10:00:05Z",
"completed_at": null,
"total_duration_seconds": null,
"error": null,
"details": [
{
"model_signature": "gpt-4",
"trading_date": "2025-01-16",
"status": "completed",
"start_time": "2025-01-16T10:00:05Z",
"end_time": "2025-01-16T10:05:23Z",
"duration_seconds": 318.5,
"error": null
},
{
"model_signature": "claude-3.7-sonnet",
"trading_date": "2025-01-16",
"status": "completed",
"start_time": "2025-01-16T10:05:24Z",
"end_time": "2025-01-16T10:10:12Z",
"duration_seconds": 288.0,
"error": null
},
{
"model_signature": "gpt-4",
"trading_date": "2025-01-17",
"status": "running",
"start_time": "2025-01-16T10:10:13Z",
"end_time": null,
"duration_seconds": null,
"error": null
},
{
"model_signature": "claude-3.7-sonnet",
"trading_date": "2025-01-17",
"status": "pending",
"start_time": null,
"end_time": null,
"duration_seconds": null,
"error": null
}
]
}
Response Fields:
| Field | Type | Description |
|---|---|---|
job_id |
string | Job UUID |
status |
string | Overall job status |
progress |
object | Progress summary |
progress.total_model_days |
integer | Total model-day combinations |
progress.completed |
integer | Successfully completed model-days |
progress.failed |
integer | Failed model-days |
progress.pending |
integer | Not yet started model-days |
date_range |
array[string] | Trading dates in this job |
models |
array[string] | Model signatures in this job |
created_at |
string | ISO 8601 timestamp when job was created |
started_at |
string | ISO 8601 timestamp when execution began |
completed_at |
string | ISO 8601 timestamp when job finished |
total_duration_seconds |
float | Total execution time in seconds |
error |
string | Error message if job failed |
details |
array[object] | Per model-day execution details |
warnings |
array[string] | Optional array of non-fatal warning messages |
Job Status Values:
| Status | Description |
|---|---|
pending |
Job created, waiting to start |
downloading_data |
Preparing price data (downloading if needed) |
running |
Job currently executing |
completed |
All model-days completed successfully |
partial |
Some model-days completed, some failed |
failed |
All model-days failed |
Model-Day Status Values:
| Status | Description |
|---|---|
pending |
Not started yet |
running |
Currently executing |
completed |
Finished successfully |
failed |
Execution failed (see error field) |
Warnings Field:
The optional warnings array contains non-fatal warning messages about the job execution:
- Rate limit warnings: Price data download hit API rate limits
- Skipped dates: Some dates couldn't be processed due to incomplete price data
- Other issues: Non-fatal problems that don't prevent job completion
Example response with warnings:
{
"job_id": "019a426b-1234-5678-90ab-cdef12345678",
"status": "completed",
"progress": {
"total_model_days": 10,
"completed": 8,
"failed": 0,
"pending": 0
},
"warnings": [
"Rate limit reached - downloaded 12/15 symbols",
"Skipped 2 dates due to incomplete price data: ['2025-10-02', '2025-10-05']"
]
}
If no warnings occurred, the field will be null or omitted.
Error Response:
404 Not Found - Job doesn't exist
{
"detail": "Job 550e8400-e29b-41d4-a716-446655440000 not found"
}
Example:
curl http://localhost:8080/simulate/status/550e8400-e29b-41d4-a716-446655440000
Polling Recommendation:
Poll every 10-30 seconds until status is completed, partial, or failed.
GET /results
Get trading results with optional date range and portfolio performance metrics.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
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 |
job_id |
string | No | Filter by job UUID |
reasoning |
string | No | Include reasoning: none (default), summary, or full. Ignored for date range queries. |
Breaking Change:
- The
dateparameter has been removed. Usestart_dateand/orend_dateinstead. - Requests using
datewill receive422 Unprocessable Entityerror.
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):
{
"count": 1,
"results": [
{
"date": "2025-01-16",
"model": "gpt-4",
"job_id": "550e8400-...",
"starting_position": {
"holdings": [{"symbol": "AAPL", "quantity": 10}],
"cash": 8500.0,
"portfolio_value": 10000.0
},
"daily_metrics": {
"profit": 100.0,
"return_pct": 1.0,
"days_since_last_trading": 1
},
"trades": [
{
"action_type": "buy",
"symbol": "MSFT",
"quantity": 5,
"price": 200.0,
"created_at": "2025-01-16T14:30:00Z"
}
],
"final_position": {
"holdings": [
{"symbol": "AAPL", "quantity": 10},
{"symbol": "MSFT", "quantity": 5}
],
"cash": 7500.0,
"portfolio_value": 10100.0
},
"metadata": {
"total_actions": 1,
"session_duration_seconds": 52.1,
"completed_at": "2025-01-16T14:31:00Z"
},
"reasoning": null
}
]
}
Response - Date Range (metrics):
{
"count": 1,
"results": [
{
"model": "gpt-4",
"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}
],
"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
}
}
]
}
Period Metrics Calculations:
period_return_pct= ((ending - starting) / starting) × 100annualized_return_pct= ((ending / starting) ^ (365 / calendar_days) - 1) × 100calendar_days= Calendar days from start_date to end_date (inclusive)trading_days= Number of actual trading days with data
Edge Trimming:
If requested range extends beyond available data, the response is trimmed to actual data boundaries:
- 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
Error Responses:
| 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:
Single date query:
curl "http://localhost:8080/results?start_date=2025-01-16&model=gpt-4"
Date range query:
curl "http://localhost:8080/results?start_date=2025-01-16&end_date=2025-01-20&model=gpt-4"
Default (last 30 days):
curl "http://localhost:8080/results"
With filters:
curl "http://localhost:8080/results?job_id=550e8400-...&start_date=2025-01-16&end_date=2025-01-20"
GET /health
Health check endpoint for monitoring and orchestration services.
Response (200 OK):
{
"status": "healthy",
"database": "connected",
"timestamp": "2025-01-16T10:00:00Z"
}
Response Fields:
| Field | Type | Description |
|---|---|---|
status |
string | Overall service health: healthy or unhealthy |
database |
string | Database connection status: connected or disconnected |
timestamp |
string | ISO 8601 timestamp of health check |
Example:
curl http://localhost:8080/health
Usage:
- Docker health checks:
HEALTHCHECK CMD curl -f http://localhost:8080/health - Monitoring systems: Poll every 30-60 seconds
- Orchestration services: Verify availability before triggering simulations
Deployment Mode
All API responses include a deployment_mode field indicating whether the service is running in production or development mode.
Response Format
{
"job_id": "abc123",
"status": "completed",
"deployment_mode": "DEV",
"is_dev_mode": true,
"preserve_dev_data": false
}
Fields:
deployment_mode: "PROD" or "DEV"is_dev_mode: Boolean flagpreserve_dev_data: Null in PROD, boolean in DEV
DEV Mode Behavior
When DEPLOYMENT_MODE=DEV is set:
- No AI API calls (mock responses)
- Separate dev database (
jobs_dev.db) - Separate data directory (
dev_agent_data/) - Database reset on startup (unless PRESERVE_DEV_DATA=true)
Health Check Example:
curl http://localhost:8080/health
Response in DEV mode:
{
"status": "healthy",
"database": "connected",
"timestamp": "2025-01-16T10:00:00Z",
"deployment_mode": "DEV",
"is_dev_mode": true,
"preserve_dev_data": false
}
Use Cases
- Testing: Validate orchestration without AI API costs
- CI/CD: Automated testing in pipelines
- Development: Rapid iteration on system logic
- Configuration validation: Test settings before production
Common Workflows
Trigger and Monitor a Simulation
- Trigger simulation:
RESPONSE=$(curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{"start_date": "2025-01-16", "end_date": "2025-01-17", "models": ["gpt-4"]}')
JOB_ID=$(echo $RESPONSE | jq -r '.job_id')
echo "Job ID: $JOB_ID"
Or use resume mode:
RESPONSE=$(curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d '{"start_date": null, "end_date": "2025-01-31", "models": ["gpt-4"]}')
JOB_ID=$(echo $RESPONSE | jq -r '.job_id')
- Poll for completion:
while true; do
STATUS=$(curl -s http://localhost:8080/simulate/status/$JOB_ID | jq -r '.status')
echo "Status: $STATUS"
if [[ "$STATUS" == "completed" ]] || [[ "$STATUS" == "partial" ]] || [[ "$STATUS" == "failed" ]]; then
break
fi
sleep 10
done
- Retrieve results:
curl "http://localhost:8080/results?job_id=$JOB_ID" | jq '.'
Scheduled Daily Simulations
Use a scheduler (cron, Airflow, etc.) to trigger simulations:
Option 1: Resume mode (recommended)
#!/bin/bash
# daily_simulation.sh - Resume from last completed date
# Calculate today's date
TODAY=$(date +%Y-%m-%d)
# Trigger simulation in resume mode
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d "{\"start_date\": null, \"end_date\": \"$TODAY\", \"models\": [\"gpt-4\"]}"
Option 2: Explicit yesterday's date
#!/bin/bash
# daily_simulation.sh - Run specific date
# Calculate yesterday's date
DATE=$(date -d "yesterday" +%Y-%m-%d)
# Trigger simulation
curl -X POST http://localhost:8080/simulate/trigger \
-H "Content-Type: application/json" \
-d "{\"start_date\": \"$DATE\", \"end_date\": \"$DATE\", \"models\": [\"gpt-4\"]}"
Add to crontab:
0 6 * * * /path/to/daily_simulation.sh
Error Handling
All endpoints return consistent error responses with HTTP status codes and detail messages.
Common Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid date format, invalid parameters, concurrent job running |
| 404 | Not Found | Job ID doesn't exist |
| 500 | Internal Server Error | Server misconfiguration, missing config file |
| 503 | Service Unavailable | Price data download failed, database unavailable |
Error Response Format
{
"detail": "Human-readable error message"
}
Retry Recommendations
- 400 errors: Fix request parameters, don't retry
- 404 errors: Verify job ID, don't retry
- 500 errors: Check server logs, investigate before retrying
- 503 errors: Retry with exponential backoff (wait 1s, 2s, 4s, etc.)
Rate Limits and Constraints
Concurrency
- Maximum concurrent jobs: 1 (configurable via
MAX_CONCURRENT_JOBS) - Attempting to start a second job returns: 400 Bad Request
Date Range Limits
- Maximum date range: 30 days (configurable via
MAX_SIMULATION_DAYS) - Attempting longer range returns: 400 Bad Request
Price Data
- Alpha Vantage API rate limit: 5 requests/minute (free tier), 75 requests/minute (premium)
- Automatic download: Enabled by default (
AUTO_DOWNLOAD_PRICE_DATA=true) - Behavior when rate limited: Partial data downloaded, simulation continues with available dates
Data Persistence
All simulation data is stored in SQLite database at data/jobs.db.
Database Tables
- jobs - Job metadata and status
- job_details - Per model-day execution details
- trading_days - Day-centric trading results with daily P&L metrics
- holdings - Portfolio holdings snapshots (ending positions only)
- actions - Trade execution ledger
- tool_usage - MCP tool usage statistics
- price_data - Historical price data cache
- price_coverage - Data availability tracking
See docs/developer/database-schema.md for complete schema reference.
Data Retention
- Job data persists indefinitely by default
- Results can be queried at any time after job completion
- Manual cleanup: Delete rows from
jobstable (cascades to related tables)
Configuration
API behavior is controlled via environment variables and server configuration file.
Environment Variables
See docs/reference/environment-variables.md for complete reference.
Key variables:
API_PORT- API server port (default: 8080)MAX_CONCURRENT_JOBS- Maximum concurrent simulations (default: 1)MAX_SIMULATION_DAYS- Maximum date range (default: 30)AUTO_DOWNLOAD_PRICE_DATA- Auto-download missing data (default: true)ALPHAADVANTAGE_API_KEY- Alpha Vantage API key (required)
Server Configuration File
Server loads model definitions from configuration file (default: configs/default_config.json).
Example config:
{
"models": [
{
"name": "GPT-4",
"basemodel": "openai/gpt-4",
"signature": "gpt-4",
"enabled": true
},
{
"name": "Claude 3.7 Sonnet",
"basemodel": "anthropic/claude-3.7-sonnet",
"signature": "claude-3.7-sonnet",
"enabled": true
}
],
"agent_config": {
"max_steps": 30,
"initial_cash": 10000.0
}
}
Model fields:
signature- Unique identifier used in API requestsenabled- Whether model runs when no models specified in requestbasemodel- Model identifier for AI provideropenai_base_url- Optional custom API endpointopenai_api_key- Optional model-specific API key
Configuration Override System
Default config: /app/configs/default_config.json (baked into image)
Custom config: /app/user-configs/config.json (optional, via volume mount)
Merge behavior:
- Custom config sections completely replace default sections (root-level merge)
- If no custom config exists, defaults are used
- Validation occurs at container startup (before API starts)
- Invalid config causes immediate exit with detailed error message
Example custom config (overrides models only):
{
"models": [
{"name": "gpt-5", "basemodel": "openai/gpt-5", "signature": "gpt-5", "enabled": true}
]
}
All other sections (agent_config, log_config, etc.) inherited from default.
OpenAPI / Swagger Documentation
Interactive API documentation available at:
- Swagger UI:
http://localhost:8080/docs - ReDoc:
http://localhost:8080/redoc - OpenAPI JSON:
http://localhost:8080/openapi.json
Client Libraries
Python
import requests
import time
class AITraderServerClient:
def __init__(self, base_url="http://localhost:8080"):
self.base_url = base_url
def trigger_simulation(self, end_date, start_date=None, models=None, replace_existing=False):
"""
Trigger a simulation job.
Args:
end_date: End date (YYYY-MM-DD), required
start_date: Start date (YYYY-MM-DD) or None for resume mode
models: List of model signatures or None for all enabled models
replace_existing: If False, skip already-completed dates (idempotent)
"""
payload = {"end_date": end_date, "replace_existing": replace_existing}
if start_date is not None:
payload["start_date"] = start_date
if models:
payload["models"] = models
response = requests.post(
f"{self.base_url}/simulate/trigger",
json=payload
)
response.raise_for_status()
return response.json()
def get_status(self, job_id):
"""Get job status."""
response = requests.get(f"{self.base_url}/simulate/status/{job_id}")
response.raise_for_status()
return response.json()
def wait_for_completion(self, job_id, poll_interval=10):
"""Poll until job completes."""
while True:
status = self.get_status(job_id)
if status["status"] in ["completed", "partial", "failed"]:
return status
time.sleep(poll_interval)
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 model:
params["model"] = model
response = requests.get(f"{self.base_url}/results", params=params)
response.raise_for_status()
return response.json()
def get_reasoning(self, job_id=None, date=None, model=None, include_full_conversation=False):
"""Query reasoning logs with optional filters."""
params = {}
if job_id:
params["job_id"] = job_id
if date:
params["date"] = date
if model:
params["model"] = model
if include_full_conversation:
params["include_full_conversation"] = "true"
response = requests.get(f"{self.base_url}/reasoning", params=params)
response.raise_for_status()
return response.json()
# Usage examples
client = AITraderServerClient()
# Single day simulation
job = client.trigger_simulation(end_date="2025-01-16", start_date="2025-01-16", models=["gpt-4"])
# Date range simulation
job = client.trigger_simulation(end_date="2025-01-20", start_date="2025-01-16")
# Resume mode (continue from last completed)
job = client.trigger_simulation(end_date="2025-01-31", models=["gpt-4"])
# Wait for completion and get results
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"])
# Get reasoning logs with full conversation
full_reasoning = client.get_reasoning(
job_id=job["job_id"],
date="2025-01-16",
include_full_conversation=True
)
TypeScript/JavaScript
class AITraderServerClient {
constructor(private baseUrl: string = "http://localhost:8080") {}
async triggerSimulation(
endDate: string,
options: {
startDate?: string | null;
models?: string[];
replaceExisting?: boolean;
} = {}
) {
const body: any = {
end_date: endDate,
replace_existing: options.replaceExisting ?? false
};
if (options.startDate !== undefined) {
body.start_date = options.startDate;
}
if (options.models) {
body.models = options.models;
}
const response = await fetch(`${this.baseUrl}/simulate/trigger`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async getStatus(jobId: string) {
const response = await fetch(
`${this.baseUrl}/simulate/status/${jobId}`
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async waitForCompletion(jobId: string, pollInterval: number = 10000) {
while (true) {
const status = await this.getStatus(jobId);
if (["completed", "partial", "failed"].includes(status.status)) {
return status;
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
}
async getResults(filters: {
jobId?: string;
startDate?: string;
endDate?: string;
model?: string;
reasoning?: string;
} = {}) {
const params = new URLSearchParams();
if (filters.jobId) params.set("job_id", filters.jobId);
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()}`
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async getReasoning(filters: {
jobId?: string;
date?: string;
model?: string;
includeFullConversation?: boolean;
} = {}) {
const params = new URLSearchParams();
if (filters.jobId) params.set("job_id", filters.jobId);
if (filters.date) params.set("date", filters.date);
if (filters.model) params.set("model", filters.model);
if (filters.includeFullConversation) params.set("include_full_conversation", "true");
const response = await fetch(
`${this.baseUrl}/reasoning?${params.toString()}`
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
}
// Usage examples
const client = new AITraderServerClient();
// Single day simulation
const job1 = await client.triggerSimulation("2025-01-16", {
startDate: "2025-01-16",
models: ["gpt-4"]
});
// Date range simulation
const job2 = await client.triggerSimulation("2025-01-20", {
startDate: "2025-01-16"
});
// Resume mode (continue from last completed)
const job3 = await client.triggerSimulation("2025-01-31", {
startDate: null,
models: ["gpt-4"]
});
// Wait for completion and get results
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 });
// Get reasoning logs with full conversation
const fullReasoning = await client.getReasoning({
jobId: job1.job_id,
date: "2025-01-16",
includeFullConversation: true
});