Documentation, examples and further information of the ta4j project
This project is maintained by ta4j Organization
Ta4j supports different numeric backends for calculations in Indicator
and BarSeries
. You can choose an existing implementation or provide your own.
Current built-in implementations:
DecimalNum
(default): uses BigDecimal
for precisionDoubleNum
: uses primitive double
for performanceBoth implement Num
and are selected via a NumFactory
on your BarSeries
.
Take a look at the usage examples to see Num
and BaseBarSeries
in action.
Num
implementationThe main purpose of supporting different data types for arithmetic operations are opposing goals in technical analysis. On the one hand performance is a critical factor in high frequency trading or big data analysis, on the other hand crypto currencies and the general handling of monetary values require arithmetic that is not based on binary floating-point types like double
or float
. For example the following simple code fragment using double
will print an unexpected result:
System.out.println(1.0 - 9*0.1)
The output will be 0.0999999999999999998
witch is not equal to 0.1. This is no bug, but the result of trying to represent decimal values in a binary number system. It is not possible to represent 0.1 (or any other negative power of ten) exactly in double or float. With those data types you can only approximate such kind of decimal values. In many cases this representation may be sufficient and you would say “rounding to the last cent will give me the correct value”, but please note that .9999 trillion dollars is approximately equal to 1 trillion dollars. Could you please deposit the difference in my bank account?
The right way to solve this problem is to use BigDecimal
, int
or long
for monetary calculations. It doesn’t mean though that doubles can never be used for that purpose. Based of the fact that indicator just use monetary values as input but further calculations and results do not have a monetary dimension, Double’s 53 significant bits (~16 decimal digits) are usually good enough for things that merely require accuracy.
You have to know your application and you should study your goals and inform yourself about which kind of data type implementation works best for you.
DecimalNum
uses BigDecimal for high precision calculations. It is the default when creating a BaseBarSeries
.
// Default (DecimalNum)
BarSeries s1 = new BaseBarSeriesBuilder().withName("series").build();
// Explicit (DecimalNum)
BarSeries s2 = new BaseBarSeriesBuilder()
.withNumFactory(DecimalNumFactory.getInstance())
.build();
DoubleNum
favors speed over precision. Use it when approximate decimal arithmetic is sufficient.
BarSeries s3 = new BaseBarSeriesBuilder()
.withNumFactory(DoubleNumFactory.getInstance())
.build();
If you want to write your own implementation of Num
, implement the Num
interface and provide a corresponding NumFactory
so a BarSeries
can create numbers and bars consistently.
BarSeries
exposes its factory via series.numFactory()
. Use it to create values that match the series’ numeric type:
Num three = series.numFactory().numOf(3);
series.addTrade(100, 10.5); // accepts Number and converts via numFactory()
Create bars with the series’ barBuilder()
; numeric inputs are converted using the same factory:
Bar bar = series.barBuilder()
.timePeriod(Duration.ofMinutes(1))
.endTime(Instant.now())
.openPrice(100.0)
.highPrice(101.0)
.lowPrice(99.5)
.closePrice(100.7)
.volume(42)
.build();
series.addBar(bar);
Note: A BarSeries
has a fixed numeric type. Do not mix different Num
types on the same series.