ERM3 Risk Metrics API
Overview
The ERM3 Risk Metrics API provides programmatic access to equity risk model data computed by our ERM3 regression system. This site consumes and visualizes these published outputs in real-time dashboards and portfolio analytics.
Key Features:
- Daily Updated: Fresh factor metrics, hedge ratios, and decompositions
- OpenAPI Ready: Import into Postman, Swagger, or AI tools (ChatGPT, Claude)
- Developer Friendly: TypeScript and Python examples with validation helpers
- Secure: JWT + Row Level Security (RLS) for user auth, service role for backend agents
Quick Start
First successful call in minutes
TypeScript (Browser/Node.js)
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://your-project.supabase.co',
'your-service-role-key' // Server-side only
);
// Get latest metrics for AAPL
const { data, error } = await supabase
.from('ticker_factor_metrics')
.select('ticker, l3_residual_er, l3_market_hr, volatility')
.eq('ticker', 'AAPL');
if (data) {
const rr = data[0].l3_residual_er;
console.log(`Residual Risk: ${(rr * 100).toFixed(1)}%`);
}
Python (Jupyter/Backend)
from supabase import create_client
import os
supabase = create_client(
os.getenv('SUPABASE_URL'),
os.getenv('SUPABASE_SERVICE_ROLE_KEY')
)
# Query latest metrics
response = supabase.table('ticker_factor_metrics') \
.select('ticker, l3_residual_er, l3_market_hr, volatility') \
.eq('ticker', 'AAPL') \
.execute()
metrics = response.data[0]
print(f"Residual Risk: {metrics['l3_residual_er']:.1%}")
print(f"Market Hedge Ratio: {metrics['l3_market_hr']:.2f}")
cURL (REST API)
curl -X GET "https://your-project.supabase.co/rest/v1/ticker_factor_metrics?ticker=eq.AAPL" \
-H "apikey: YOUR_SERVICE_ROLE_KEY" \
-H "Content-Type: application/json"
Core API Endpoints
| Endpoint | Frequency | Use Case |
|----------|-----------|----------|
| /erm3_ticker_returns | Daily | Time series analysis of stock returns with factor decompositions |
| /erm3_l3_decomposition | Monthly (1st trading day) | Historical risk attribution and hedge ratio evolution |
| /ticker_factor_metrics | Daily (latest only) | Real-time dashboards, stock screening, portfolio construction |
Example Response
{
"metadata": {
"as_of": "2024-01-15",
"last_updated": "2024-01-15T10:30:00Z",
"frequency": "daily",
"source_table": "ticker_factor_metrics",
"units": {
"hr": "dollar_ratio",
"er": "decimal_fraction",
"volatility": "annualized_decimal"
}
},
"data": [
{
"ticker": "AAPL",
"l3_residual_er": 0.54,
"l3_market_hr": 0.98,
"volatility": 0.25,
"sharpe_ratio": 1.2
}
]
}
Key Concepts
RR (Residual Risk)
Definition: Unexplained variance after hedging all factors (market, sector, subsector).
Formula: RR = 1 - (l3_market_er + l3_sector_er + l3_subsector_er)
Use Cases:
- Screen for high RR (>0.5) to identify alpha opportunities
- Risk budgeting: allocate capital to stocks with sufficient residual risk capacity
- Portfolio construction: balance factor exposure vs. idiosyncratic risk
HR (Hedge Ratio)
Definition: The dollar amount of factor ETF to short per $1 of stock position to neutralize factor exposure.
Example: l3_market_hr = 0.98 means short $0.98 of SPY for every $1.00 long in the stock.
Use Cases:
- Construct market-neutral portfolios
- Calculate hedge quantities for factor-neutral strategies
- Risk decomposition analysis
ER (Explained Risk)
Definition: Percentage of stock variance explained by factor regressions (R-squared).
Hierarchical Levels:
- L1: Market-only (SPY beta)
- L2: Market + Sector (GICS sector ETF)
- L3: Market + Sector + Subsector (GICS subsector ETF)
Authentication Options
User-Facing Clients (Browser/Mobile)
Use JWT + Row Level Security (RLS) for user-facing applications:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key' // Public key (safe to expose)
);
// User login
await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'secure-password'
});
// Token automatically included in queries
const { data } = await supabase
.from('ticker_factor_metrics')
.select('*')
.eq('ticker', 'AAPL');
Backend Agents (Server-Side)
Use service_role key (full access, bypasses RLS):
# ✅ Server-side only - NEVER expose in browser
supabase = create_client(
os.getenv('SUPABASE_URL'),
os.getenv('SUPABASE_SERVICE_ROLE_KEY') # From environment variable
)
# Full access (no RLS)
response = supabase.table('ticker_factor_metrics').select('*').execute()
Error Handling
API errors follow a structured format:
{
"error": "TICKER_NOT_FOUND",
"message": "Ticker 'XYZABC' not found in universe 'uni_mc_3000'. Check ticker spelling or try a different universe.",
"code": 404,
"details": {
"field": "ticker",
"received": "XYZABC",
"universe": "uni_mc_3000",
"suggestion": "Check for typos or use ticker search endpoint"
}
}
Common Errors:
401 AUTHENTICATION_REQUIRED: Missing or invalid JWT/service_role key404 TICKER_NOT_FOUND: Ticker doesn't exist in specified universe404 DATE_NOT_AVAILABLE: Non-trading day or out of range429 RATE_LIMIT_EXCEEDED: Too many requests (implement client-side throttling)
Response Metadata
All responses include metadata for freshness validation and unit tracking:
as_of- Date of data snapshotlast_updated- Timestamp of last data refreshfrequency- "daily" or "monthly"units- Field units (e.g., "decimal_fraction" for ER, "dollar_ratio" for HR)record_count- Number of records returnedquery_time_ms- Query execution time
Client-side validation:
from datetime import datetime, timedelta
def validate_freshness(metadata, max_age_days=7):
as_of_date = datetime.fromisoformat(metadata['as_of'])
age_days = (datetime.utcnow().date() - as_of_date.date()).days
return age_days <= max_age_days
metadata, data = client.get_latest_metrics('AAPL')
if not validate_freshness(metadata, max_age_days=3):
print("Warning: Data is stale")
Use Cases
1. Stock Screening for Alpha
# Find stocks with high residual risk (>50%)
response = supabase.table('ticker_factor_metrics') \
.select('ticker, l3_residual_er, volatility, sharpe_ratio') \
.gte('l3_residual_er', 0.5) \
.order('l3_residual_er', desc=True) \
.limit(50) \
.execute()
high_alpha_stocks = response.data
2. Factor-Neutral Portfolio Construction
# Get hedge ratios for portfolio
tickers = ['AAPL', 'MSFT', 'GOOGL']
metrics = []
for ticker in tickers:
response = supabase.table('ticker_factor_metrics') \
.select('ticker, l3_market_hr, l3_sector_hr, l3_subsector_hr') \
.eq('ticker', ticker) \
.execute()
metrics.append(response.data[0])
# Calculate hedge quantities
position_size = 100000 # $100k per stock
for m in metrics:
spy_hedge = position_size * m['l3_market_hr']
print(f"{m['ticker']}: Short ${spy_hedge:.0f} of SPY")
3. Time Series Risk Attribution
# Analyze residual risk trend over time
response = supabase.table('erm3_l3_decomposition') \
.select('date, l3_residual_er') \
.eq('ticker', 'AAPL') \
.eq('market_factor_etf', 'SPY') \
.eq('universe', 'uni_mc_3000') \
.order('date', ascending=True) \
.execute()
df = pd.DataFrame(response.data)
df['date'] = pd.to_datetime(df['date'])
df.plot(x='date', y='l3_residual_er', title='AAPL Residual Risk Over Time')
Full Documentation
For complete API reference, schemas, and integration guides, see the ERM3 API Documentation Repository:
| Document | Description | |----------|-------------| | OPENAPI_SPEC.yaml | Complete API contract with schemas and examples | | SEMANTIC_ALIASES.md | Field definitions, formulas, and source mappings | | AUTHENTICATION_GUIDE.md | JWT, service_role, token management, AI agent patterns | | RESPONSE_METADATA.md | Metadata contract, units, freshness validation | | ERROR_SCHEMA.md | Error codes, recovery patterns, debugging | | VALIDATION_HELPERS.md | Data quality checks, alignment validators |
How This Site Uses ERM3
Model Computation: All risk model regressions (Huber/Ridge) are computed in the ERM3 repository using Python, NumPy, and scikit-learn.
Data Consumption: This website (riskmodels.net) consumes published ERM3 outputs from Supabase and visualizes them in:
- Portfolio analytics dashboards
- Risk decomposition narratives
- Interactive factor analysis charts
- Hedge ratio calculators
Support
- API Issues: support@riskmodels.net
- Documentation: ERM3 API Docs
- Wiki: Risk Models Wiki
- Status: All systems operational
Last Updated: February 10, 2026
API Version: 1.0.0