--- title: "Compute portfolio risk metrics (Sharpe, beta, correlation) via a free API — in JS and Python" published: false description: "Stop re-deriving Sharpe, Sortino, beta, alpha, drawdown, correlation and rebalancing. Send your price series to one endpoint and get the numbers back — with copy-paste JavaScript and Python." tags: javascript, python, api, tutorial If you've ever built anything that touches a portfolio — a robo-advisor, a crypto tracker, a backtester, a personal-finance dashboard — you've hit the same wall: the analytics math is fiddly and easy to get subtly wrong. Sharpe looks trivial until you realize you annualized volatility with the wrong factor. Beta needs a benchmark and a covariance that lines up on dates. Max drawdown has an off-by-one that silently reports the wrong trough. Correlation matrices are fine until you forget to convert prices to returns first. I kept rewriting this code, so I turned it into an API. This post shows how to use it in JavaScript and Python , with runnable snippets. Informational only — not investment advice. It's a compute service, not a recommendation engine. What it does (and doesn't) It's a compute-as-a-service API. You bring your own price data — the API never fetches or redistributes market data. You POST a price series, it returns the metrics. No data-license constraints, no PII. Four endpoints: Endpoint Returns POST /v1/metrics total & annualized return, volatility, Sharpe, Sortino, Calmar , max drawdown, beta, alpha , information ratio POST /v1/correlation Pearson correlation matrix (2–50 instruments) + insights POST /v1/diversification asset-class / geography / sector spread + HHI concentration score POST /v1/rebalancing buy/sell/hold deltas vs a target allocation The metrics in one line each Sharpe — return per unit of total risk. Sortino — Sharpe, but only penalizing downside volatility. Calmar — annualized return divided by max drawdown. Beta — how much you move relative to a benchmark. Alpha — return beyond what beta explains. Max drawdown — worst peak-to-trough drop. Information ratio — active return per unit of tracking error. Step 1 — Get a free key Grab a key on RapidAPI (free tier: 1,000 requests/month, no card): 👉 https://rapidapi.com/gabriele-rEc4i6FCa/api/portfolio-analytics bash export RAPIDAPI_KEY="your-key" Step 2 — Call it from JavaScript (zero dependencies) Node 18+ has global fetch , so no packages needed: js const HOST = 'portfolio-analytics.p.rapidapi.com'; async function metrics(history, benchmark) { const res = await fetch( https://HOST/v1/metrics,method:POST,headers:ContentType:application/json,XRapidAPIHost:HOST,XRapidAPIKey:process.env.RAPIDAPIKEY,,body:JSON.stringify(history,benchmark),);if(!res.ok)thrownewError(API{HOST}/v1/metrics , { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-RapidAPI-Host': HOST, 'X-RapidAPI-Key': process.env.RAPIDAPI_KEY, }, body: JSON.stringify({ history, benchmark }), }); if (!res.ok) throw new Error( API {res.status} ); return res.json(); } const price = (arr) => arr.map((close, i) => ({ date: 2024-01-{String(i + 1).padStart(2, '0')} , close })); const { metrics: m } = await metrics( price([100, 101, 100.5, 102.8, 103.4, 102.1, 104.9]), price([100, 100.3, 100.1, 100.9, 101.2, 101.0, 101.6]), ); console.log( Sharpe {m.sharpeRatio} · Beta m.betaMaxDD{m.beta} · Max DD {m.maxDrawdown}% ); Step 3 — Same thing in Python (standard library only) No requests needed — urllib is enough:python import json, os, urllib.request HOST = "portfolio-analytics.p.rapidapi.com" def metrics(history, benchmark): body = json.dumps({"history": history, "benchmark": benchmark}).encode() req = urllib.request.Request( f"https://{HOST}/v1/metrics", data=body, headers={ "Content-Type": "application/json", "X-RapidAPI-Host": HOST, "X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"], }, method="POST", ) with urllib.request.urlopen(req) as r: return json.load(r) def price(arr): return [{"date": f"2024-01-{i+1:02d}", "close": c} for i, c in enumerate(arr)] res = metrics( price([100, 101, 100.5, 102.8, 103.4, 102.1, 104.9]), price([100, 100.3, 100.1, 100.9, 101.2, 101.0, 101.6]), ) m = res["metrics"] print(f"Sharpe {m['sharpeRatio']} · Beta {m['beta']} · Max DD {m['maxDrawdown']}%") Step 4 — Beyond single-portfolio metrics Correlation — pass 2–50 instruments, get an NxN Pearson matrix + insights: json POST /v1/correlation { "instruments": [ { "name": "S&P 500", "ticker": "SPY", "history": [ ... ] }, { "name": "Gold", "ticker": "GLD", "history": [ ... ] } ] } Rebalancing — send current positions + target weights, get buy/sell/hold deltas: json POST /v1/rebalancing { "positions": [{ "name": "VWCE", "category": "etf", "value": 7000 }, { "name": "AAPL", "category": "stock", "value": 3000 }], "targets": [{ "category": "etf", "targetPercent": 50 }, { "category": "stock", "targetPercent": 30 }, { "category": "bond", "targetPercent": 20 }] } Why an API instead of a library? Fair question. Three reasons it earned its place in my stack: Correctness, once. The annualization, downside-deviation and drawdown edge cases are solved and tested in one place, not re-derived per project or per language. Language-agnostic. JS, Python, Go, whatever — it's just HTTP. No data liability. You keep your prices; the service only does math. Nothing to license, nothing sensitive leaving your control beyond anonymous number arrays. If you'd rather vendor the math, that's valid too — but for quick projects, one HTTP call beats porting formulas. Try it 🌐 Docs & live landing: https://swalance-portfolio-analytics.fly.dev/ 🧰 Ready-made clients (JS / Python / curl): https://github.com/Gab-Swalance/portfolio-analytics-api-examples 🔑 Free key: https://rapidapi.com/gabriele-rEc4i6FCa/api/portfolio-analytics If you build something with it, I'd love to hear what — drop a comment. Disclaimer: informational purposes only, not investment advice. No warranty of accuracy.