How the Unified Risk Engine Computes VaR and CVaR in CloddsBot

CloddsBot's unified risk engine calculates Value-at-Risk (VaR) and Conditional VaR (CVaR) using a rolling window of profit-and-loss data, applying both historical percentile methods and parametric Gaussian models to deliver real-time portfolio risk metrics.

The alsk1992/CloddsBot repository implements a sophisticated risk management system that monitors trading exposure through dedicated statistical modules. This engine processes profit-and-loss histories to compute risk metrics that drive automated circuit-breaker decisions, preventing trades that would exceed configured risk thresholds.

Architecture of the Unified Risk Engine

The VaR Calculator Module

The core calculations reside in src/risk/var.ts, which exports the createVaRCalculator factory function. This module maintains a rolling window of P&L records and exposes the calculateAt method for retrieving risk metrics at specified confidence levels.

In src/risk/engine.ts, the risk engine instantiates the calculator during initialization with a configurable window size (defaulting to 100 observations). Line 158 creates the calculator instance, while line 290 invokes the calculation logic to enforce VaR limits against user-defined thresholds during trade evaluation.

P&L Data Management

The calculator stores the last windowSize P&L values in memory, updating the rolling window whenever new trades are recorded. This data structure enables rapid recalculation of risk metrics without reprocessing entire historical datasets, ensuring low-latency risk checks during active trading sessions.

VaR and CVaR Calculation Methods

Historical VaR (Percentile-Based)

The engine computes historical VaR by sorting the P&L array in ascending order at line 165 of src/risk/var.ts. It then extracts the loss at the target percentile—for 95% confidence, this represents the 5th percentile worst loss observed in the window. The sign is flipped to return a positive dollar amount, ensuring VaR represents potential portfolio loss rather than negative return values.

Parametric VaR (Gaussian Approach)

For parametric VaR, the calculator assumes normally distributed returns. It computes the mean and standard deviation of the P&L series, then applies the formula -(mean - z × σ) where z represents the z-score for the confidence level (e.g., 1.645 for 95%). This implementation appears at line 171 of src/risk/var.ts, providing a model-based alternative to the empirical historical method.

CVaR (Expected Shortfall)

Conditional VaR measures tail risk by averaging all losses that exceed the VaR threshold. Lines 175-179 of src/risk/var.ts isolate the "tail" events beyond the calculated VaR percentile and compute their mean. This Expected Shortfall metric indicates the average loss severity should the portfolio breach the VaR level, offering deeper insight into extreme downside scenarios.

Practical Implementation

Portfolio-Level Risk Queries

The engine exposes consolidated risk metrics through the calculateAt method. The following example demonstrates retrieving VaR and CVaR at the 95% confidence level:

import { RiskEngine } from '@/risk/engine';

const engine = new RiskEngine(/* config */);
const { historicalVaR, parametricVaR, cvar } = engine.getVaRCalculator().calculateAt(0.95);

console.log(`Historical VaR (95%): $${historicalVaR.toFixed(2)}`);
console.log(`Parametric VaR (95%): $${parametricVaR.toFixed(2)}`);
console.log(`CVaR/Expected Shortfall: $${cvar.toFixed(2)}`);

Per-Position Risk Analysis

The calculator provides position-level granularity through the positionVaR method. Lines 212-220 of src/risk/var.ts apply historical VaR logic to individual position P&L slices and calculate contribution ratios to total portfolio risk:

const positionVaRs = engine.getVaRCalculator().positionVaR();

positionVaRs.forEach(position => {
  console.log(
    `${position.id}: VaR $${position.var.toFixed(2)} ` +
    `(contribution: ${(position.contribution * 100).toFixed(1)}%)`
  );
});

Result Packaging

The calculator returns a VaRResult object containing historical VaR, parametric VaR, and CVaR values. Any negative values are clamped to zero (lines 179-180 of src/risk/var.ts), ensuring risk metrics represent actual potential loss exposures rather than statistical artifacts or data errors.

Summary

  • The unified risk engine in alsk1992/CloddsBot computes Value-at-Risk using both historical percentile and parametric Gaussian methods via src/risk/var.ts.
  • Conditional VaR (Expected Shortfall) is calculated as the average of tail losses exceeding the VaR threshold, implemented at lines 175-179.
  • The createVaRCalculator function maintains a rolling window of P&L data (default 100 records) integrated through src/risk/engine.ts.
  • Risk metrics drive circuit-breaker logic by comparing calculated VaR against configurable limits at lines 158 and 290 of the engine implementation.
  • Per-position VaR analysis enables granular risk attribution and contribution analysis for portfolio diversification insights.

Frequently Asked Questions

What is the default window size for P&L history in CloddsBot?

The default window size is 100 records, configurable through the varWindowSize parameter in the risk engine configuration. This rolling window determines how many historical profit-and-loss observations factor into VaR calculations, with older observations discarded as new trades arrive.

How does CloddsBot handle negative VaR values?

The calculator clamps negative values to zero when packaging results into the VaRResult object at lines 179-180 of src/risk/var.ts. This ensures that risk metrics represent actual potential loss exposures rather than statistical artifacts or profitable tail scenarios.

What confidence levels does the parametric VaR method support?

The parametric method supports any confidence level mapped to standard normal distribution z-scores, with 95% (z ≈ 1.645) being the system default. The implementation at line 171 dynamically calculates the appropriate z-score for the requested confidence level passed to the calculateAt method.

Can the risk engine reject trades based on VaR limits?

Yes. In src/risk/engine.ts at line 290, the engine queries currentVaR.historicalVaR and compares it against user-defined VaR limits. If the calculated risk exceeds the threshold, the circuit-breaker logic rejects the trade to prevent excessive portfolio exposure, utilizing the unified risk engine's real-time computation capabilities.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →