quantitivecalc
Version:
A TypeScript library providing advanced quantitative finance functions for risk analysis, performance metrics, and technical indicators. (Currently in development)
61 lines (60 loc) • 3.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateSharpeRatio = calculateSharpeRatio;
/**
* Calculates the rolling Sharpe ratio for a given dataset.
*
* The Sharpe ratio is computed over a moving window of specified size, using the provided returns column.
* The result is stored in a new column for each row, with `null` for rows where there is insufficient data.
*
* @param data - Array of data objects containing return values.
* @param returnsColumn - The key in each data object representing the return value.
* @param resultColumn - The key to store the calculated Sharpe ratio in each data object.
* @param windowSize - The number of periods to use for the rolling window (default: 252, typical for daily data over one year).
* @param riskFreeRate - The annual risk-free rate to use in the calculation (default: 0.02, or 2%).
* @returns A new array of data objects with the Sharpe ratio added to each row under `resultColumn`.
*
* @remarks
* - The Sharpe ratio is annualized by multiplying by `sqrt(252)`.
* - If there is insufficient data in the window, the result is `null` for that row.
* - Assumes daily data; adjust `windowSize` and risk-free rate conversion for other frequencies.
*/
function calculateSharpeRatio(data, returnsColumn, resultColumn, windowSize = 252, // 1 year for daily data
riskFreeRate = 0.02) {
if (!data || data.length === 0) {
return [];
}
const result = data.map(row => ({ ...row }));
const dailyRiskFreeRate = riskFreeRate / 252; // Convert annual to daily
for (let i = 0; i < result.length; i++) {
if (i < windowSize - 1) {
result[i][resultColumn] = null;
}
else {
// Get returns 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);
}
}
if (windowReturns.length > 1) {
// Calculate excess returns (returns - risk-free rate)
const excessReturns = windowReturns.map(r => r - dailyRiskFreeRate);
// Calculate mean and standard deviation of excess returns
const meanExcessReturn = excessReturns.reduce((sum, val) => sum + val, 0) / excessReturns.length;
const variance = excessReturns.reduce((sum, val) => sum + Math.pow(val - meanExcessReturn, 2), 0) / (excessReturns.length - 1);
const stdDev = Math.sqrt(variance);
// Sharpe ratio = mean excess return / standard deviation
// Annualize by multiplying by sqrt(252)
const sharpeRatio = stdDev > 0 ? (meanExcessReturn / stdDev) * Math.sqrt(252) : 0;
result[i][resultColumn] = sharpeRatio;
}
else {
result[i][resultColumn] = null;
}
}
}
return result;
}