Computing Percentages

From TechWiki

One would think that computing percentages is very trivial and perhaps even a little boring. However, some of the calculations can be quite subtle and lead to confusion/errors if your not careful...

TOC

Contents

The Problem

Percentage calculations depend on the choice of the base value. I.e., a 10 unit move can correspond to any possible percentage value, depending on the choice reference value. There are four possible conventions for choosing the base price:

  1. Use the previous price (PRE)
  2. Use the current price (CUR)
  3. Always use the maximal price (MAX), i.e., for an up move the current price, for a down move the previous price
  4. Always use the minimal price (MIN)

As an example, a price move by 10 units from 100 to 110 corresponds to a

  1. 10\% PRE
  2. 9.\overline{09}\% CUR
  3. 9.\overline{09}\% MAX
  4. 10\% MIN

and a price move by 10 units from 100 to 90 corresponds to a

  1. 10\% PRE
  2. 11.\overline{1}\% CUR
  3. 10\% MAX
  4. 11.\overline{1}\% MIN

This reference-point dependence also impacts the commutativity of the percentage operator. E.g., starting from 100 units, a 10% move up followed by a 10% move down can be interpreted as follows

  1. 100 \to 110 \to 99 PRE
  2. 100 \to 111.\overline{1} \to 101.\overline{01}CUR
  3. 100 \to 111.\overline{1} \to 100 MAX
  4. 100 \to 110 \to 100 MIN

So the more unintuitive definition of percentages using MIN or MAX ensures the commutativity of the mathematical operation.

The Solution: Log Percentages

How to come to terms with all these alternative possibilities? To escape from relative reference-points, it is advisable to move to log-space. Now fixed thresholds in log-space correspond to percentage values independent of any reference point definition.

As an example, the log-transformed PRE variant looks like:

Image:LogPercentages.png

To summarize, there are two thresholds in log-space (dUp, dDown) and four percentages (\pm px, py1, py2).

It becomes now clear that the apparent ambiguity in selecting the reference point comes from the two log-space thresholds dUp, dDown, or more generically dSmall, dBig:

  1. PRE \Longleftrightarrow using dSmall for up moves and dBig for down moves
  2. CUR \Longleftrightarrow using dBig for up moves and dSmall for down moves
  3. MAX \Longleftrightarrow using dBig
  4. MIN \Longleftrightarrow using dSmall

From a mathematical point of view, MIN and MAX are more natural selections for percentage calculations, and indeed are the only definitions guaranteeing that an inversion x \to 1/x yields identical results.

The Equations

Approximately

|px| \approx 100 \cdot dSmall \approx 100 \cdot |dBig|.

CUR

To compute the current price in the CUR real-space percentage calculation, there are two equations.

For a down move

x_i = \frac{x_{i-1}}{\% +1}.

For an up move

x_i = \frac{x_{i-1}}{-\% +1}.


From Real-Space to Log-Space

dSmall = \ln \left( 1 + \frac{px}{100} \right)  = -\ln \left( 1 - \frac{py_1}{100} \right)
dBig = \ln \left( 1 - \frac{|px|}{100} \right)  = -\ln \left( 1 + \frac{|py_2|}{100} \right)


From Log-Space to Real-Space

And vice versa:

px = \left( \exp(dSmall) - 1 \right) \cdot 100
-|px| = \left( \exp(dBig) - 1 \right) \cdot 100
py_1 = \left( -\exp(-dSmall) + 1 \right) \cdot  100
py_2 = \left( -\exp(-dBig) + 1 \right) \cdot 100


Algorithm

To compute the small/big thresholds

  for (int i = 0; i < length; i++) {
     thresholdsUp[i] = lnThrUp(cfgThresh[i]);
     thresholdsDown[i] = lnThrDown(cfgThresh[i]);
  }

from an array of percentages cfgThresh[i] (> 0) the mapping to fixed price moves in log-space is given by

  public static final double lnThrUp(double thr) {
     return Math.log(1 + thr / 100.0);
  }
  public static final double lnThrDown(double thr) {
     return -Math.log(1 - thr / 100.0);
  }

Note that all threshold values are positive.