running prettier for cleanup

This commit is contained in:
Boki 2025-06-11 10:13:25 -04:00
parent fe7733aeb5
commit d85cd58acd
151 changed files with 29158 additions and 27966 deletions

View file

@ -1,391 +1,429 @@
/**
* Basic Financial Calculations
* Core mathematical functions for financial analysis
*/
/**
* Calculate percentage change between two values
*/
export function percentageChange(oldValue: number, newValue: number): number {
if (oldValue === 0) return 0;
return ((newValue - oldValue) / oldValue) * 100;
}
/**
* Calculate simple return
*/
export function simpleReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice === 0) return 0;
return (finalPrice - initialPrice) / initialPrice;
}
/**
* Calculate logarithmic return
*/
export function logReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice <= 0 || finalPrice <= 0) return 0;
return Math.log(finalPrice / initialPrice);
}
/**
* Calculate compound annual growth rate (CAGR)
*/
export function cagr(startValue: number, endValue: number, years: number): number {
if (years <= 0 || startValue <= 0 || endValue <= 0) return 0;
return Math.pow(endValue / startValue, 1 / years) - 1;
}
/**
* Calculate annualized return from periodic returns
*/
export function annualizeReturn(periodicReturn: number, periodsPerYear: number): number {
return Math.pow(1 + periodicReturn, periodsPerYear) - 1;
}
/**
* Calculate annualized volatility from periodic returns
*/
export function annualizeVolatility(periodicVolatility: number, periodsPerYear: number): number {
return periodicVolatility * Math.sqrt(periodsPerYear);
}
/**
* Calculate present value
*/
export function presentValue(futureValue: number, rate: number, periods: number): number {
return futureValue / Math.pow(1 + rate, periods);
}
/**
* Calculate future value
*/
export function futureValue(presentValue: number, rate: number, periods: number): number {
return presentValue * Math.pow(1 + rate, periods);
}
/**
* Calculate net present value of cash flows
*/
export function netPresentValue(cashFlows: number[], discountRate: number): number {
return cashFlows.reduce((npv, cashFlow, index) => {
return npv + cashFlow / Math.pow(1 + discountRate, index);
}, 0);
}
/**
* Calculate internal rate of return (IRR) using Newton-Raphson method
*/
export function internalRateOfReturn(cashFlows: number[], guess: number = 0.1, maxIterations: number = 100): number {
let rate = guess;
for (let i = 0; i < maxIterations; i++) {
let npv = 0;
let dnpv = 0;
for (let j = 0; j < cashFlows.length; j++) {
npv += cashFlows[j] / Math.pow(1 + rate, j);
dnpv += -j * cashFlows[j] / Math.pow(1 + rate, j + 1);
}
if (Math.abs(npv) < 1e-10) break;
if (Math.abs(dnpv) < 1e-10) break;
rate = rate - npv / dnpv;
}
return rate;
}
/**
* Calculate payback period
*/
export function paybackPeriod(initialInvestment: number, cashFlows: number[]): number {
let cumulativeCashFlow = 0;
for (let i = 0; i < cashFlows.length; i++) {
cumulativeCashFlow += cashFlows[i];
if (cumulativeCashFlow >= initialInvestment) {
return i + 1 - (cumulativeCashFlow - initialInvestment) / cashFlows[i];
}
}
return -1; // Never pays back
}
/**
* Calculate compound interest
*/
export function compoundInterest(
principal: number,
rate: number,
periods: number,
compoundingFrequency: number = 1
): number {
return principal * Math.pow(1 + rate / compoundingFrequency, compoundingFrequency * periods);
}
/**
* Calculate effective annual rate
*/
export function effectiveAnnualRate(nominalRate: number, compoundingFrequency: number): number {
return Math.pow(1 + nominalRate / compoundingFrequency, compoundingFrequency) - 1;
}
/**
* Calculate bond price given yield
*/
export function bondPrice(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
let price = 0;
// Present value of coupon payments
for (let i = 1; i <= periodsToMaturity; i++) {
price += couponPayment / Math.pow(1 + discountRate, i);
}
// Present value of face value
price += faceValue / Math.pow(1 + discountRate, periodsToMaturity);
return price;
}
/**
* Calculate bond yield given price (Newton-Raphson approximation)
*/
export function bondYield(
price: number,
faceValue: number,
couponRate: number,
periodsToMaturity: number,
paymentsPerYear: number = 2,
guess: number = 0.05
): number {
let yield_ = guess;
const maxIterations = 100;
const tolerance = 1e-8;
for (let i = 0; i < maxIterations; i++) {
const calculatedPrice = bondPrice(faceValue, couponRate, yield_, periodsToMaturity, paymentsPerYear);
const diff = calculatedPrice - price;
if (Math.abs(diff) < tolerance) break;
// Numerical derivative
const delta = 0.0001;
const priceUp = bondPrice(faceValue, couponRate, yield_ + delta, periodsToMaturity, paymentsPerYear);
const derivative = (priceUp - calculatedPrice) / delta;
if (Math.abs(derivative) < tolerance) break;
yield_ = yield_ - diff / derivative;
}
return yield_;
}
/**
* Calculate duration (Macaulay duration)
*/
export function macaulayDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear);
let weightedTime = 0;
// Weighted time of coupon payments
for (let i = 1; i <= periodsToMaturity; i++) {
const presentValue = couponPayment / Math.pow(1 + discountRate, i);
weightedTime += (i * presentValue) / bondPriceValue;
}
// Weighted time of face value
const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity);
weightedTime += (periodsToMaturity * faceValuePV) / bondPriceValue;
return weightedTime / paymentsPerYear; // Convert to years
}
/**
* Calculate modified duration
*/
export function modifiedDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const macDuration = macaulayDuration(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear);
return macDuration / (1 + yieldToMaturity / paymentsPerYear);
}
/**
* Calculate bond convexity
*/
export function bondConvexity(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
let convexity = 0;
const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear);
for (let i = 1; i <= periodsToMaturity; i++) {
const presentValue = couponPayment / Math.pow(1 + discountRate, i);
convexity += (i * (i + 1) * presentValue) / Math.pow(1 + discountRate, 2);
}
const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity);
convexity += (periodsToMaturity * (periodsToMaturity + 1) * faceValuePV) / Math.pow(1 + discountRate, 2);
return convexity / (bondPriceValue * paymentsPerYear * paymentsPerYear);
}
/**
* Calculate dollar duration
*/
export function dollarDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2,
basisPointChange: number = 0.01 // 1 basis point = 0.01%
): number {
const modifiedDur = modifiedDuration(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear);
const bondPriceValue = bondPrice(faceValue, couponRate, yieldToMaturity, periodsToMaturity, paymentsPerYear);
return modifiedDur * bondPriceValue * basisPointChange;
}
/**
* Calculate accrued interest
*/
export function accruedInterest(
faceValue: number,
couponRate: number,
daysSinceLastCoupon: number,
daysInCouponPeriod: number
): number {
return (faceValue * couponRate) * (daysSinceLastCoupon / daysInCouponPeriod);
}
/**
* Calculate clean price
*/
export function cleanPrice(dirtyPrice: number, accruedInterestValue: number): number {
return dirtyPrice - accruedInterestValue;
}
/**
* Calculate dirty price
*/
export function dirtyPrice(cleanPriceValue: number, accruedInterestValue: number): number {
return cleanPriceValue + accruedInterestValue;
}
/**
* Calculate dividend discount model (DDM)
*/
export function dividendDiscountModel(
currentDividend: number,
growthRate: number,
discountRate: number
): number {
if (discountRate <= growthRate) return NaN; // Indeterminate
return currentDividend * (1 + growthRate) / (discountRate - growthRate);
}
/**
* Calculate weighted average cost of capital (WACC)
*/
export function weightedAverageCostOfCapital(
costOfEquity: number,
costOfDebt: number,
equityWeight: number,
debtWeight: number,
taxRate: number
): number {
return (equityWeight * costOfEquity) + (debtWeight * costOfDebt * (1 - taxRate));
}
/**
* Calculate capital asset pricing model (CAPM)
*/
export function capitalAssetPricingModel(
riskFreeRate: number,
beta: number,
marketRiskPremium: number
): number {
return riskFreeRate + beta * marketRiskPremium;
}
/**
* Calculate hurdle rate
*/
export function hurdleRate(
costOfCapital: number,
riskPremium: number
): number {
return costOfCapital + riskPremium;
}
/**
* Calculate degree of operating leverage (DOL)
*/
export function degreeOfOperatingLeverage(
contributionMargin: number,
operatingIncome: number
): number {
return contributionMargin / operatingIncome;
}
/**
* Calculate degree of financial leverage (DFL)
*/
export function degreeOfFinancialLeverage(
ebit: number,
earningsBeforeTax: number
): number {
return ebit / earningsBeforeTax;
}
/**
* Calculate degree of total leverage (DTL)
*/
export function degreeOfTotalLeverage(
dol: number,
dfl: number
): number {
return dol * dfl;
}
/**
* Calculate economic value added (EVA)
*/
export function economicValueAdded(
netOperatingProfitAfterTax: number,
capitalInvested: number,
wacc: number
): number {
return netOperatingProfitAfterTax - (capitalInvested * wacc);
}
/**
* Basic Financial Calculations
* Core mathematical functions for financial analysis
*/
/**
* Calculate percentage change between two values
*/
export function percentageChange(oldValue: number, newValue: number): number {
if (oldValue === 0) return 0;
return ((newValue - oldValue) / oldValue) * 100;
}
/**
* Calculate simple return
*/
export function simpleReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice === 0) return 0;
return (finalPrice - initialPrice) / initialPrice;
}
/**
* Calculate logarithmic return
*/
export function logReturn(initialPrice: number, finalPrice: number): number {
if (initialPrice <= 0 || finalPrice <= 0) return 0;
return Math.log(finalPrice / initialPrice);
}
/**
* Calculate compound annual growth rate (CAGR)
*/
export function cagr(startValue: number, endValue: number, years: number): number {
if (years <= 0 || startValue <= 0 || endValue <= 0) return 0;
return Math.pow(endValue / startValue, 1 / years) - 1;
}
/**
* Calculate annualized return from periodic returns
*/
export function annualizeReturn(periodicReturn: number, periodsPerYear: number): number {
return Math.pow(1 + periodicReturn, periodsPerYear) - 1;
}
/**
* Calculate annualized volatility from periodic returns
*/
export function annualizeVolatility(periodicVolatility: number, periodsPerYear: number): number {
return periodicVolatility * Math.sqrt(periodsPerYear);
}
/**
* Calculate present value
*/
export function presentValue(futureValue: number, rate: number, periods: number): number {
return futureValue / Math.pow(1 + rate, periods);
}
/**
* Calculate future value
*/
export function futureValue(presentValue: number, rate: number, periods: number): number {
return presentValue * Math.pow(1 + rate, periods);
}
/**
* Calculate net present value of cash flows
*/
export function netPresentValue(cashFlows: number[], discountRate: number): number {
return cashFlows.reduce((npv, cashFlow, index) => {
return npv + cashFlow / Math.pow(1 + discountRate, index);
}, 0);
}
/**
* Calculate internal rate of return (IRR) using Newton-Raphson method
*/
export function internalRateOfReturn(
cashFlows: number[],
guess: number = 0.1,
maxIterations: number = 100
): number {
let rate = guess;
for (let i = 0; i < maxIterations; i++) {
let npv = 0;
let dnpv = 0;
for (let j = 0; j < cashFlows.length; j++) {
npv += cashFlows[j] / Math.pow(1 + rate, j);
dnpv += (-j * cashFlows[j]) / Math.pow(1 + rate, j + 1);
}
if (Math.abs(npv) < 1e-10) break;
if (Math.abs(dnpv) < 1e-10) break;
rate = rate - npv / dnpv;
}
return rate;
}
/**
* Calculate payback period
*/
export function paybackPeriod(initialInvestment: number, cashFlows: number[]): number {
let cumulativeCashFlow = 0;
for (let i = 0; i < cashFlows.length; i++) {
cumulativeCashFlow += cashFlows[i];
if (cumulativeCashFlow >= initialInvestment) {
return i + 1 - (cumulativeCashFlow - initialInvestment) / cashFlows[i];
}
}
return -1; // Never pays back
}
/**
* Calculate compound interest
*/
export function compoundInterest(
principal: number,
rate: number,
periods: number,
compoundingFrequency: number = 1
): number {
return principal * Math.pow(1 + rate / compoundingFrequency, compoundingFrequency * periods);
}
/**
* Calculate effective annual rate
*/
export function effectiveAnnualRate(nominalRate: number, compoundingFrequency: number): number {
return Math.pow(1 + nominalRate / compoundingFrequency, compoundingFrequency) - 1;
}
/**
* Calculate bond price given yield
*/
export function bondPrice(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
let price = 0;
// Present value of coupon payments
for (let i = 1; i <= periodsToMaturity; i++) {
price += couponPayment / Math.pow(1 + discountRate, i);
}
// Present value of face value
price += faceValue / Math.pow(1 + discountRate, periodsToMaturity);
return price;
}
/**
* Calculate bond yield given price (Newton-Raphson approximation)
*/
export function bondYield(
price: number,
faceValue: number,
couponRate: number,
periodsToMaturity: number,
paymentsPerYear: number = 2,
guess: number = 0.05
): number {
let yield_ = guess;
const maxIterations = 100;
const tolerance = 1e-8;
for (let i = 0; i < maxIterations; i++) {
const calculatedPrice = bondPrice(
faceValue,
couponRate,
yield_,
periodsToMaturity,
paymentsPerYear
);
const diff = calculatedPrice - price;
if (Math.abs(diff) < tolerance) break;
// Numerical derivative
const delta = 0.0001;
const priceUp = bondPrice(
faceValue,
couponRate,
yield_ + delta,
periodsToMaturity,
paymentsPerYear
);
const derivative = (priceUp - calculatedPrice) / delta;
if (Math.abs(derivative) < tolerance) break;
yield_ = yield_ - diff / derivative;
}
return yield_;
}
/**
* Calculate duration (Macaulay duration)
*/
export function macaulayDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
const bondPriceValue = bondPrice(
faceValue,
couponRate,
yieldToMaturity,
periodsToMaturity,
paymentsPerYear
);
let weightedTime = 0;
// Weighted time of coupon payments
for (let i = 1; i <= periodsToMaturity; i++) {
const presentValue = couponPayment / Math.pow(1 + discountRate, i);
weightedTime += (i * presentValue) / bondPriceValue;
}
// Weighted time of face value
const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity);
weightedTime += (periodsToMaturity * faceValuePV) / bondPriceValue;
return weightedTime / paymentsPerYear; // Convert to years
}
/**
* Calculate modified duration
*/
export function modifiedDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const macDuration = macaulayDuration(
faceValue,
couponRate,
yieldToMaturity,
periodsToMaturity,
paymentsPerYear
);
return macDuration / (1 + yieldToMaturity / paymentsPerYear);
}
/**
* Calculate bond convexity
*/
export function bondConvexity(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2
): number {
const couponPayment = (faceValue * couponRate) / paymentsPerYear;
const discountRate = yieldToMaturity / paymentsPerYear;
let convexity = 0;
const bondPriceValue = bondPrice(
faceValue,
couponRate,
yieldToMaturity,
periodsToMaturity,
paymentsPerYear
);
for (let i = 1; i <= periodsToMaturity; i++) {
const presentValue = couponPayment / Math.pow(1 + discountRate, i);
convexity += (i * (i + 1) * presentValue) / Math.pow(1 + discountRate, 2);
}
const faceValuePV = faceValue / Math.pow(1 + discountRate, periodsToMaturity);
convexity +=
(periodsToMaturity * (periodsToMaturity + 1) * faceValuePV) / Math.pow(1 + discountRate, 2);
return convexity / (bondPriceValue * paymentsPerYear * paymentsPerYear);
}
/**
* Calculate dollar duration
*/
export function dollarDuration(
faceValue: number,
couponRate: number,
yieldToMaturity: number,
periodsToMaturity: number,
paymentsPerYear: number = 2,
basisPointChange: number = 0.01 // 1 basis point = 0.01%
): number {
const modifiedDur = modifiedDuration(
faceValue,
couponRate,
yieldToMaturity,
periodsToMaturity,
paymentsPerYear
);
const bondPriceValue = bondPrice(
faceValue,
couponRate,
yieldToMaturity,
periodsToMaturity,
paymentsPerYear
);
return modifiedDur * bondPriceValue * basisPointChange;
}
/**
* Calculate accrued interest
*/
export function accruedInterest(
faceValue: number,
couponRate: number,
daysSinceLastCoupon: number,
daysInCouponPeriod: number
): number {
return faceValue * couponRate * (daysSinceLastCoupon / daysInCouponPeriod);
}
/**
* Calculate clean price
*/
export function cleanPrice(dirtyPrice: number, accruedInterestValue: number): number {
return dirtyPrice - accruedInterestValue;
}
/**
* Calculate dirty price
*/
export function dirtyPrice(cleanPriceValue: number, accruedInterestValue: number): number {
return cleanPriceValue + accruedInterestValue;
}
/**
* Calculate dividend discount model (DDM)
*/
export function dividendDiscountModel(
currentDividend: number,
growthRate: number,
discountRate: number
): number {
if (discountRate <= growthRate) return NaN; // Indeterminate
return (currentDividend * (1 + growthRate)) / (discountRate - growthRate);
}
/**
* Calculate weighted average cost of capital (WACC)
*/
export function weightedAverageCostOfCapital(
costOfEquity: number,
costOfDebt: number,
equityWeight: number,
debtWeight: number,
taxRate: number
): number {
return equityWeight * costOfEquity + debtWeight * costOfDebt * (1 - taxRate);
}
/**
* Calculate capital asset pricing model (CAPM)
*/
export function capitalAssetPricingModel(
riskFreeRate: number,
beta: number,
marketRiskPremium: number
): number {
return riskFreeRate + beta * marketRiskPremium;
}
/**
* Calculate hurdle rate
*/
export function hurdleRate(costOfCapital: number, riskPremium: number): number {
return costOfCapital + riskPremium;
}
/**
* Calculate degree of operating leverage (DOL)
*/
export function degreeOfOperatingLeverage(
contributionMargin: number,
operatingIncome: number
): number {
return contributionMargin / operatingIncome;
}
/**
* Calculate degree of financial leverage (DFL)
*/
export function degreeOfFinancialLeverage(ebit: number, earningsBeforeTax: number): number {
return ebit / earningsBeforeTax;
}
/**
* Calculate degree of total leverage (DTL)
*/
export function degreeOfTotalLeverage(dol: number, dfl: number): number {
return dol * dfl;
}
/**
* Calculate economic value added (EVA)
*/
export function economicValueAdded(
netOperatingProfitAfterTax: number,
capitalInvested: number,
wacc: number
): number {
return netOperatingProfitAfterTax - capitalInvested * wacc;
}

