True Range (TR)

Function Syntax

=TR(data)  
  • data (array):
    Range of columns containing the date, Open, high, Low, close, volume data.

Returns:
A two-column array of dates and their corresponding TR values.

Google Sheets Output Example

Below is an example of the resulting array when applying the custom TR() function in Google Sheets

tr return array
TR Return Array

Spreadsheet

Apps Scripts Code

Here’s the full Google Apps Script code.
Paste this directly into the Google Sheets Apps Script editor (Extensions → Apps Script):

tr.gs
/**

 * Calculates the True Range (TR) values

 *

 * @param {array} data - an array where the first column is the  dates, and subsequent columns for  open, high, low, close and volume.  

 * @returns {Array<Array>} - A 2D array containing dates and their corresponding True Range values.

 *

 * @customfunction

 */

  

function TR(data) {

  // Preprocess data using getData

  const processedData = getData(data);

  // Extract columns from the processed data

  const dates = processedData.slice(1).map(row => row[0]);

  const highs = processedData.slice(1).map(row => row[2]);

  const lows = processedData.slice(1).map(row => row[3]);

  const closes = processedData.slice(1).map(row => row[4]);

  

  // Calculate True Range values

  let trueRanges = [["Date", "TR"]]; // Include headers

  for (let i = 1; i < highs.length; i++) {

    let tr = Math.max(highs[i], closes[i - 1]) - Math.min(lows[i], closes[i - 1]);

    trueRanges.push([dates[i], tr]);

  }

  

  return trueRanges;

}

  

/**

 *

 * @param {Array<Array>} data - A 2D array of cell values to process. The first row should contain headers.

 * @returns {Array<Array>} - A 2D array where the first row contains headers and subsequent rows contain processed data.

 *

 * @customfunction

 *

 */

  

function getData(data) {

  if (!Array.isArray(data) || data.length === 0) {

    throw new Error("Input data must be a non-empty array.");

  }

  
  

  // Check if the first row contains headers

  const headers = Array.isArray(data[0]) && data[0].every(item => typeof item === "string")

    ? data[0]

    : null;

  

  if (!headers) {

    // If no headers, assume the first column is the date column and process data

    return data.map(row => row.map((value, index) => {

      return index === 0 ? new Date(value) : parseFloat(value);

    }));

  }

  

  // Process rows with headers

  return [headers, ...data.slice(1).map(row => headers.map((header, index) => {

    const key = header.toLowerCase();

    return key === "date" ? new Date(row[index]) : parseFloat(row[index]) || row[index];

  }))];

}
Last updated on