Re: Factor

Factor: the language, the theory, and the practice.

TA-Lib

Sunday, August 24, 2025

#ffi

The TA-Lib is a C project that supports adding “technical analysis to your own financial market trading applications”. It was originally created in 2001 and is well-tested, recently released, and popular:

200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands etc… See complete list…

Candlestick patterns recognition

Core written in C/C++ with API also available for Python.

Open-Source (BSD License). Can be freely integrated in your own open-source or commercial applications.

Of course, I wanted to be able to call the library using Factor. We have a C library interface that makes it pretty easy to interface with C libraries.

First, we add the library we expect to load:

<< "ta-lib" {
    { [ os windows? ] [ "libta-lib.dll" ] }
    { [ os macos?   ] [ "libta-lib.dylib" ] }
    { [ os unix?    ] [ "libta-lib.so" ] }
} cond cdecl add-library >>

LIBRARY: ta-lib

Then, we can define some types and some library functions to calculate the relative strength index:

TYPEDEF: int TA_RetCode

FUNCTION: TA_RetCode TA_RSI ( int startIdx, int endIdx, double* inReal, int optInTimePeriod, int* outBegIdx, int* outNBElement, double* outReal )
FUNCTION: int TA_RSI_Lookback ( int optInTimePeriod )

We use a simple code generator to define all the functions, as well as wrappers that can be used to call it:

:: RSI ( real timeperiod -- real )
    0 int <ref> :> outbegidx
    0 int <ref> :> outnbelement
    real check-array :> inreal
    inreal length :> len
    inreal check-begidx1 :> begidx
    len 1 - begidx - :> endidx
    timeperiod TA_RSI_Lookback begidx + :> lookback
    len lookback make-double-array :> outreal
    0 endidx inreal begidx tail-slice timeperiod outbegidx outnbelement outreal lookback tail-slice TA_RSI ta-check-success
    outreal ;

And, now we can use it!

IN: scratchpad 10 10 randoms 3 RSI .
double-array{
    0/0.
    0/0.
    0/0.
    50.0
    62.16216216216216
    31.506849315068497
    46.38069705093834
    32.33644859813084
    59.75541967759867
    66.53570603189276
}

You’ll note that the first few values are 0/0. which represents a NaN when we don’t have enough data to compute an answer – either because we are in the lookback phase or because the inputs have NaNs.

For convenience, we convert the inputs to double-array to perform the calculation, but if the input is already a double-array then there is not any data conversion cost.

There are some advanced techniques including use of the Abstract API for meta-programming, default values for parameters, candlestick settings, streaming indicator support, and documentation that we probably should think about adding as well.

This is available on my GitHub.