C/C++ Core API
2.0 How to add TA-Lib to your app
3.1 Initialize and Shutdown
3.2 Batch Processing
3.3 Output Size and Lookback
3.4 Return Codes
4.1 Abstraction Layer
4.2 Numerical Stability
4.3 Candlestick Settings
4.4 Input Type: float vs. double
4.5 High-performance Multi-threading
1.0 Introduction
The Core API provides:
- The lifecycle of the library (TA_Initialize / TA_Shutdown).
- The global settings (e.g. TA_SetUnstablePeriod, TA_SetCandleSettings).
- Every TA function, each processing a whole array of data at once.
- An optional abstraction layer for calling those functions dynamically.
To process a live feed one bar at a time instead, see the companion C/C++ Streaming API.
You must first install the C/C++ library, which will provide all the shared/static libraries and headers needed to compile and link your program.
2.0 How to add TA-Lib to your app
In your source code, add #include "ta_libc.h" and link to the library named "ta-lib".
You may need to add TA-Lib to the compiler's and linker's search paths. For example, with gcc:
-I/usr/local/include/ta-lib -L/usr/local/lib -lta-libThe paths depend on the method used to install. Typical locations for headers (-I) are:
/usr/local/include/ta-lib/usr/include/ta-lib/opt/include/ta-lib
Typical locations for the libraries (-L) are:
/usr/lib/usr/lib64/usr/local/lib/usr/local/lib64/opt/lib/opt/local/lib
For homebrew, use brew --prefix ta-lib to find the paths.
For Windows, look into C:\Program Files\TA-Lib for 64-bit and C:\Program Files (x86)\TA-Lib for 32-bit.
3.0 Calling into TA-Lib
All of TA-Lib's public functions are declared in the include/*.h headers.
3.1 Initialize and Shutdown
TA_RetCode TA_Initialize( void ); TA_RetCode TA_Shutdown( void );
TA_Initialize must be called once (and only once), from a single thread, prior to any other API function. After it returns TA_SUCCESS, you can start processing your data in three ways: batch processing, the streaming API or through the abstraction layer.
TA_Shutdown releases the resources acquired by TA_Initialize. Call it single-threaded, typically from the last remaining thread just before your application exits.
3.2 Batch Processing
Every function follows the same simple pattern: it reads its inputs from arrays you pass in and writes its results to buffers you allocate.
A function never writes more elements than you request, so the buffers only need to cover the startIdx-to-endIdx range.
As an example, let's walk through TA_MA, a function to calculate a moving average.
TA_RetCode TA_MA( int startIdx, int endIdx, const double inReal[], int optInTimePeriod, int optInMAType, int *outBegIdx, int *outNBElement, double outReal[] )
All TA functions use the same calling pattern, divided into four groups:
- The output will be calculated only for the range specified by startIdx and endIdx. These are zero-based indices into the input arrays.
- One or more input arrays are then specified. Typically, these are the "price" data. In this example there is only one input. All input parameter names start with "in".
- Zero or more optional inputs are then specified. In this example there are two optional inputs. These parameters give finer control specific to each function. If you do not care about a particular optIn, just specify TA_INTEGER_DEFAULT or TA_REAL_DEFAULT (depending on the type). For a moving-average type, use TA_MAType_DEFAULT.
- One or more output arrays come last. In this example there is only one output (outReal). The parameters outBegIdx and outNBElement always come just before the output arrays.
This calling pattern takes some getting used to, but it lets your app spend time and memory only on the data it actually needs.
For example, here is how to calculate a 30-day simple moving average (SMA) of daily closing prices:
TA_Real closePrice[400]; TA_Real out[400]; TA_Integer outBeg; TA_Integer outNBElement;
/* ... initialize your closing price here... */
retCode = TA_MA( 0, 399, &closePrice[0], 30, TA_MAType_SMA, &outBeg, &outNBElement, &out[0] );
/* The output is displayed here */ for( i=0; i < outNBElement; i++ ) printf( "Day %d = %f\n", outBeg+i, out[i] );
After the call, it is important to check the values returned in outBeg and outNBElement. Even though we requested the whole range (0 to 399), a 30-day average is not defined until the 30th day. Consequently, outBeg will be 29 (zero-based) and outNBElement will be 400-29 = 371. In other words, only the first 371 elements of out[] are written, and they correspond to input elements 29 through 399.
As another example, if you had requested only the range 125 to 225, outBeg would be 125 and outNBElement would be 101 (endIdx is inclusive: 225-125+1). The 30-day minimum is not a problem here, because the 125 closing prices before the requested range provide the needed history. As you may have guessed, only the first 101 elements of out[] are written; the rest is left untouched.
Here is another example. This time we calculate a 14-bar exponential moving average for a single price bar (say, the last of 300 bars):
TA_Real closePrice[300]; TA_Real out; TA_Integer outBeg; TA_Integer outNBElement;
/* ... initialize your closing price here... */
retCode = TA_MA( 299, 299, &closePrice[0], 14, TA_MAType_EMA, &outBeg, &outNBElement, &out );
In this example, outBeg will be 299, outNBElement will be 1, and only one value is written into out.
If you do not provide enough data to calculate even one value, outNBElement will be 0 and outBeg should be ignored.
If the input and output of a TA function are of the same type, the caller can reuse the input buffer to store one of the outputs. The following example works:
#define BUFFER_SIZE 100 TA_Real buffer[BUFFER_SIZE]; ... retCode = TA_MA( 0, BUFFER_SIZE-1, &buffer[0], 30, TA_MAType_SMA, &outBeg, &outNBElement, &buffer[0] );
Of course, the input is overwritten, but this avoids allocating a temporary buffer. All TA functions support this.
3.3 Output Size and Lookback
It is important that the output array is large enough. Here are three ways to determine the allocation size; all of them work for every TA function:
| Method | Description |
|---|---|
| Input Matching | allocationSize = endIdx + 1; Pros: Easy to understand and implement. Cons: Memory allocation unnecessarily large when requesting a small range. |
| Range Matching | allocationSize = endIdx - startIdx + 1; Pros: Easy to implement. Cons: Allocation slightly larger than needed. Example: with startIdx = 0, a 30-period SMA wastes 29 elements because of the lookback. |
| Exact Allocation | lookback = TA_XXXX_Lookback( ... ) ; temp = max( lookback, startIdx ); if( temp > endIdx ) allocationSize = 0; // No output else allocationSize = endIdx - temp + 1; Pros: Allocates exactly what is needed. Cons: Slightly more complex. |
Each TA function has a matching TA_XXXX_Lookback function. Example: for TA_SMA, it is TA_SMA_Lookback.
The lookback is the number of input elements consumed before the first output can be calculated. Example: a simple moving average (SMA) of period 10 has a lookback of 9.
3.4 Return Codes
Every TA function returns a TA_RetCode. TA_SUCCESS (zero) means the call completed and wrote its outputs; on anything else, treat outBegIdx and outNBElement as undefined and the output buffers as untouched.
The codes a caller normally encounters:
| Code | Meaning |
|---|---|
TA_SUCCESS | No error. |
TA_LIB_NOT_INITIALIZE | TA_Initialize was not called, or did not succeed. |
TA_BAD_PARAM | A parameter is out of range, or a required pointer is NULL. |
TA_ALLOC_ERR | Allocation failed, most likely out of memory. |
TA_OUT_OF_RANGE_START_INDEX | startIdx is negative or above TA_MAX_INDEX. |
TA_OUT_OF_RANGE_END_INDEX | endIdx is negative, above TA_MAX_INDEX, or below startIdx. |
The full list is the TA_RetCode enumeration in ta_defs.h. Rather than mapping the codes yourself, TA_SetRetCodeInfo turns any of them - including one this version of the library does not know - into a printable name and description:
TA_RetCodeInfo info;
if( retCode != TA_SUCCESS )
{
TA_SetRetCodeInfo( retCode, &info );
printf( "Error %d(%s): %s\n", retCode, info.enumStr, info.infoStr );
}which prints, for example:
Error 1(TA_LIB_NOT_INITIALIZE): TA_Initialize was not successfully calledThe TA_XXXX_Lookback functions are the exception to the pattern: they return an int rather than a TA_RetCode, and answer -1 when a parameter is out of range. Check for that before using the value as an allocation size.
3.5 Index Range
TA_MAX_INDEX is the largest value startIdx or endIdx may take: 100,000,000. A call outside the range is rejected rather than computed:
| Condition | Return code |
|---|---|
startIdx < 0 or startIdx > TA_MAX_INDEX | TA_OUT_OF_RANGE_START_INDEX |
endIdx < 0, endIdx > TA_MAX_INDEX, or endIdx < startIdx | TA_OUT_OF_RANGE_END_INDEX |
The limit is the same number in every language binding — TA_MAX_INDEX in C, ta_lib::MAX_INDEX in Rust, Core.MAX_INDEX in Java and C# — so a call is accepted or rejected identically whichever you use. It is a constant rather than a buffer length, so the check costs two comparisons and is done before anything is read.
For context on the size: 100 million one-minute bars is about 190 years of 24/7 data, or a century of a regular equity session. Series that long are usually tick data, where the streaming API is the better tool anyway.
This bounds the API domain and nothing else. In particular it is not a promise about accuracy. A handful of functions accumulate rounding error as the series grows, and the worst of them have lost several digits well before this limit — WMA, HMA, CORREL and the LINEARREG family are the ones to know about. No single index cap can express that, because the error depends on the data and on the period, not on the index alone. It is also unrelated to the numerical-stability categories, which answer a different question: whether a value converges as history grows, not how rounding accumulates within it.
Raising this limit later would only admit calls that are rejected today, so it is safe to treat 100,000,000 as a floor rather than a fixed contract.
4.0 Advanced Features
4.1 Abstraction Layer
Instead of hard-coding calls to specific TA functions, an app can drive them all dynamically through the interface in ta_abstract.h — looking functions up by name at runtime. For any function it reports the inputs it takes, its optional parameters with their valid ranges, and the outputs it produces — so you can call a function whose signature was unknown at compile time.
This is what you want when the function or its parameters are not fixed in your code. Typical uses:
- Generating glue code or wrappers for higher-level languages.
- Automatically picking up new functions after a TA-Lib upgrade, with no code change.
- "Mutating" the function and its parameters while searching for strategies (e.g. a genetic or neural-network algorithm).
- Populating a charting app: the indicator menu and each settings dialog come straight from the metadata.
If you only need a handful of specific functions, calling them directly — with batch processing or the streaming API — is simpler.
4.2 Numerical Stability
Take one bar and compute an indicator for it twice: once with a year of history before it, once with a decade. Do you get the same value? For many functions, always — they read a fixed number of bars and ignore everything older. Others are recursive, so their earliest values depend on how much history precedes them, converging as more bars are supplied — the Exponential Moving Average is the classic example. A few accumulate from the very first bar and never converge at all.
Each function's documentation specifies which of the four numerical-stability categories applies to it.
This is about convergence, not rounding. A function can be perfectly convergent and still accumulate floating-point error over a very long series — a separate axis, noted under Index Range.
See the Unstable Period page for how to configure this.
4.3 Candlestick Settings
The candlestick pattern functions (TA_CDL*) judge each candle against tunable thresholds — is its body "long", its shadow "short", two candles "near". These thresholds are global settings: change them once, from a single thread, before any concurrent calls (see multi-threading).
See the Candlestick Settings page for the API, the setting types and their defaults.
4.4 Input Type: float vs. double
Each TA function has two implementations: one accepts input arrays of double, the other of float. The float version carries the "TA_S_" prefix, e.g. TA_S_MA is the float equivalent of TA_MA.
TA_RetCode TA_MA( int startIdx,
int endIdx,
const double inReal[],
int optInTimePeriod,
TA_MAType optInMAType,
int *outBegIdx,
int *outNBElement,
double outReal[] );
TA_RetCode TA_S_MA( int startIdx,
int endIdx,
const float inReal[],
int optInTimePeriod,
TA_MAType optInMAType,
int *outBegIdx,
int *outNBElement,
double outReal[] );
Internally, both versions do all calculations in double — each float element is converted to double when read. Consequently, both functions produce the same output, bit-for-bit.
Some apps already hold their price data as float. The TA_S_XXXX functions consume such arrays directly (no conversion copy needed) while keeping every intermediate calculation in double.
4.5 High-performance Multi-threading
TA-Lib is multi-thread safe where it matters most for performance: calling the TA functions themselves (TA_SMA, TA_RSI, ...).
One important caveat: the "global settings" must first be initialized from a single thread. That includes calls to:
Once these initial calls are done, the application can call the rest of the API from multiple threads (including the ta_abstract.h interface).
The exception at the other end is TA_Shutdown, which is single-threaded as well.
Note: TA-Lib assumes it is linked against a thread-safe malloc/free runtime, which is the default on all modern platforms (Linux, Windows, Mac). In other words, any toolchain supporting C11 or newer is safe.