File diff suppressed because it is too large Load diff

View file

@ -1,166 +1,175 @@
/**
* Comprehensive Financial Calculations Library
*
* This module provides a complete set of financial calculations for trading and investment analysis.
* Organized into logical categories for easy use and maintenance.
*/
// Core interfaces for financial data
export interface OHLCVData {
open: number;
high: number;
low: number;
close: number;
volume: number;
timestamp: Date;
}
export interface PriceData {
price: number;
timestamp: Date;
}
// Financial calculation result interfaces
export interface PortfolioMetrics {
totalValue: number;
totalReturn: number;
totalReturnPercent: number;
dailyReturn: number;
dailyReturnPercent: number;
maxDrawdown: number;
sharpeRatio: number;
beta: number;
alpha: number;
volatility: number;
}
export interface RiskMetrics {
var95: number; // Value at Risk 95%
var99: number; // Value at Risk 99%
cvar95: number; // Conditional VaR 95%
maxDrawdown: number;
volatility: number;
downside_deviation: number;
calmar_ratio: number;
sortino_ratio: number;
beta: number;
alpha: number;
sharpeRatio: number;
treynorRatio: number;
trackingError: number;
informationRatio: number;
}
export interface TechnicalIndicators {
sma: number[];
ema: number[];
rsi: number[];
macd: { macd: number[], signal: number[], histogram: number[] };
bollinger: { upper: number[], middle: number[], lower: number[] };
atr: number[];
stochastic: { k: number[], d: number[] };
williams_r: number[];
cci: number[];
momentum: number[];
roc: number[];
}
// Additional interfaces for new functionality
export interface TradeExecution {
entry: number;
exit: number;
peak?: number;
trough?: number;
volume: number;
timestamp: Date;
}
export interface MarketData {
price: number;
volume: number;
timestamp: Date;
bid?: number;
ask?: number;
bidSize?: number;
askSize?: number;
}
export interface BacktestResults {
trades: TradeExecution[];
equityCurve: Array<{ value: number; date: Date }>;
performance: PortfolioMetrics;
riskMetrics: RiskMetrics;
drawdownAnalysis: any; // Import from performance-metrics
}
// Export all calculation functions
export * from './basic-calculations';
export * from './technical-indicators';
export * from './risk-metrics';
export * from './portfolio-analytics';
export * from './options-pricing';
export * from './position-sizing';
export * from './performance-metrics';
export * from './market-statistics';
export * from './volatility-models';
export * from './correlation-analysis';
// Import specific functions for convenience functions
import {
sma, ema, rsi, macd, bollingerBands, atr, stochastic,
williamsR, cci, momentum, roc
} from './technical-indicators';
import { calculateRiskMetrics } from './risk-metrics';
import { calculateStrategyMetrics } from './performance-metrics';
// Convenience function to calculate all technical indicators at once
export function calculateAllTechnicalIndicators(
ohlcv: OHLCVData[],
periods: { sma?: number; ema?: number; rsi?: number; atr?: number } = {}
): TechnicalIndicators {
const {
sma: smaPeriod = 20,
ema: emaPeriod = 20,
rsi: rsiPeriod = 14,
atr: atrPeriod = 14
} = periods;
const closes = ohlcv.map(d => d.close);
return {
sma: sma(closes, smaPeriod),
ema: ema(closes, emaPeriod),
rsi: rsi(closes, rsiPeriod),
macd: macd(closes),
bollinger: bollingerBands(closes),
atr: atr(ohlcv, atrPeriod),
stochastic: stochastic(ohlcv),
williams_r: williamsR(ohlcv),
cci: cci(ohlcv),
momentum: momentum(closes),
roc: roc(closes)
};
}
// Convenience function for comprehensive portfolio analysis
export function analyzePortfolio(
returns: number[],
equityCurve: Array<{ value: number; date: Date }>,
benchmarkReturns?: number[],
riskFreeRate: number = 0.02
): {
performance: PortfolioMetrics;
risk: RiskMetrics;
trades?: any;
drawdown?: any;
} {
const performance = calculateStrategyMetrics(equityCurve, benchmarkReturns, riskFreeRate);
const equityValues = equityCurve.map(point => point.value);
const risk = calculateRiskMetrics(returns, equityValues, benchmarkReturns, riskFreeRate);
return {
performance,
risk
};
}
// Import specific functions for convenience functions
import { calculateStrategyMetrics } from './performance-metrics';
import { calculateRiskMetrics } from './risk-metrics';
import {
atr,
bollingerBands,
cci,
ema,
macd,
momentum,
roc,
rsi,
sma,
stochastic,
williamsR,
} from './technical-indicators';
/**
* Comprehensive Financial Calculations Library
*
* This module provides a complete set of financial calculations for trading and investment analysis.
* Organized into logical categories for easy use and maintenance.
*/
// Core interfaces for financial data
export interface OHLCVData {
open: number;
high: number;
low: number;
close: number;
volume: number;
timestamp: Date;
}
export interface PriceData {
price: number;
timestamp: Date;
}
// Financial calculation result interfaces
export interface PortfolioMetrics {
totalValue: number;
totalReturn: number;
totalReturnPercent: number;
dailyReturn: number;
dailyReturnPercent: number;
maxDrawdown: number;
sharpeRatio: number;
beta: number;
alpha: number;
volatility: number;
}
export interface RiskMetrics {
var95: number; // Value at Risk 95%
var99: number; // Value at Risk 99%
cvar95: number; // Conditional VaR 95%
maxDrawdown: number;
volatility: number;
downside_deviation: number;
calmar_ratio: number;
sortino_ratio: number;
beta: number;
alpha: number;
sharpeRatio: number;
treynorRatio: number;
trackingError: number;
informationRatio: number;
}
export interface TechnicalIndicators {
sma: number[];
ema: number[];
rsi: number[];
macd: { macd: number[]; signal: number[]; histogram: number[] };
bollinger: { upper: number[]; middle: number[]; lower: number[] };
atr: number[];
stochastic: { k: number[]; d: number[] };
williams_r: number[];
cci: number[];
momentum: number[];
roc: number[];
}
// Additional interfaces for new functionality
export interface TradeExecution {
entry: number;
exit: number;
peak?: number;
trough?: number;
volume: number;
timestamp: Date;
}
export interface MarketData {
price: number;
volume: number;
timestamp: Date;
bid?: number;
ask?: number;
bidSize?: number;
askSize?: number;
}
export interface BacktestResults {
trades: TradeExecution[];
equityCurve: Array<{ value: number; date: Date }>;
performance: PortfolioMetrics;
riskMetrics: RiskMetrics;
drawdownAnalysis: any; // Import from performance-metrics
}
// Export all calculation functions
export * from './basic-calculations';
export * from './technical-indicators';
export * from './risk-metrics';
export * from './portfolio-analytics';
export * from './options-pricing';
export * from './position-sizing';
export * from './performance-metrics';
export * from './market-statistics';
export * from './volatility-models';
export * from './correlation-analysis';
// Convenience function to calculate all technical indicators at once
export function calculateAllTechnicalIndicators(
ohlcv: OHLCVData[],
periods: { sma?: number; ema?: number; rsi?: number; atr?: number } = {}
): TechnicalIndicators {
const {
sma: smaPeriod = 20,
ema: emaPeriod = 20,
rsi: rsiPeriod = 14,
atr: atrPeriod = 14,
} = periods;
const closes = ohlcv.map(d => d.close);
return {
sma: sma(closes, smaPeriod),
ema: ema(closes, emaPeriod),
rsi: rsi(closes, rsiPeriod),
macd: macd(closes),
bollinger: bollingerBands(closes),
atr: atr(ohlcv, atrPeriod),
stochastic: stochastic(ohlcv),
williams_r: williamsR(ohlcv),
cci: cci(ohlcv),
momentum: momentum(closes),
roc: roc(closes),
};
}
// Convenience function for comprehensive portfolio analysis
export function analyzePortfolio(
returns: number[],
equityCurve: Array<{ value: number; date: Date }>,
benchmarkReturns?: number[],
riskFreeRate: number = 0.02
): {
performance: PortfolioMetrics;
risk: RiskMetrics;
trades?: any;
drawdown?: any;
} {
const performance = calculateStrategyMetrics(equityCurve, benchmarkReturns, riskFreeRate);
const equityValues = equityCurve.map(point => point.value);
const risk = calculateRiskMetrics(returns, equityValues, benchmarkReturns, riskFreeRate);
return {
performance,
risk,
};
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,375 +1,387 @@
/**
* Risk Metrics and Analysis
* Comprehensive risk measurement tools for portfolio and trading analysis
*/
import { RiskMetrics, treynorRatio } from './index';
/**
* Calculate Value at Risk (VaR) using historical simulation
*/
export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
const sortedReturns = [...returns].sort((a, b) => a - b);
const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
return sortedReturns[index] || 0;
}
/**
* Calculate Conditional Value at Risk (CVaR/Expected Shortfall)
*/
export function conditionalValueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
const sortedReturns = [...returns].sort((a, b) => a - b);
const cutoffIndex = Math.floor((1 - confidenceLevel) * sortedReturns.length);
if (cutoffIndex === 0) return sortedReturns[0];
const tailReturns = sortedReturns.slice(0, cutoffIndex);
return tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
}
/**
* Calculate parametric VaR using normal distribution
*/
export function parametricVaR(
returns: number[],
confidenceLevel: number = 0.95,
portfolioValue: number = 1
): number {
if (returns.length === 0) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
const stdDev = Math.sqrt(variance);
// Z-score for confidence level (normal distribution)
const zScore = getZScore(confidenceLevel);
return portfolioValue * (mean - zScore * stdDev);
}
/**
* Calculate maximum drawdown
*/
export function maxDrawdown(equityCurve: number[]): number {
if (equityCurve.length < 2) return 0;
let maxDD = 0;
let peak = equityCurve[0];
for (let i = 1; i < equityCurve.length; i++) {
if (equityCurve[i] > peak) {
peak = equityCurve[i];
} else {
const drawdown = (peak - equityCurve[i]) / peak;
maxDD = Math.max(maxDD, drawdown);
}
}
return maxDD;
}
/**
* Calculate downside deviation
*/
export function downsideDeviation(returns: number[], targetReturn: number = 0): number {
if (returns.length === 0) return 0;
const downsideReturns = returns.filter(ret => ret < targetReturn);
if (downsideReturns.length === 0) return 0;
const sumSquaredDownside = downsideReturns.reduce(
(sum, ret) => sum + Math.pow(ret - targetReturn, 2),
0
);
return Math.sqrt(sumSquaredDownside / returns.length);
}
/**
* Calculate Sharpe ratio
*/
export function sharpeRatio(returns: number[], riskFreeRate: number = 0): number {
if (returns.length < 2) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
return (mean - riskFreeRate) / stdDev;
}
/**
* Calculate beta coefficient
*/
export function beta(portfolioReturns: number[], marketReturns: number[]): number {
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) {
return 0;
}
const n = portfolioReturns.length;
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / n;
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / n;
let covariance = 0;
let marketVariance = 0;
for (let i = 0; i < n; i++) {
const portfolioDiff = portfolioReturns[i] - portfolioMean;
const marketDiff = marketReturns[i] - marketMean;
covariance += portfolioDiff * marketDiff;
marketVariance += marketDiff * marketDiff;
}
return marketVariance === 0 ? 0 : covariance / marketVariance;
}
/**
* Calculate alpha
*/
export function alpha(
portfolioReturns: number[],
marketReturns: number[],
riskFreeRate: number = 0
): number {
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / portfolioReturns.length;
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / marketReturns.length;
const portfolioBeta = beta(portfolioReturns, marketReturns);
return portfolioMean - (riskFreeRate + portfolioBeta * (marketMean - riskFreeRate));
}
/**
* Calculate tracking error
*/
export function trackingError(portfolioReturns: number[], benchmarkReturns: number[]): number {
if (portfolioReturns.length !== benchmarkReturns.length || portfolioReturns.length === 0) {
return 0;
}
const activeReturns = portfolioReturns.map((ret, i) => ret - benchmarkReturns[i]);
const mean = activeReturns.reduce((sum, ret) => sum + ret, 0) / activeReturns.length;
const variance = activeReturns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (activeReturns.length - 1);
return Math.sqrt(variance);
}
/**
* Calculate volatility (standard deviation of returns)
*/
export function volatility(returns: number[]): number {
if (returns.length < 2) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
return Math.sqrt(variance);
}
/**
* Calculate annualized volatility
*/
export function annualizedVolatility(returns: number[], periodsPerYear: number = 252): number {
return volatility(returns) * Math.sqrt(periodsPerYear);
}
/**
* Calculate skewness (measure of asymmetry)
*/
export function skewness(returns: number[]): number {
if (returns.length < 3) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
const skew = returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
return skew;
}
/**
* Calculate kurtosis (measure of tail heaviness)
*/
export function kurtosis(returns: number[]): number {
if (returns.length < 4) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
const kurt = returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;
return kurt - 3; // Excess kurtosis (subtract 3 for normal distribution baseline)
}
/**
* Calculate comprehensive risk metrics
*/
export function calculateRiskMetrics(
returns: number[],
equityCurve: number[],
marketReturns?: number[],
riskFreeRate: number = 0
): RiskMetrics {
if (returns.length === 0) {
return {
var95: 0,
var99: 0,
cvar95: 0,
maxDrawdown: 0,
volatility: 0,
downside_deviation: 0,
calmar_ratio: 0,
sortino_ratio: 0,
beta: 0,
alpha: 0,
sharpeRatio: 0,
treynorRatio: 0,
trackingError: 0,
informationRatio: 0
};
}
const portfolioVolatility = volatility(returns);
const portfolioMean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
// Calculate VaR
const var95Value = valueAtRisk(returns, 0.95);
const var99Value = valueAtRisk(returns, 0.99);
const cvar95Value = conditionalValueAtRisk(returns, 0.95);
// Calculate max drawdown
const maxDD = maxDrawdown(equityCurve);
// Calculate downside deviation
const downsideDeviationValue = downsideDeviation(returns);
// Calculate ratios
const calmarRatio = maxDD > 0 ? portfolioMean / maxDD : 0;
const sortinoRatio = downsideDeviationValue > 0 ? (portfolioMean - riskFreeRate) / downsideDeviationValue : 0;
const sharpeRatio = portfolioVolatility > 0 ? (portfolioMean - riskFreeRate) / portfolioVolatility : 0;
let portfolioBeta = 0;
let portfolioAlpha = 0;
let portfolioTreynorRatio = 0;
let portfolioTrackingError = 0;
let informationRatio = 0;
if (marketReturns && marketReturns.length === returns.length) {
portfolioBeta = beta(returns, marketReturns);
portfolioAlpha = alpha(returns, marketReturns, riskFreeRate);
portfolioTreynorRatio = treynorRatio(returns, marketReturns, riskFreeRate);
portfolioTrackingError = trackingError(returns, marketReturns);
informationRatio = portfolioTrackingError > 0 ? portfolioAlpha / portfolioTrackingError : 0;
}
return {
var95: var95Value,
var99: var99Value,
cvar95: cvar95Value,
maxDrawdown: maxDD,
volatility: portfolioVolatility,
downside_deviation: downsideDeviationValue,
calmar_ratio: calmarRatio,
sortino_ratio: sortinoRatio,
beta: portfolioBeta,
alpha: portfolioAlpha,
sharpeRatio,
treynorRatio: portfolioTreynorRatio,
trackingError: portfolioTrackingError,
informationRatio
};
}
/**
* Helper function to get Z-score for confidence level
* This implementation handles arbitrary confidence levels
*/
function getZScore(confidenceLevel: number): number {
// First check our lookup table for common values (more precise)
const zScores: { [key: string]: number } = {
'0.90': 1.282,
'0.95': 1.645,
'0.975': 1.960,
'0.99': 2.326,
'0.995': 2.576
};
const key = confidenceLevel.toString();
if (zScores[key]) return zScores[key];
// For arbitrary confidence levels, use approximation
if (confidenceLevel < 0.5) return -getZScore(1 - confidenceLevel);
if (confidenceLevel >= 0.999) return 3.09; // Cap at 99.9% for numerical stability
// Approximation of inverse normal CDF
const y = Math.sqrt(-2.0 * Math.log(1.0 - confidenceLevel));
return y - (2.515517 + 0.802853 * y + 0.010328 * y * y) /
(1.0 + 1.432788 * y + 0.189269 * y * y + 0.001308 * y * y * y);
}
/**
* Calculate portfolio risk contribution
*/
export function riskContribution(
weights: number[],
covarianceMatrix: number[][],
portfolioVolatility: number
): number[] {
const n = weights.length;
const contributions: number[] = [];
for (let i = 0; i < n; i++) {
let marginalContribution = 0;
for (let j = 0; j < n; j++) {
marginalContribution += weights[j] * covarianceMatrix[i][j];
}
const contribution = (weights[i] * marginalContribution) / Math.pow(portfolioVolatility, 2);
contributions.push(contribution);
}
return contributions;
}
/**
* Calculate Ulcer Index
*/
export function ulcerIndex(equityCurve: Array<{ value: number; date: Date }>): number {
let sumSquaredDrawdown = 0;
let peak = equityCurve[0].value;
for (const point of equityCurve) {
peak = Math.max(peak, point.value);
const drawdownPercent = (peak - point.value) / peak * 100;
sumSquaredDrawdown += drawdownPercent * drawdownPercent;
}
return Math.sqrt(sumSquaredDrawdown / equityCurve.length);
}
/**
* Calculate risk-adjusted return (RAR)
*/
export function riskAdjustedReturn(
portfolioReturn: number,
portfolioRisk: number,
riskFreeRate: number = 0
): number {
if (portfolioRisk === 0) return 0;
return (portfolioReturn - riskFreeRate) / portfolioRisk;
}
/**
* Risk Metrics and Analysis
* Comprehensive risk measurement tools for portfolio and trading analysis
*/
import { RiskMetrics, treynorRatio } from './index';
/**
* Calculate Value at Risk (VaR) using historical simulation
*/
export function valueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
const sortedReturns = [...returns].sort((a, b) => a - b);
const index = Math.floor((1 - confidenceLevel) * sortedReturns.length);
return sortedReturns[index] || 0;
}
/**
* Calculate Conditional Value at Risk (CVaR/Expected Shortfall)
*/
export function conditionalValueAtRisk(returns: number[], confidenceLevel: number = 0.95): number {
if (returns.length === 0) return 0;
const sortedReturns = [...returns].sort((a, b) => a - b);
const cutoffIndex = Math.floor((1 - confidenceLevel) * sortedReturns.length);
if (cutoffIndex === 0) return sortedReturns[0];
const tailReturns = sortedReturns.slice(0, cutoffIndex);
return tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length;
}
/**
* Calculate parametric VaR using normal distribution
*/
export function parametricVaR(
returns: number[],
confidenceLevel: number = 0.95,
portfolioValue: number = 1
): number {
if (returns.length === 0) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
const stdDev = Math.sqrt(variance);
// Z-score for confidence level (normal distribution)
const zScore = getZScore(confidenceLevel);
return portfolioValue * (mean - zScore * stdDev);
}
/**
* Calculate maximum drawdown
*/
export function maxDrawdown(equityCurve: number[]): number {
if (equityCurve.length < 2) return 0;
let maxDD = 0;
let peak = equityCurve[0];
for (let i = 1; i < equityCurve.length; i++) {
if (equityCurve[i] > peak) {
peak = equityCurve[i];
} else {
const drawdown = (peak - equityCurve[i]) / peak;
maxDD = Math.max(maxDD, drawdown);
}
}
return maxDD;
}
/**
* Calculate downside deviation
*/
export function downsideDeviation(returns: number[], targetReturn: number = 0): number {
if (returns.length === 0) return 0;
const downsideReturns = returns.filter(ret => ret < targetReturn);
if (downsideReturns.length === 0) return 0;
const sumSquaredDownside = downsideReturns.reduce(
(sum, ret) => sum + Math.pow(ret - targetReturn, 2),
0
);
return Math.sqrt(sumSquaredDownside / returns.length);
}
/**
* Calculate Sharpe ratio
*/
export function sharpeRatio(returns: number[], riskFreeRate: number = 0): number {
if (returns.length < 2) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
return (mean - riskFreeRate) / stdDev;
}
/**
* Calculate beta coefficient
*/
export function beta(portfolioReturns: number[], marketReturns: number[]): number {
if (portfolioReturns.length !== marketReturns.length || portfolioReturns.length < 2) {
return 0;
}
const n = portfolioReturns.length;
const portfolioMean = portfolioReturns.reduce((sum, ret) => sum + ret, 0) / n;
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / n;
let covariance = 0;
let marketVariance = 0;
for (let i = 0; i < n; i++) {
const portfolioDiff = portfolioReturns[i] - portfolioMean;
const marketDiff = marketReturns[i] - marketMean;
covariance += portfolioDiff * marketDiff;
marketVariance += marketDiff * marketDiff;
}
return marketVariance === 0 ? 0 : covariance / marketVariance;
}
/**
* Calculate alpha
*/
export function alpha(
portfolioReturns: number[],
marketReturns: number[],
riskFreeRate: number = 0
): number {
const portfolioMean =
portfolioReturns.reduce((sum, ret) => sum + ret, 0) / portfolioReturns.length;
const marketMean = marketReturns.reduce((sum, ret) => sum + ret, 0) / marketReturns.length;
const portfolioBeta = beta(portfolioReturns, marketReturns);
return portfolioMean - (riskFreeRate + portfolioBeta * (marketMean - riskFreeRate));
}
/**
* Calculate tracking error
*/
export function trackingError(portfolioReturns: number[], benchmarkReturns: number[]): number {
if (portfolioReturns.length !== benchmarkReturns.length || portfolioReturns.length === 0) {
return 0;
}
const activeReturns = portfolioReturns.map((ret, i) => ret - benchmarkReturns[i]);
const mean = activeReturns.reduce((sum, ret) => sum + ret, 0) / activeReturns.length;
const variance =
activeReturns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) /
(activeReturns.length - 1);
return Math.sqrt(variance);
}
/**
* Calculate volatility (standard deviation of returns)
*/
export function volatility(returns: number[]): number {
if (returns.length < 2) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance =
returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / (returns.length - 1);
return Math.sqrt(variance);
}
/**
* Calculate annualized volatility
*/
export function annualizedVolatility(returns: number[], periodsPerYear: number = 252): number {
return volatility(returns) * Math.sqrt(periodsPerYear);
}
/**
* Calculate skewness (measure of asymmetry)
*/
export function skewness(returns: number[]): number {
if (returns.length < 3) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
const skew =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 3), 0) / returns.length;
return skew;
}
/**
* Calculate kurtosis (measure of tail heaviness)
*/
export function kurtosis(returns: number[]): number {
if (returns.length < 4) return 0;
const mean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) => sum + Math.pow(ret - mean, 2), 0) / returns.length;
const stdDev = Math.sqrt(variance);
if (stdDev === 0) return 0;
const kurt =
returns.reduce((sum, ret) => sum + Math.pow((ret - mean) / stdDev, 4), 0) / returns.length;
return kurt - 3; // Excess kurtosis (subtract 3 for normal distribution baseline)
}
/**
* Calculate comprehensive risk metrics
*/
export function calculateRiskMetrics(
returns: number[],
equityCurve: number[],
marketReturns?: number[],
riskFreeRate: number = 0
): RiskMetrics {
if (returns.length === 0) {
return {
var95: 0,
var99: 0,
cvar95: 0,
maxDrawdown: 0,
volatility: 0,
downside_deviation: 0,
calmar_ratio: 0,
sortino_ratio: 0,
beta: 0,
alpha: 0,
sharpeRatio: 0,
treynorRatio: 0,
trackingError: 0,
informationRatio: 0,
};
}
const portfolioVolatility = volatility(returns);
const portfolioMean = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
// Calculate VaR
const var95Value = valueAtRisk(returns, 0.95);
const var99Value = valueAtRisk(returns, 0.99);
const cvar95Value = conditionalValueAtRisk(returns, 0.95);
// Calculate max drawdown
const maxDD = maxDrawdown(equityCurve);
// Calculate downside deviation
const downsideDeviationValue = downsideDeviation(returns);
// Calculate ratios
const calmarRatio = maxDD > 0 ? portfolioMean / maxDD : 0;
const sortinoRatio =
downsideDeviationValue > 0 ? (portfolioMean - riskFreeRate) / downsideDeviationValue : 0;
const sharpeRatio =
portfolioVolatility > 0 ? (portfolioMean - riskFreeRate) / portfolioVolatility : 0;
let portfolioBeta = 0;
let portfolioAlpha = 0;
let portfolioTreynorRatio = 0;
let portfolioTrackingError = 0;
let informationRatio = 0;
if (marketReturns && marketReturns.length === returns.length) {
portfolioBeta = beta(returns, marketReturns);
portfolioAlpha = alpha(returns, marketReturns, riskFreeRate);
portfolioTreynorRatio = treynorRatio(returns, marketReturns, riskFreeRate);
portfolioTrackingError = trackingError(returns, marketReturns);
informationRatio = portfolioTrackingError > 0 ? portfolioAlpha / portfolioTrackingError : 0;
}
return {
var95: var95Value,
var99: var99Value,
cvar95: cvar95Value,
maxDrawdown: maxDD,
volatility: portfolioVolatility,
downside_deviation: downsideDeviationValue,
calmar_ratio: calmarRatio,
sortino_ratio: sortinoRatio,
beta: portfolioBeta,
alpha: portfolioAlpha,
sharpeRatio,
treynorRatio: portfolioTreynorRatio,
trackingError: portfolioTrackingError,
informationRatio,
};
}
/**
* Helper function to get Z-score for confidence level
* This implementation handles arbitrary confidence levels
*/
function getZScore(confidenceLevel: number): number {
// First check our lookup table for common values (more precise)
const zScores: { [key: string]: number } = {
'0.90': 1.282,
'0.95': 1.645,
'0.975': 1.96,
'0.99': 2.326,
'0.995': 2.576,
};
const key = confidenceLevel.toString();
if (zScores[key]) return zScores[key];
// For arbitrary confidence levels, use approximation
if (confidenceLevel < 0.5) return -getZScore(1 - confidenceLevel);
if (confidenceLevel >= 0.999) return 3.09; // Cap at 99.9% for numerical stability
// Approximation of inverse normal CDF
const y = Math.sqrt(-2.0 * Math.log(1.0 - confidenceLevel));
return (
y -
(2.515517 + 0.802853 * y + 0.010328 * y * y) /
(1.0 + 1.432788 * y + 0.189269 * y * y + 0.001308 * y * y * y)
);
}
/**
* Calculate portfolio risk contribution
*/
export function riskContribution(
weights: number[],
covarianceMatrix: number[][],
portfolioVolatility: number
): number[] {
const n = weights.length;
const contributions: number[] = [];
for (let i = 0; i < n; i++) {
let marginalContribution = 0;
for (let j = 0; j < n; j++) {
marginalContribution += weights[j] * covarianceMatrix[i][j];
}
const contribution = (weights[i] * marginalContribution) / Math.pow(portfolioVolatility, 2);
contributions.push(contribution);
}
return contributions;
}
/**
* Calculate Ulcer Index
*/
export function ulcerIndex(equityCurve: Array<{ value: number; date: Date }>): number {
let sumSquaredDrawdown = 0;
let peak = equityCurve[0].value;
for (const point of equityCurve) {
peak = Math.max(peak, point.value);
const drawdownPercent = ((peak - point.value) / peak) * 100;
sumSquaredDrawdown += drawdownPercent * drawdownPercent;
}
return Math.sqrt(sumSquaredDrawdown / equityCurve.length);
}
/**
* Calculate risk-adjusted return (RAR)
*/
export function riskAdjustedReturn(
portfolioReturn: number,
portfolioRisk: number,
riskFreeRate: number = 0
): number {
if (portfolioRisk === 0) return 0;
return (portfolioReturn - riskFreeRate) / portfolioRisk;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,55 +1,55 @@
/**
* Date and time utilities for working with market data
*/
export const dateUtils = {
/**
* Check if a date is a trading day (Monday-Friday, non-holiday)
* This is a simplified implementation - a real version would check market holidays
*/
isTradingDay(date: Date): boolean {
const day = date.getDay();
return day > 0 && day < 6; // Mon-Fri
},
/**
* Get the next trading day from a given date
*/
getNextTradingDay(date: Date): Date {
const nextDay = new Date(date);
nextDay.setDate(nextDay.getDate() + 1);
while (!this.isTradingDay(nextDay)) {
nextDay.setDate(nextDay.getDate() + 1);
}
return nextDay;
},
/**
* Get the previous trading day from a given date
*/
getPreviousTradingDay(date: Date): Date {
const prevDay = new Date(date);
prevDay.setDate(prevDay.getDate() - 1);
while (!this.isTradingDay(prevDay)) {
prevDay.setDate(prevDay.getDate() - 1);
}
return prevDay;
},
/**
* Format a date as YYYY-MM-DD
*/
formatDate(date: Date): string {
return date.toISOString().split('T')[0];
},
/**
* Parse a date string in YYYY-MM-DD format
*/
parseDate(dateStr: string): Date {
return new Date(dateStr);
}
};
/**
* Date and time utilities for working with market data
*/
export const dateUtils = {
/**
* Check if a date is a trading day (Monday-Friday, non-holiday)
* This is a simplified implementation - a real version would check market holidays
*/
isTradingDay(date: Date): boolean {
const day = date.getDay();
return day > 0 && day < 6; // Mon-Fri
},
/**
* Get the next trading day from a given date
*/
getNextTradingDay(date: Date): Date {
const nextDay = new Date(date);
nextDay.setDate(nextDay.getDate() + 1);
while (!this.isTradingDay(nextDay)) {
nextDay.setDate(nextDay.getDate() + 1);
}
return nextDay;
},
/**
* Get the previous trading day from a given date
*/
getPreviousTradingDay(date: Date): Date {
const prevDay = new Date(date);
prevDay.setDate(prevDay.getDate() - 1);
while (!this.isTradingDay(prevDay)) {
prevDay.setDate(prevDay.getDate() - 1);
}
return prevDay;
},
/**
* Format a date as YYYY-MM-DD
*/
formatDate(date: Date): string {
return date.toISOString().split('T')[0];
},
/**
* Parse a date string in YYYY-MM-DD format
*/
parseDate(dateStr: string): Date {
return new Date(dateStr);
},
};

View file

@ -1,2 +1,2 @@
export * from './dateUtils';
export * from './calculations/index';
export * from './dateUtils';
export * from './calculations/index';