Ever since we published this set of tutorials, we have been receiving questions from fellow thinkScripters demanding more in-depth info on certain constructions in our programming language. This was a pleasure to know that our users look though the scripts of pre-defined studies in order to employ more advanced constructions, which, of course, made them wonder what certain things were. In this article, we're going to discuss a subject that was briefly mentioned in chapter 2: the
Double type constants.
What you already know is that
Double.Pi stand for mathematical numbers e and pi, respectively. The most curious, though, have noticed that there are three other
Double constants in thinkScript that are used everywhere throughout studies:
Double.NEGATIVE_INFINITY. While they are obviously harder to get and their purpose might seem obscure, they're not really secret combos; let's see what they are and how you may use them in your scripts.
First of all, NaN here stands for "not a number". Basically speaking, this constant represents a value that cannot be plotted because it doesn't exist for some reason. Several examples of when it happens:
1. OHLC values of the current symbol are NaN when a script tries to retrieve them from the time point before IPO or before the first visible bar on tick and range charts. The same happens when the script tries to retrieve OHLC values from the right expansion of the chart (i.e., from the time point in the "future", which doesn't have any OHLC values yet).
2. OHLC values of the secondary symbol are NaN when there is a gap in the chart data. Consider the following script:
plot price = close ("SPX");
On an intraday /ES chart, values of price are NaN when there are no quotes for SPX.
3. Results of errors, where the expression has no mathematical sense, e.g.:
Not being a number,
Double.NaN is still a constant, so it's possible to use it in some expressions. However, its usage is somewhat specific:
1. When you use a
Double.NaN in an if-expression, the result is also NaN, no matter what is stated in the then-else branches. For example, the following script (which could have been supposed to check for "not-a-number" situations on chart):
plot Data = if close == Double.NaN then 1 else 0;
will not plot anything because its output is always NaN and thus cannot be plotted. In order to check that close is NaN, the
isNan() function can be used:
plot Data = if isNaN(close) then 1 else 0;
2. NaN plus any number is not a number. This is important for recursive variables: if at some point of recursion the variable becomes NaN, all its further values will be NaN as well. In order to avoid that, use the
isNan() function can help you fill chart gaps with certain values. Consider the following script:
input symbol = "IBM";
def closeSymbol = close(symbol);
def closeSymbolWithOutNaN = CompoundValue(1, if IsNaN(closeSymbol) then closeSymbolWithOutNan else closeSymbol, closeSymbol);
plot Data = closeSymbolWithOutNaN;
This script plots the close price of IBM without gaps. Any possible gap in the plot is replaced with the last known value of close before it.
Checking for gaps or not existing values is not the only application of the NaN concept. It may also be applied in scripts where you need to plot something for certain bars and nothing for others. Consider the following script:
def isLastThreeBars = IsNaN(close[-3]) and !IsNaN(close[-2]);
def isRightExpansion = IsNaN(close);
def lastClose = CompoundValue(1, if isLastThreeBars then close[-2] else lastClose, Double.NaN);
plot Data = if !isRightExpansion then lastClose else Double.NaN;
This script detects three last bars by checking that there's nothing after them. It plots the last bar's close price across these three bars and nothing for other bars and the right expansion.
Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY
These two constants were added for those who can't imagine scripting without dividing by zero. The
Double.POSITIVE_INFINITY is the thinkScript representation of dividing any positive number by zero and the
Double.NEGATIVE_INFINITY is its negative counterpart. They represent respectively the positive and the negative values of infinitely large magnitude.
While the whole concept of infinite values might seem esoteric, they can be successfully employed in your scripts. Consider the following examples.
Here is a sophisticated way of plotting the smallest non-zero high-to-low range on chart:
def range = high - low;
plot SmallestRange = LowestAll(if range == 0 then Double.POSITIVE_INFINITY else range);
This plot replaces zero range values with infinitely large, which makes it possible to detect the smallest value.
In chapter 9, we discussed a function called
AddCloud(); this function visualizes the difference between two calculated values (variables or plots) by filling the area between them with a translucent color. Infinity bounds though may trick it into displaying something more impressive:
def hiLevel = if close >= Average(close) then Double.POSITIVE_INFINITY else Double.NEGATIVE_INFINITY;
AddCloud(hiLevel, -hiLevel, Color.LIGHT_GREEN, Color.LIGHT_RED);
Assigning infinite values to a variable will have the
addCloud() function display infinite stripes and paint them corresponding to a rule. In our case, the stripes where the close price is greater than or equal to its average will be painted green and the others will be painted red. The chart will not be re-scaled because infinity values are not plotted.
Consider the script below:
input price = close;
def length = if close > close then 20 else 30;
plot VariableMax = fold i = 0 to length with m = Double.NEGATIVE_INFINITY do Max(m, getValue(price, i, 30));
This script finds the greatest of several recent values of price, with a non-constant number of values being processed. This approach was used in the implementation of the Semi-Cup Formation study.