[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Question Regarding Mathematical Operations in TS



PureBytes Links

Trading Reference Links

At 7:48 AM -0700 12/27/01, jedaz wrote:

>I appears that TS truncates, or perhaps rounds values prior to performing
>mathematical operations - which is scary. I must be missing something simple
>here. I have looked in the TS2000i online manual and can find nothing
>regarding precision regarding TS mathematical operations.

I did a study of TradeStation precision last summer and am attaching
the summary below. The print routines do truncate results to less
precision than the internal accuracy for some reason. You can find
the internal values by printing the integer and fractional parts
separately.

Bob Fulks

-----

INTRODUCTION

This message will summarize my experiments with TradeStation to
determine the precision of arithmetic operations, illustrates what
errors it may cause, and how you might "work-around" these errors.
Please notify me if you spot any mistakes in this as I would like it
to be as accurate as possible.


NUMERIC VARIABLES

Numeric variables appear to be single-precision floats using the IEEE
standard. This format in described at:

    <http://www.psc.edu/general/software/packages/ieee/ieee.html>

All variables are stored in this format. Conceptually it is easier to
think of the limits in terms of integers and non-integers, (even
though all numbers are stored in the floating point format).

INTEGER ARITHMETIC

The format consists of a mantissa of 23 bits plus one implied bit for
a total of 24 bits, allowing it to represent integers in the range of:

     +-2^24 = +-16,777,216

This limit is important in cases where you might be counting. For
example, in the following loop:

        for j = 1 to Limit begin
           k = k + 1;
        end;

if "Limit" exceeds +16,777,215 then the loop will not terminate since
the increment by 1 will then be below the least significant digit.

The same thing applies to the limit on counters:

    Count = Count + 1;

This limit is high enough so that it is unlikely to cause any
problems in normal use.


LARGER FLOATING POINT NUMBERS

Numbers higher than +16,777,216 such as +16,777,220 would be
represented conceptually as:

     8,388,610 * 2^1

This would work up to 33,554,432 which would be represented
conceptually as:

    16,777,216 * 2^1

And continuing up to 67,108,864 which would be represented
conceptually as:

    16,777,216 * 2^2

at which point the least significant digit changes by 2^3 = 8 for the
next value. This continues up to very large numbers.

So the poorest resolution would occur when the representation is near
the 8,388,610 end of the range and would be about 1.2 decimal digits
of error in 10^7, decreasing to about 0.6 decimal digits of error in
10^7 at the 16,777,216 end of the range.

So we can approximate the error as 1 part in 10^7.


SUBTRACTING TWO NEAR-EQUAL NUMBERS

This becomes an issue in code that subtracts nearly equal numbers such as:

    1,234,623.890 - 1,234,567.803

The numbers are only accurate to about:

    1,234,623.9 and 1,234,567.8,

respectively, so the difference:

    56.1 could be from about 56.0 to 56.2

There are a lot of functions that subtract two prices to find a small
difference between the two. For example, The Dow Jones Industrial
Average (DJIA) is at about 10300 so

    10323.45 - 10321.20 = 2.25

is accurate to about 3 digits which is probably OK. But many
functions subtract the square of price or Price1*Price2 and these
can give very inaccurate results.

The usual way to avoid these errors is to offset all prices in the
calculation by a constant. So instead of using the DJIA directly, you
might subtract 10000 from all prices used in the calculation and
re-scale the result at the end of the calculation by adding back the
10000. This can keep the numbers small enough to avoid these errors.

In many cases there are several different algorithms for calculating
the same function and some may preserve the accuracy much better than
others.


LARGEST POSSIBLE NUMBER

The largest number that can be represented by the floating point
format is about 3.4 * 10^38, which is a very large number.
TradeStation 4.0 limits this to 2.147483 * 10^15 at which point some
of the built-in routines stop working correctly. TS2000i has a higher
limit. (This limit was not tested on all routines and the true limit
may be much less than this.) But, in any case, the limit is large
enough so it will not cause a problem in most cases.

The PowerEditor will let you type in a number as large as
100,000,000,000,000,008 which would be stored as 1.000000 * 10^17.


SMALLER NUMBERS

All numbers use the same floating point representation so are
accurate to about one part in 10^7. An example would be 0.3426749XXX
where the XXX digits are inaccurate.


ITERATIVE CALCULATIONS

The limited precision also becomes important in calculations that
carry a value from bar to bar, which is very common. Many
TradeStation functions do this since it is much faster than
recalculating everything on every bar. A simple example is the series
version of the "Average" function:

inputs: Price(NumericSeries), Length(NumericSimple) ;
vars  : Sum(0), Counter(0) ;

if currentbar = 1
then begin
    Sum = 0;
    for counter = 0 to Length - 1
    begin
       Sum = Sum + Price[counter];
    end;
end
else
    Sum = Sum[1] + Price - Price[Length];
if Length > 0 then
    Average = Sum / Length
else
    Average = 0;


In this code, the complete accurate calculation is done at CurrentBar
= 1. On each subsequent bar, only the changes are calculated. Since
the error on each calculation can be can be one or two parts in 10^7,
after 1000 bars the error might be one part in 10^4. For the DJIA,
this would be an error of one point out of 10000. This doesn't sound
like much. But if your trading system bought when the price crosses
over the Average, you could get very different results based upon a
one-point error.

There is a very simple fix for this. Simply replace:

   if currentbar = 1

with

   if Mod(currentbar, 100) = 1

This causes the code to do the complete, accurate calculation every
100 bars, which prevents the error from accumulating. The added
calculation would add less than 1% to the calculation time.

This solution works on any most function that carries a value from bar to bar.

One exception is the RSI function. This indicator was developed
"before computers" so uses a different calculation for the initial
bars than for the subsequent bars - to simplify what was then a
manual calculation. TradeStation retained the original definition so
if you modify it to redo the initial calculation every 100 bars, you
will get different values.


DIVIDE-BY-ZERO AND DIVIDE-BY-SMALL-NUMBER ERRORS

In TradeStation 4.0 and 2000i, Omega advises us to add a test for
"Divide-by-zero" errors:

   if Denom <> 0 then Result = Numer / Denom;

I have been told that they removed these checks for the function
library in TS Pro and as a result you get random "Divide-by-zero"
errors in code that worked in TS4.0 and TS2000i. (It is hard to
believe that they could do this but who knows...)

The above expression allows the calculation if "Denom" is not zero.
If "Denom" is zero, it does not allow the calculation so in that
case, "Result" will retain the value from the previous bar. That is
usually OK but you may want to do something else in such cases. I
have seen some plots that do not get updated for many bars because
the "Divide-by-zero" test prevented them from being updated.

In some cases the denominator will not be zero but will be very
small, resulting in a very large incorrect result. This would occur
for cases where the correct denominator is zero but, because of the
round-off errors, is some very small value.

In many such case both the denominator and the numerator might very,
very small or zero and the result actually has a value. (Result = 0/0)

There is a method (called "L'Hopital's Rule") to determine the value:

    if AbsValue(Denom) > Limit
       then Result = Numer / Denom
       else Result = dNumer / dDenom;

where dNumer and dDenom are the first derivative of the equations for
the numerator and denominator, respectively. "Limit" would be set to
some appropriate small value. (To use this trick you need an
understanding of differential calculus.)


SUMMARY

The use single-precision floating point was probably a good design
decision when memory was expensive and TradeStation 4.0 was limited
to the 64K program size. Unfortunately, Omega did not write their
function library so that it produced accurate results with the
available arithmetic so the users must be alert for and "work-around"
such errors.

Now days, memory is very much less expensive so most modern programs
use double-precision floating point arithmetic. Unfortunately Omega
retained the single-precision arithmetic in TS2000i so we still have
to worry about these issues.

Bob Fulks
July, 2001