0.0.4 • Public • Published

    | 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 https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangerow-column-numrows
     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


    Drawdowns related measures

    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

    Returns related measures

    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

    Sharpe ratio related measures

    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)

    Returns to variability related measures

    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\portfolio_analytics.dev.min.js:

    npm test
    grunt test

    Submit a pull-request...


    MIT License


    npm i portfolio-analytics

    DownloadsWeekly Downloads






    Last publish


    • lequant40