Skip to main content
Glama
Ademscodeisnotsobad

Quant Companion MCP

risk.test.ts3.68 kB
/** * Tests for risk metrics */ import { describe, it, expect } from "vitest"; import { computeRiskMetrics, computeMaxDrawdown, computeHistoricalVaR, } from "./risk"; describe("computeRiskMetrics", () => { it("computes basic metrics correctly", () => { // Simple equity curve: 10% total return over 10 days const equityCurve = [ 10000, 10100, 10200, 10300, 10200, 10400, 10500, 10600, 10800, 10900, 11000, ]; const metrics = computeRiskMetrics(equityCurve); expect(metrics.totalReturn).toBeCloseTo(0.1, 4); expect(metrics.annualizedReturn).toBeGreaterThan(0); expect(metrics.annualizedVol).toBeGreaterThan(0); }); it("computes Sharpe ratio", () => { const equityCurve = [10000, 10050, 10100, 10150, 10200, 10250]; const metrics = computeRiskMetrics(equityCurve, 0.02); expect(metrics.sharpe).not.toBeNull(); expect(metrics.sharpe!).toBeGreaterThan(0); }); it("returns null Sharpe when vol is zero", () => { // Constant equity = zero vol const equityCurve = [10000, 10000, 10000, 10000, 10000]; const metrics = computeRiskMetrics(equityCurve); expect(metrics.annualizedVol).toBe(0); expect(metrics.sharpe).toBeNull(); }); it("computes Sortino ratio", () => { // Curve with some negative days const equityCurve = [10000, 10100, 9900, 10050, 10200, 9800, 10300]; const metrics = computeRiskMetrics(equityCurve, 0.02); expect(metrics.sortino).not.toBeNull(); }); it("computes max drawdown correctly", () => { // Clear drawdown: peak at 11000, trough at 9000 const equityCurve = [10000, 11000, 10500, 9000, 9500, 10000]; const metrics = computeRiskMetrics(equityCurve); // Drawdown from 11000 to 9000 = 18.18% expect(metrics.maxDrawdown).toBeCloseTo(0.1818, 2); }); }); describe("computeMaxDrawdown", () => { it("returns 0 for monotonically increasing curve", () => { const equityCurve = [100, 110, 120, 130, 140]; const dd = computeMaxDrawdown(equityCurve); expect(dd).toBe(0); }); it("computes correct drawdown", () => { const equityCurve = [100, 120, 90, 110]; // Peak: 120, trough: 90, DD = 30/120 = 25% const dd = computeMaxDrawdown(equityCurve); expect(dd).toBeCloseTo(0.25, 4); }); it("handles multiple drawdowns", () => { const equityCurve = [100, 110, 100, 120, 90, 110]; // Biggest DD: 120 -> 90 = 25% const dd = computeMaxDrawdown(equityCurve); expect(dd).toBeCloseTo(0.25, 4); }); }); describe("computeHistoricalVaR", () => { it("computes 95% VaR correctly", () => { // 100 returns from -10% to +10% const returns: number[] = []; for (let i = 0; i < 100; i++) { returns.push((i - 50) / 500); // -0.1 to 0.098 } const result = computeHistoricalVaR({ returns, confidence: 0.95 }); // 5th percentile should be around -0.09 expect(result.var).toBeGreaterThan(0.08); expect(result.var).toBeLessThan(0.1); }); it("computes Expected Shortfall", () => { // More varied returns for meaningful ES calculation const returns = [-0.10, -0.08, -0.06, -0.05, -0.04, -0.03, -0.02, -0.01, 0.01, 0.02, 0.03, 0.04, 0.05]; const result = computeHistoricalVaR({ returns, confidence: 0.9 }); expect(result.expectedShortfall).toBeDefined(); // ES should be >= VaR (average of tail losses) expect(result.expectedShortfall!).toBeGreaterThanOrEqual(result.var); }); it("throws for invalid confidence", () => { expect(() => computeHistoricalVaR({ returns: [0.01], confidence: 1.5 }) ).toThrow(); expect(() => computeHistoricalVaR({ returns: [0.01], confidence: 0 }) ).toThrow(); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ademscodeisnotsobad/Quant-Companion-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server