curl --request POST \
--url https://api.chicago.global/v1/portfolio/analyze \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"portfolio": [
{
"date": "2024-01-01",
"symbol": "AAPL",
"weight": 0.25
}
],
"start_date": "2024-01-01",
"end_date": "2024-11-01",
"base_currency": "USD",
"benchmark": "ACWI.OQ",
"initial_value": 10000,
"include_transaction_costs": false,
"transaction_cost_config": {
"Singapore": {
"buy": 0.14,
"sell": 0.14
},
"United States": {
"buy": 0.1,
"sell": 0.1
}
},
"fields": [
"portfolio_summary",
"performance_metrics",
"latest_holdings"
]
}
'import requests
url = "https://api.chicago.global/v1/portfolio/analyze"
payload = {
"portfolio": [
{
"date": "2024-01-01",
"symbol": "AAPL",
"weight": 0.25
}
],
"start_date": "2024-01-01",
"end_date": "2024-11-01",
"base_currency": "USD",
"benchmark": "ACWI.OQ",
"initial_value": 10000,
"include_transaction_costs": False,
"transaction_cost_config": {
"Singapore": {
"buy": 0.14,
"sell": 0.14
},
"United States": {
"buy": 0.1,
"sell": 0.1
}
},
"fields": ["portfolio_summary", "performance_metrics", "latest_holdings"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
portfolio: [{date: '2024-01-01', symbol: 'AAPL', weight: 0.25}],
start_date: '2024-01-01',
end_date: '2024-11-01',
base_currency: 'USD',
benchmark: 'ACWI.OQ',
initial_value: 10000,
include_transaction_costs: false,
transaction_cost_config: {Singapore: {buy: 0.14, sell: 0.14}, 'United States': {buy: 0.1, sell: 0.1}},
fields: ['portfolio_summary', 'performance_metrics', 'latest_holdings']
})
};
fetch('https://api.chicago.global/v1/portfolio/analyze', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.chicago.global/v1/portfolio/analyze",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'portfolio' => [
[
'date' => '2024-01-01',
'symbol' => 'AAPL',
'weight' => 0.25
]
],
'start_date' => '2024-01-01',
'end_date' => '2024-11-01',
'base_currency' => 'USD',
'benchmark' => 'ACWI.OQ',
'initial_value' => 10000,
'include_transaction_costs' => false,
'transaction_cost_config' => [
'Singapore' => [
'buy' => 0.14,
'sell' => 0.14
],
'United States' => [
'buy' => 0.1,
'sell' => 0.1
]
],
'fields' => [
'portfolio_summary',
'performance_metrics',
'latest_holdings'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.chicago.global/v1/portfolio/analyze"
payload := strings.NewReader("{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.chicago.global/v1/portfolio/analyze")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.chicago.global/v1/portfolio/analyze")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Analyze Portfolio
curl --request POST \
--url https://api.chicago.global/v1/portfolio/analyze \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"portfolio": [
{
"date": "2024-01-01",
"symbol": "AAPL",
"weight": 0.25
}
],
"start_date": "2024-01-01",
"end_date": "2024-11-01",
"base_currency": "USD",
"benchmark": "ACWI.OQ",
"initial_value": 10000,
"include_transaction_costs": false,
"transaction_cost_config": {
"Singapore": {
"buy": 0.14,
"sell": 0.14
},
"United States": {
"buy": 0.1,
"sell": 0.1
}
},
"fields": [
"portfolio_summary",
"performance_metrics",
"latest_holdings"
]
}
'import requests
url = "https://api.chicago.global/v1/portfolio/analyze"
payload = {
"portfolio": [
{
"date": "2024-01-01",
"symbol": "AAPL",
"weight": 0.25
}
],
"start_date": "2024-01-01",
"end_date": "2024-11-01",
"base_currency": "USD",
"benchmark": "ACWI.OQ",
"initial_value": 10000,
"include_transaction_costs": False,
"transaction_cost_config": {
"Singapore": {
"buy": 0.14,
"sell": 0.14
},
"United States": {
"buy": 0.1,
"sell": 0.1
}
},
"fields": ["portfolio_summary", "performance_metrics", "latest_holdings"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
portfolio: [{date: '2024-01-01', symbol: 'AAPL', weight: 0.25}],
start_date: '2024-01-01',
end_date: '2024-11-01',
base_currency: 'USD',
benchmark: 'ACWI.OQ',
initial_value: 10000,
include_transaction_costs: false,
transaction_cost_config: {Singapore: {buy: 0.14, sell: 0.14}, 'United States': {buy: 0.1, sell: 0.1}},
fields: ['portfolio_summary', 'performance_metrics', 'latest_holdings']
})
};
fetch('https://api.chicago.global/v1/portfolio/analyze', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.chicago.global/v1/portfolio/analyze",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'portfolio' => [
[
'date' => '2024-01-01',
'symbol' => 'AAPL',
'weight' => 0.25
]
],
'start_date' => '2024-01-01',
'end_date' => '2024-11-01',
'base_currency' => 'USD',
'benchmark' => 'ACWI.OQ',
'initial_value' => 10000,
'include_transaction_costs' => false,
'transaction_cost_config' => [
'Singapore' => [
'buy' => 0.14,
'sell' => 0.14
],
'United States' => [
'buy' => 0.1,
'sell' => 0.1
]
],
'fields' => [
'portfolio_summary',
'performance_metrics',
'latest_holdings'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.chicago.global/v1/portfolio/analyze"
payload := strings.NewReader("{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.chicago.global/v1/portfolio/analyze")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.chicago.global/v1/portfolio/analyze")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"portfolio\": [\n {\n \"date\": \"2024-01-01\",\n \"symbol\": \"AAPL\",\n \"weight\": 0.25\n }\n ],\n \"start_date\": \"2024-01-01\",\n \"end_date\": \"2024-11-01\",\n \"base_currency\": \"USD\",\n \"benchmark\": \"ACWI.OQ\",\n \"initial_value\": 10000,\n \"include_transaction_costs\": false,\n \"transaction_cost_config\": {\n \"Singapore\": {\n \"buy\": 0.14,\n \"sell\": 0.14\n },\n \"United States\": {\n \"buy\": 0.1,\n \"sell\": 0.1\n }\n },\n \"fields\": [\n \"portfolio_summary\",\n \"performance_metrics\",\n \"latest_holdings\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}How It Works
This endpoint uses async job processing for handling large portfolios:- Submit Job: POST your portfolio data to
/v1/portfolio/analyze - Receive Job ID: Get a job ID and polling URL immediately
- Poll for Status: Check
/v1/jobs/{job_id}until status iscompleted - Get Results: The completed job response contains the full analysis
analyze is a one-shot, stateless computation.Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
portfolio | array | Yes | - | List of holdings (see format below) |
start_date | string | No | First rebalance date | Analysis start date (YYYY-MM-DD) |
end_date | string | No | Yesterday | Analysis end date (YYYY-MM-DD) |
base_currency | string | No | USD | Base currency (USD, SGD, EUR, etc.) |
benchmark | string | No | ACWI.OQ | Benchmark for comparison (RIC, e.g. ACWI.OQ, SPY) |
initial_value | number | No | 10000 | Starting portfolio value |
include_transaction_costs | boolean | No | false | Apply market-specific transaction costs |
transaction_cost_config | object | No | Default rates | Custom transaction cost rates by market |
fields | array | No | All fields | Select specific response fields |
ACWI.OQ (MSCI ACWI ETF). If a benchmark symbol can’t be
resolved to a RIC the request fails with a clear validation error rather than silently
falling back.Portfolio Holdings Format
Each item in theportfolio array:
| Field | Type | Required | Description |
|---|---|---|---|
date | string | Yes | Rebalance date (YYYY-MM-DD) |
symbol | string | Yes | Stock symbol (e.g. AAPL.O for Apple Inc) |
weight | number | Yes | Portfolio weight as decimal (0.25 = 25%) |
Field Selection
Use the optionalfields parameter to return only specific data sections. If omitted, all fields are returned.
{
"portfolio": [...],
"fields": ["portfolio_summary", "performance_metrics", "latest_holdings"]
}
benchmark_prices is internal-only and is no longer a selectable fields value or
part of the default response. For benchmark charting, use
daily_summary.benchmark_value_base instead.Response Fields
The completed analysis includes:| Field | Description |
|---|---|
portfolio_summary | Final value, total return, P&L breakdown, transaction costs |
turnover_analysis | Turnover metrics, rebalance frequency, costs by market |
performance_metrics | Sharpe, Sortino, VaR, volatility, drawdown |
rolling_metrics | 30/60/90-day rolling Sharpe, beta, correlation |
drawdown_analysis | Drawdown episodes and recovery times |
portfolio_scores | Quant factor scores (value, quality, momentum) |
latest_holdings | Current positions with company info |
transactions | All buy/sell trades |
market_allocation | Market weights over time |
sector_allocation | Sector weights over time |
currency_allocation | Currency weights over time |
company_contribution | P&L attribution by company |
sector_contribution | P&L attribution by sector |
market_contribution | P&L attribution by market |
time_period_returns | MTD, YTD, 1Y, inception returns |
daily_summary | Equity curve with benchmark comparison (benchmark_value_base) and cumulative return (cumulative_return) |
analytics_version | Version of the analytics calculation that produced this result. Always included, whatever fields selects |
contribution_pct is each row’s contribution
to the portfolio return, as a decimal: its P&L divided by initial_value
(0.0353 = 3.53 percentage points). The rows sum to the portfolio’s return, and a
row’s sign always matches its own P&L.
analytics_version increases whenever the calculation’s output changes. If you
store results, compare it with a fresh result to know when yours are out of date.
Example Response
When the job completes,GET /v1/jobs/{job_id} returns:
{
"job_id": "port-9730dcc9-7a06-495c-85a9-4a405d2140e4",
"job_type": "portfolio_analysis",
"status": "completed",
"result": {
"success": true,
"result": {
"portfolio_parameters": {
"start_date": "2025-07-01",
"end_date": "2025-07-31",
"base_currency": "USD",
"benchmark": "ACWI.OQ",
"initial_value": 10000.0,
"include_transaction_costs": false
},
"portfolio_summary": {
"final_value": 9890.37,
"total_return": -0.0110,
"total_price_pl": 122.79,
"total_fx_pl": -232.32,
"total_pl": -109.63,
"total_transaction_cost": 0.0,
"include_transaction_costs": false
},
"performance_metrics": {
"portfolio": {
"total_return": -0.0110,
"annualized_volatility": 0.1335,
"max_drawdown": -0.0383,
"sharpe_ratio": -0.71,
"sortino_ratio": -1.67,
"var_95": -0.0093
},
"relative": {
"beta": 1.52,
"alpha_annualized": -0.32,
"correlation": 0.67,
"information_ratio": -1.67,
"tracking_error": 0.103
}
},
"portfolio_scores": {
"value": 42,
"quality": 65,
"momentum": 85,
"defensive": 90,
"tactical": 37,
"total": 71
},
"latest_holdings": [
{ "ric": "2330.TW", "weight": 0.295, "market": "Taiwan", "sector": "Technology", "recommendation": "STRONG BUY" },
{ "ric": "AAPL.O", "weight": 0.205, "market": "United States", "sector": "Technology", "recommendation": "BUY" }
],
"daily_summary": [
{ "date": "2025-07-01", "portfolio_value": 10000.0, "benchmark_value_base": 10000.0, "cumulative_return": 0.0 },
{ "date": "2025-07-31", "portfolio_value": 9890.37, "benchmark_value_base": 10111.0, "cumulative_return": -0.0110 }
]
}
}
}
Example: Polling for Results
const jobId = response.job_id;
const checkUrl = `https://api.chicago.global/v1/jobs/${jobId}`;
// Poll every 3 seconds
const interval = setInterval(async () => {
const status = await fetch(checkUrl, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
}).then(r => r.json());
if (status.status === 'completed') {
clearInterval(interval);
console.log('Analysis:', status.result);
} else if (status.status === 'failed') {
clearInterval(interval);
console.error('Failed:', status.error);
} else {
console.log(`Progress: ${status.progress?.percent}%`);
}
}, 3000);
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
List of portfolio holdings with dates and weights
Show child attributes
Show child attributes
Analysis start date in YYYY-MM-DD format (defaults to first rebalance date)
"2024-01-01"
Analysis end date in YYYY-MM-DD format (defaults to yesterday)
"2024-11-01"
Base currency for portfolio
"USD"
Benchmark for comparison
"ACWI.OQ"
Initial portfolio value in base currency
10000
Apply market-specific transaction costs during rebalancing
false
Custom transaction cost rates by market. Format: {'market_name': {'buy': rate, 'sell': rate}}. If not provided, uses default rates.
{
"Singapore": { "buy": 0.14, "sell": 0.14 },
"United States": { "buy": 0.1, "sell": 0.1 }
}
Optional list of fields to include in response. If not provided, all fields are returned. Available fields: portfolio_parameters, portfolio_input, data_quality, portfolio_summary, turnover_analysis, performance_metrics, rolling_metrics, drawdown_analysis, portfolio_scores, concentration_metrics, transactions, company_info, latest_holdings, market_allocation, sector_allocation, currency_allocation, company_contribution, sector_contribution, market_contribution, time_period_returns, monthly_returns, annual_returns, daily_summary
[
"portfolio_summary",
"performance_metrics",
"latest_holdings"
]
Response
Successful Response