UNPKG

quantitivecalc

Version:

A TypeScript library providing advanced quantitative finance functions for risk analysis, performance metrics, and technical indicators. (Currently in development)

55 lines (54 loc) 2.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateDrawdown = calculateDrawdown; /** * Calculates the current drawdown for each point in a time series of values. * * The current drawdown represents the percentage decline from the previous peak at each point * in time. This is particularly useful for creating underwater charts that visualize how far * below the high-water mark an investment is at any given time. When a new peak is reached, * the drawdown becomes 0. * * @param data - An array of objects representing the time series data. * @param sourceColumn - The key in each object from which to read the numeric value for drawdown calculation. * @param resultColumn - The key in which to store the calculated drawdown for each row (default: 'drawdown'). * @param asPercentage - Whether to return the drawdown as a percentage (default: true). If false, returns as decimal. * @returns A new array of objects, each including the calculated current drawdown in the specified result column. * * @remarks * - If the value in `sourceColumn` is not a valid number, the previous row's drawdown value is used. * - Current drawdown is calculated as `(value - peak) / peak`, where `peak` is the highest value observed so far. * - Drawdown values are always <= 0 (exactly 0 at peaks, negative during declines). * - If the input data is empty or undefined, an empty array is returned. * - The first valid data point is considered the initial peak with 0 drawdown. * - This creates an "underwater chart" - the line stays at 0 at peaks and goes negative during drawdowns. */ function calculateDrawdown(data, sourceColumn, resultColumn = 'drawdown', asPercentage = true) { if (!data || data.length === 0) { return []; } // Create a copy of the data to avoid mutating the original const result = data.map(row => ({ ...row })); let peak = -Infinity; for (let i = 0; i < result.length; i++) { const value = result[i][sourceColumn]; if (typeof value === 'number' && !isNaN(value)) { // Update peak if current value is higher if (value > peak) { peak = value; } // Calculate current drawdown from peak // Should always be <= 0 (negative or zero at peak) const currentDrawdown = peak > 0 ? (value - peak) / peak : 0; // Convert to percentage if requested const finalDrawdown = asPercentage ? currentDrawdown * 100 : currentDrawdown; // Ensure drawdown is never positive (safety check) result[i][resultColumn] = Math.min(0, finalDrawdown); } else { // Invalid data - use previous value or 0 for first row result[i][resultColumn] = i > 0 ? result[i - 1][resultColumn] : 0; } } return result; }