A JavaScript library to track and measure stock market portfolios performances.





GitHub Stars



Last Commit

4yrs ago






Size (min+gzip)




Type Definitions





| French version

PortfolioAnalytics v0.0.4 (Changelog)

Travis Build Status

In order to track my personal stock market investments performances, as well as to analyse trading strategies on my blog Le Quant 40, I wanted to use portfolio performances measures computed in JavaScript.

Why in JavaScript ? Because I am a fan of Google Sheets, which is easily extensible thanks to Google Apps Script, a JavaScript-based language.

After several fruitless hours of Googling (incomplete codes, incorrect codes, undocumented codes...), I decided to create my own JavaScript library of such portfolio performances measures, hoping that it could be useful to other people...


  • Compatible with Google Sheets
  • Compatible with any browser supporting ECMAScript 5 (i.e., front-end development)
  • Compatible with Node.js (i.e., back-end development)
  • (Performances) Automatically uses JavaScript Typed Arrays
  • (Accuracy) Internally uses accurate numerical algorithms (e.g., corrected two pass algorithms for mean, variance, skewness and kurtosis, accurate algorithm for error function...)
  • Code continuously tested and integrated by Travis CI
  • Code heavily documented using JSDoc


Usage in Google Sheets

If you would like to use PortfolioAnalytics in Google Sheets, you can either:


  • Import the JavaScript files from the dist/gs directory into your spreadsheet script

In both cases, providing data to the PortfolioAnalytics functions is then accomplished your preferred way:

  • Using a wrapper function in your spreadsheet script, directly accessible from your spreadsheet, to which you can provide a standard data range (A1:A99...), e.g.:
function computeUlcerIndexWrapper(iEquityCurveRange) {
  // Convert the input range coming from the spreadsheet into an array
  var aInternalArray = [];
  for (var i=0; i<iEquityCurveRange.length; ++i) {
  // Compute the index
  var ulcerIndex = PortfolioAnalytics.ulcerIndex(aInternalArray);
  // Return it to the spreadsheet
  return ulcerIndex;
  • Using pure Google Apps Script functions - typically the getRange(...) familly of functions -, optimized for speed, e.g.:
function computeUlcerIndex() {
  // Adapted from
 var ss = SpreadsheetApp.getActiveSpreadsheet();
 var sheet = ss.getSheets()[0];
 var range = sheet.getRange(1, 1, 100); // A1:A100
 var values = range.getValues();

 // Convert the above range into an array
 var aInternalArray = [];
 for (var row in values) {
   for (var col in values[row]) {
  // Compute the index
  var ulcerIndex = PortfolioAnalytics.ulcerIndex(aInternalArray);
  // Do something with it (use it in a computation, write it back to the spreadsheet, etc.)

You can find examples of PortfolioAnalytics usage in this spreadsheet.

Usage inside a browser

If you would like to use PortfolioAnalytics inside a browser you can download its source code and/or its minified source code.

You then just need to include this code in an HTML page, e.g.:

<script src="portfolio_analytics.dist.min.js" type="text/javascript"></script>

To be noted that if the browser is compatible with JavaScript Typed Arrays, you can provide such arrays in input to PortfolioAnalytics for better performances, e.g.:

PortfolioAnalytics.arithmeticReturns(new Float64Array([100.0, 109.75, 111.25]))
// Will output a Float64Array

Usage with Node.js

If you would like to use PortfolioAnalytics with Node.js, you simply need to declare it as a dependency of your project in your package.json file.

Then, this is standard Node.js code, e.g.:

var PortfolioAnalytics = require('portfolio-analytics');
var ui = PortfolioAnalytics.ulcerIndex([100, 110, 105, 102, 95]);
// ui == 0.07204222820421435


PortfolioAnalytics.maxDrawdown([1, 2, 1]); 
// The maximum drawdown

PortfolioAnalytics.drawdownFunction([1, 2, 1]); 
// The drawdown function

PortfolioAnalytics.topDrawdowns([1, 2, 1], 1); 
// The top 'n' drawdowns (second largest drawdown, etc.) with their start/end indexes

PortfolioAnalytics.ulcerIndex([1, 2, 1]);
// The Ulcer Index

PortfolioAnalytics.painIndex([1, 2, 1]);
// The Pain Index, also corresponding to the average of the drawdown function

PortfolioAnalytics.conditionalDrawdown([100, 90, 80], 0.5);
// The conditional drawdown
PortfolioAnalytics.cumulativeReturn([1, 2, 1]); 
// The cumulative return from first to last period

PortfolioAnalytics.cagr([1, 2, 1], [new Date("2015-12-31"), new Date("2016-12-31"), new Date("2017-12-31")]); 
// The compound annual growth rate (CAGR) from first to last date

PortfolioAnalytics.arithmeticReturns([1, 2, 1]); 
// The arithmetic returns for all periods

PortfolioAnalytics.valueAtRisk([1, 2, 1], 0.7);
// The (percent) value at risk
PortfolioAnalytics.sharpeRatio([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100]); 
// The Sharpe ratio

PortfolioAnalytics.biasAdjustedSharpeRatio([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100]); 
// The Sharpe ratio adjusted for its bias

PortfolioAnalytics.doubleSharpeRatio([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100]); 
// The double Sharpe ratio (i.e., the Sharpe ratio, adjusted for its estimation risk)

PortfolioAnalytics.sharpeRatioConfidenceInterval([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100], 0.05); 
// The confidence interval for the Sharpe ratio (here, at 5% significance level)

PortfolioAnalytics.probabilisticSharpeRatio([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100], 0); 
// The probabilistic Sharpe ratio (i.e., the probability that the Sharpe ratio is greater 
// than a reference Sharpe ratio, here 0)

PortfolioAnalytics.minimumTrackRecordLength([100, 110, 105, 107.5, 115], [100, 100, 100, 100, 100], 0.05, 0); 
// The minimum track record length (i.e., the minimal length of the track record of the performance 
// to have statistical confidence, here at 95%, that the Sharpe ratio is greater than a reference Sharpe ratio, here 0)
PortfolioAnalytics.gainToPainRatio([1, 2, 1]); 
// The gain to pain ratio

How to contribute ?

Fork the projet from Github...

Instal the Grunt dependencies

npm install



  • The following command generates the files to be used inside a browser or with Node.js in the dist directory:
grunt deliver
  • The following command generates the files to be used in Google Sheets in the dist\gs directory:
grunt deliver-gs


Any of the following two commands run the QUnit unit tests contained in the test directory on the generated file dist\

npm test
grunt test

Submit a pull-request...


MIT License

Rate & Review

Great Documentation0
Easy to Use0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Unwelcoming Community0
No reviews found
Be the first to rate


No alternatives found


No tutorials found
Add a tutorial