Results
Retrieve optimization results, performance metrics, and analytics
/jobs/{run_id}/resultRetrieve the full optimization result for a completed job. Only available when job status is "succeeded". You must own the run.
Authentication
Required. Bearer token (JWT) or API key. You must be the owner of the run.
Path Parameters
run_idstringThe unique identifier returned when the optimization job was createdResponse Structure
Top-level Fields
run_idstringUnique identifier for this optimization runstatusstringAlways "succeeded" for results endpointtimingobjectContains startedAt, finishedAt (ISO 8601), and durationSecondsMethods Array
Each element in the methodsarray represents one optimization method's results:
methodstringOptimization method identifier, e.g. "MVO", "HRP"weightsobjectTicker to weight mapping, e.g. {"INFY": 0.35, "RELIANCE": 0.65}metricsobjectAll performance metrics (see Performance Metrics Reference below)yearly_returnsobjectYear to return mapping for the benchmark, e.g. {"2021": 0.24}portfolio_yearly_returnsobjectYear to return mapping for this portfolio methodrolling_betasobjectRolling period to beta value, e.g. {"60": 0.92, "120": 0.87}compute_started_atstringISO 8601 timestamp when this method began computingcompute_finished_atstringISO 8601 timestamp when this method finishedcompute_duration_secondsnumberWall-clock time to compute this method in secondsmethod_labelstring?Display label (set when method is degraded, e.g. "MVO (RF-clipped)")base_methodstring?Original method name before degradationis_degradedboolean?True if risk-free rate was clipped to fit feasible regiondegradationobject|string?Structured metadata or string describing the RF-clipping (type, reason, rates, epsilon)optimization_risk_free_ratenumber?Clipped risk-free rate used in the optimizer (differs from global RF)Response Object
The top-level response object contains aggregated data and signed URLs:
resultsobjectPer-method data: weights, performance metrics, compute timing, risk_attribution, rolling_backtest, sharpe_inference, and degradation metadatacumulative_returnsarrayArray of cumulative return values for the portfolio over timedatesarrayArray of date strings corresponding to cumulative_returnsbenchmark_returnsarrayArray of benchmark cumulative return valuesstock_yearly_returnsobjectPer-stock yearly returns keyed by tickerchart_bundlestring|objectSigned URL (historical) or inline object (live) with pre-computed chart data: cumulative returns, rolling correlation, covariance, spectral decomposition, risk attribution, stationarity testsrisk_free_ratenumberThe risk-free rate used for performance metric computationsstart_datestringStart date of the backtest periodend_datestringEnd date of the backtest periodmetaobjectContains run_idExample Response
{
"run_id": "opt_abc123def456",
"status": "succeeded",
"timing": {
"startedAt": "2025-01-15T10:30:00.000Z",
"finishedAt": "2025-01-15T10:30:47.000Z",
"durationSeconds": 47.2
},
"methods": [
{
"method": "MVO",
"weights": {
"INFY": 0.2541,
"RELIANCE": 0.3102,
"TCS": 0.1893,
"HDFCBANK": 0.2464
},
"metrics": {
"expected_return": 0.1847,
"cagr": 0.1623,
"volatility": 0.2134,
"max_drawdown": -0.2891,
"sharpe": 0.8742,
"sortino": 1.2156,
"treynor_ratio": 0.1203,
"omega_ratio": 1.4521,
"calmar_ratio": 0.5614,
"sterling_ratio": 0.4892,
"v2_ratio": 1.0345,
"information_ratio": 0.3241,
"upside_potential_ratio": 1.8923,
"modigliani_risk_adjusted_performance": 0.1456,
"var_95": -0.0312,
"cvar_95": -0.0487,
"var_90": -0.0234,
"cvar_90": -0.0356,
"evar_95": -0.0423,
"dar_95": -0.1845,
"cdar_95": -0.2234,
"portfolio_beta": 0.8734,
"portfolio_alpha": 0.0523,
"beta_pvalue": 0.0001,
"r_squared": 0.7845,
"blume_adjusted_beta": 0.9156,
"welch_beta": 0.8612,
"semi_beta": 0.9423,
"vasicek_beta": 0.8891,
"james_stein_beta": 0.8945,
"skewness": -0.3421,
"kurtosis": 4.2156,
"entropy": 3.1245,
"gini_mean_difference": 0.0187,
"ulcer_index": 0.0923,
"tracking_error": 0.0845,
"upside_capture": 0.9234,
"downside_capture": 0.7845,
"effective_n": 3.42,
"romad": 0.5614
},
"yearly_returns": {
"2020": 0.1523,
"2021": 0.2847,
"2022": -0.0892,
"2023": 0.1945,
"2024": 0.1234
},
"portfolio_yearly_returns": {
"2020": 0.1789,
"2021": 0.3102,
"2022": -0.0645,
"2023": 0.2156,
"2024": 0.1456
},
"rolling_betas": {
"60": 0.9234,
"120": 0.8745,
"252": 0.8512
},
"compute_started_at": "2025-01-15T10:30:02.000Z",
"compute_finished_at": "2025-01-15T10:30:15.000Z",
"compute_duration_seconds": 13.4
}
// ... additional methods
],
"response": {
"results": {
"MVO": {
"returns_dist": "https://storage.googleapis.com/...signed-url...",
"max_drawdown_plot": "https://storage.googleapis.com/...signed-url..."
}
},
"cumulative_returns": [1.0, 1.002, 1.008, 1.003, ...],
"dates": ["2020-01-02", "2020-01-03", "2020-01-06", ...],
"benchmark_returns": [1.0, 1.001, 1.005, 0.998, ...],
"stock_yearly_returns": {
"INFY": { "2020": 0.85, "2021": 0.32, "2022": -0.12, ... },
"RELIANCE": { "2020": 0.30, "2021": 0.18, "2022": 0.05, ... }
},
"covariance_heatmap": "https://storage.googleapis.com/...signed-url...",
"risk_free_rate": 0.065,
"start_date": "2020-01-01",
"end_date": "2024-12-31",
"meta": {
"run_id": "opt_abc123def456",
"timing": {
"startedAt": "2025-01-15T10:30:00.000Z",
"finishedAt": "2025-01-15T10:30:47.000Z",
"durationSeconds": 47.2
}
}
}
}curl
curl -X GET https://api.portfolioopt.in/jobs/opt_abc123def456/result \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.get( "https://api.portfolioopt.in/jobs/opt_abc123def456/result", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) result = response.json() # Access MVO weights mvo = next(m for m in result["methods"] if m["method"] == "MVO") print("MVO Weights:", mvo["weights"]) print("Sharpe Ratio:", mvo["metrics"]["sharpe"]) print("Max Drawdown:", mvo["metrics"]["max_drawdown"]) # Access cumulative returns for charting dates = result["response"]["dates"] cum_returns = result["response"]["cumulative_returns"]
Performance Metrics Reference
Comprehensive reference of all metrics returned in the metrics object for each optimization method. Metrics are grouped by category.
Returns
expected_returnExpected ReturnAnnualized expected portfolio return based on historical mean0.05 - 0.30cagrCAGRCompound Annual Growth Rate over the backtest period0.02 - 0.25Risk
volatilityVolatilityAnnualized standard deviation of portfolio returns0.10 - 0.40max_drawdownMaximum DrawdownLargest peak-to-trough decline during the backtest period-0.60 - 0.00var_95VaR (95%)Value-at-Risk at 95% confidence level (daily)-0.05 - -0.01cvar_95CVaR (95%)Conditional Value-at-Risk (Expected Shortfall) at 95%-0.08 - -0.01var_90VaR (90%)Value-at-Risk at 90% confidence level (daily)-0.04 - -0.005cvar_90CVaR (90%)Conditional Value-at-Risk at 90% confidence level-0.06 - -0.008evar_95EVaR (95%)Entropic Value-at-Risk at 95% confidence level-0.07 - -0.01dar_95DaR (95%)Drawdown-at-Risk at 95% confidence level-0.40 - -0.05cdar_95CDaR (95%)Conditional Drawdown-at-Risk at 95% confidence level-0.50 - -0.08Ratios
sharpeSharpe RatioRisk-adjusted return relative to the risk-free rate-1.0 - 3.0sortinoSortino RatioDownside risk-adjusted return using semi-deviation-1.0 - 5.0treynor_ratioTreynor RatioExcess return per unit of systematic (beta) risk-0.10 - 0.30omega_ratioOmega RatioProbability-weighted ratio of gains to losses0.5 - 3.0calmar_ratioCalmar RatioAnnualized return divided by maximum drawdown-1.0 - 5.0sterling_ratioSterling RatioAnnualized return divided by average drawdown-1.0 - 4.0v2_ratioV2 RatioRisk-adjusted performance using second-order lower partial moments-2.0 - 5.0information_ratioInformation RatioActive return divided by tracking error vs. benchmark-2.0 - 2.0upside_potential_ratioUpside Potential RatioRatio of upside potential to downside risk0.5 - 4.0modigliani_risk_adjusted_performanceM² (Modigliani)Risk-adjusted performance normalized to benchmark volatility0.0 - 0.30Beta & Regression
portfolio_betaPortfolio BetaSensitivity of portfolio returns to the benchmark0.3 - 1.5portfolio_alphaPortfolio AlphaExcess return not explained by benchmark exposure (annualized)-0.10 - 0.15beta_pvalueBeta p-valueStatistical significance of the beta estimate0.0 - 1.0r_squaredR-squaredProportion of portfolio variance explained by the benchmark0.0 - 1.0blume_adjusted_betaBlume Adjusted BetaBeta adjusted toward 1.0 using Blume's mean-reversion formula0.5 - 1.3welch_betaWelch BetaBeta estimated using Welch's robust regression method0.3 - 1.5semi_betaSemi BetaBeta calculated using only downside return observations0.3 - 2.0vasicek_betaVasicek BetaBayesian-shrinkage beta using Vasicek's adjustment0.4 - 1.4james_stein_betaJames-Stein BetaShrinkage estimator that reduces extreme beta estimates0.5 - 1.3Distribution
skewnessSkewnessAsymmetry of the return distribution (negative = left tail)-2.0 - 1.0kurtosisKurtosisTail heaviness of the return distribution (excess kurtosis)0.0 - 10.0entropyEntropyInformation-theoretic measure of return distribution uncertainty0.0 - 5.0Other
gini_mean_differenceGini Mean DifferenceAverage absolute difference between all pairs of returns0.005 - 0.05ulcer_indexUlcer IndexRoot mean squared drawdown measuring sustained losses0.01 - 0.30tracking_errorTracking ErrorStandard deviation of the portfolio's excess returns vs. benchmark0.02 - 0.25upside_captureUpside CapturePercentage of benchmark gains captured in up markets0.5 - 1.5downside_captureDownside CapturePercentage of benchmark losses captured in down markets0.3 - 1.3effective_nEffective NEffective number of independent bets in the portfolio1.0 - NromadRoMaDReturn over Maximum Drawdown-1.0 - 5.0Analytics Endpoints
Dashboard summaries, run comparisons, and cached statistics for the analytics layer.
/analytics/dashboardRetrieve a comprehensive dashboard summary including method performance, benchmark stats, daily run counts, recent runs, saved comparisons, and auto-generated insights.
Authentication
Required. JWT only (not available via API key). Returns data scoped to the authenticated user.
Example Response
{
"summary": {
"totalRuns": 42,
"succeededRuns": 38,
"failedRuns": 3,
"pendingRuns": 1,
"avgDurationSeconds": 34.7,
"totalStocksAnalyzed": 156
},
"methodPerformance": {
"MVO": { "avgSharpe": 0.92, "avgReturn": 0.18, "runCount": 15 },
"HRP": { "avgSharpe": 0.85, "avgReturn": 0.16, "runCount": 12 },
...
},
"benchmarkStats": {
"cagr": 0.1245,
"volatility": 0.1834,
"sharpe": 0.6723
},
"dailyRunCounts": [
{ "date": "2025-01-10", "count": 3 },
{ "date": "2025-01-11", "count": 5 },
...
],
"recentRuns": [
{
"runId": "opt_abc123",
"status": "succeeded",
"createdAt": "2025-01-15T10:30:00Z",
"stocks": ["INFY", "RELIANCE", "TCS"],
"methods": ["MVO", "HRP"]
},
...
],
"savedComparisons": [
{
"comparisonId": "cmp_xyz789",
"name": "Q4 Strategy Comparison",
"runCount": 3,
"createdAt": "2025-01-12T08:00:00Z"
}
],
"insights": [
"HRP has outperformed MVO by 2.3% CAGR over the last 10 runs",
"Your average Sharpe ratio improved 12% this month"
]
}curl
curl -X GET https://api.portfolioopt.in/analytics/dashboard \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.get( "https://api.portfolioopt.in/analytics/dashboard", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) dashboard = response.json() print("Total Runs:", dashboard["summary"]["totalRuns"]) print("Insights:", dashboard["insights"])
/analytics/comparisonsCreate a saved comparison to group multiple optimization runs for side-by-side analysis.
Authentication
Required. Bearer token (JWT) or API key.
Request Body
{
"name": "Q4 2024 Strategy Comparison",
"description": "Comparing MVO vs HRP across different stock sets",
"runIds": ["opt_abc123", "opt_def456", "opt_ghi789"]
}Response
{
"comparisonId": "cmp_xyz789",
"name": "Q4 2024 Strategy Comparison",
"description": "Comparing MVO vs HRP across different stock sets",
"runIds": ["opt_abc123", "opt_def456", "opt_ghi789"],
"isPinned": false,
"createdAt": "2025-01-15T12:00:00Z",
"updatedAt": "2025-01-15T12:00:00Z"
}curl
curl -X POST https://api.portfolioopt.in/analytics/comparisons \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Q4 2024 Strategy Comparison", "description": "Comparing MVO vs HRP across different stock sets", "runIds": ["opt_abc123", "opt_def456", "opt_ghi789"] }'
Python
import requests response = requests.post( "https://api.portfolioopt.in/analytics/comparisons", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}, json={ "name": "Q4 2024 Strategy Comparison", "description": "Comparing MVO vs HRP across different stock sets", "runIds": ["opt_abc123", "opt_def456", "opt_ghi789"] } ) comparison = response.json() print("Comparison ID:", comparison["comparisonId"])
/analytics/comparisons/{comparison_id}Retrieve the details of a saved comparison including all associated run data.
Authentication
Required. Bearer token (JWT) or API key.
Response
{
"comparisonId": "cmp_xyz789",
"name": "Q4 2024 Strategy Comparison",
"description": "Comparing MVO vs HRP across different stock sets",
"isPinned": false,
"createdAt": "2025-01-15T12:00:00Z",
"updatedAt": "2025-01-15T12:00:00Z",
"runs": [
{
"runId": "opt_abc123",
"status": "succeeded",
"stocks": ["INFY", "RELIANCE", "TCS"],
"methods": ["MVO", "HRP"],
"createdAt": "2025-01-14T09:00:00Z",
"metrics": { ... }
},
{
"runId": "opt_def456",
"status": "succeeded",
"stocks": ["HDFCBANK", "ICICIBANK", "SBIN"],
"methods": ["MVO", "MinCVaR"],
"createdAt": "2025-01-14T11:30:00Z",
"metrics": { ... }
}
]
}curl
curl -X GET https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789 \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.get( "https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) comparison = response.json() for run in comparison["runs"]: print(f"Run {run['runId']}: {run['status']}, stocks={run['stocks']}")
/analytics/comparisons/{comparison_id}Update a saved comparison. All fields are optional -- only include the fields you want to change.
Authentication
Required. Bearer token (JWT) or API key.
Request Body
namestring?New name for the comparisondescriptionstring?New descriptionisPinnedboolean?Pin or unpin the comparison on the dashboardaddRunIdsstring[]?Run IDs to add to this comparisonremoveRunIdsstring[]?Run IDs to remove from this comparison{
"name": "Updated Comparison Name",
"isPinned": true,
"addRunIds": ["opt_newrun001"],
"removeRunIds": ["opt_ghi789"]
}curl
curl -X PATCH https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789 \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Comparison Name", "isPinned": true, "addRunIds": ["opt_newrun001"] }'
Python
import requests response = requests.patch( "https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}, json={ "name": "Updated Comparison Name", "isPinned": True, "addRunIds": ["opt_newrun001"] } ) print("Updated:", response.json())
/analytics/comparisons/{comparison_id}Permanently delete a saved comparison. This does not delete the underlying runs.
Authentication
Required. Bearer token (JWT) or API key.
Response
{
"message": "Comparison deleted successfully"
}curl
curl -X DELETE https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789 \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.delete( "https://api.portfolioopt.in/analytics/comparisons/cmp_xyz789", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) print(response.json()["message"])
/analytics/refresh-statsForce a refresh of cached analytics statistics. Use this after completing new runs if dashboard data appears stale.
Authentication
Required. Bearer token (JWT) or API key.
Response
{
"message": "Statistics refreshed successfully",
"refreshedAt": "2025-01-15T12:30:00Z"
}curl
curl -X POST https://api.portfolioopt.in/analytics/refresh-stats \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.post( "https://api.portfolioopt.in/analytics/refresh-stats", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) print("Refreshed at:", response.json()["refreshedAt"])
Tags
Organize and label your optimization runs with custom tags for filtering and grouping.
/runs/{run_id}/tagsAdd a tag to an optimization run. Tags have a name and optional color for visual identification.
Authentication
Required. Bearer token (JWT) or API key.
Request Body
{
"tagName": "production",
"tagColor": "#22c55e"
}Response
{
"message": "Tag added successfully",
"tag": {
"tagName": "production",
"tagColor": "#22c55e",
"addedAt": "2025-01-15T14:00:00Z"
}
}curl
curl -X POST https://api.portfolioopt.in/runs/opt_abc123def456/tags \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tagName": "production", "tagColor": "#22c55e"}'
Python
import requests response = requests.post( "https://api.portfolioopt.in/runs/opt_abc123def456/tags", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"}, json={"tagName": "production", "tagColor": "#22c55e"} ) print(response.json()["tag"])
/runs/{run_id}/tags/{tag_name}Remove a tag from an optimization run by tag name.
Authentication
Required. Bearer token (JWT) or API key.
Response
{
"message": "Tag removed successfully"
}curl
curl -X DELETE https://api.portfolioopt.in/runs/opt_abc123def456/tags/production \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.delete( "https://api.portfolioopt.in/runs/opt_abc123def456/tags/production", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) print(response.json()["message"])
/analytics/tagsList all unique tags across your optimization runs with usage counts.
Authentication
Required. Bearer token (JWT) or API key.
Response
{
"tags": [
{ "tagName": "production", "tagColor": "#22c55e", "runCount": 8 },
{ "tagName": "experimental", "tagColor": "#f59e0b", "runCount": 5 },
{ "tagName": "backtest-q4", "tagColor": "#3b82f6", "runCount": 3 }
]
}curl
curl -X GET https://api.portfolioopt.in/analytics/tags \ -H "Authorization: Bearer YOUR_JWT_TOKEN"
Python
import requests response = requests.get( "https://api.portfolioopt.in/analytics/tags", headers={"Authorization": "Bearer YOUR_JWT_TOKEN"} ) for tag in response.json()["tags"]: print(f"{tag['tagName']} ({tag['runCount']} runs)")