quantitivecalc
Version:
A TypeScript library providing advanced quantitative finance functions for risk analysis, performance metrics, and technical indicators. (Currently in development)
68 lines (67 loc) • 3.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateCalmarRatio = calculateCalmarRatio;
/**
* Calculates the Calmar Ratio for a rolling window over a dataset.
*
* The Calmar Ratio is defined as the annualized return divided by the maximum drawdown
* over a specified window. This function computes the ratio for each row in the data,
* using the specified columns for returns and prices, and stores the result in a new column.
*
* @param data - Array of data objects containing price and return information.
* @param returnsColumn - The key in each data object representing the periodic return value.
* @param priceColumn - The key in each data object representing the price value.
* @param resultColumn - The key to store the calculated Calmar Ratio in each data object.
* @param windowSize - The number of periods to use for the rolling window (default is 252).
* @returns A new array of data objects with the Calmar Ratio added to each row in the specified result column.
*
* @remarks
* - If there is insufficient data for the window, or if the maximum drawdown is zero, the result will be `null`.
* - Assumes daily data; annualization uses 252 trading days.
*/
function calculateCalmarRatio(data, returnsColumn, priceColumn, resultColumn, windowSize = 252) {
if (!data || data.length === 0) {
return [];
}
const result = data.map(row => ({ ...row }));
for (let i = 0; i < result.length; i++) {
if (i < windowSize - 1) {
result[i][resultColumn] = null;
}
else {
// Calculate annualized return for the window
const windowReturns = [];
for (let j = i - windowSize + 1; j <= i; j++) {
const returnValue = result[j][returnsColumn];
if (typeof returnValue === 'number' && !isNaN(returnValue)) {
windowReturns.push(returnValue);
}
}
// Calculate maximum drawdown for the window
let peak = -Infinity;
let maxDrawdown = 0;
for (let j = i - windowSize + 1; j <= i; j++) {
const price = result[j][priceColumn];
if (typeof price === 'number' && !isNaN(price)) {
if (price > peak)
peak = price;
const drawdown = (peak - price) / peak;
if (drawdown > maxDrawdown)
maxDrawdown = drawdown;
}
}
if (windowReturns.length > 0 && maxDrawdown > 0) {
// Annualized return
const avgReturn = windowReturns.reduce((sum, val) => sum + val, 0) / windowReturns.length;
const annualizedReturn = avgReturn * 252;
// Calmar Ratio = Annualized Return / Maximum Drawdown
const calmarRatio = annualizedReturn / maxDrawdown;
result[i][resultColumn] = calmarRatio;
}
else {
result[i][resultColumn] = null;
}
}
}
return result;
}