Tulip Indicators Usage

Open-Source Technical Analysis Indicator Library

Fork me on GitHub

Usage Index

  1. Introduction
  2. Features
  3. Building
  4. Indicator Interface
  5. A first example
  6. Generic Interface

Introduction

Tulip Indicators is a set of technical analysis indicator functions implemented in ANSI C. It has no external dependencies. Tulip Indicators currently implements 104 indicators. The complete list is here.

A primary goal in implementing Tulip Indicators was to create an easy-to-use interface. To this end, every Tulip Indicators function has exactly the same interface. Therefore, if you learn how to use one indicator, you know how to use them all.

Features

Building

Tulip Indicators requires a good C compiler, and has no other external dependencies.

Tulip Indicators is easiest to build from the command line. On most platforms, you'll just need to type make. This will build Tulip Indicators as static library and run the unit tests.

git clone https://github.com/TulipCharts/tulipindicators
cd tulipindicators
make

To use Tulip Indicators in your project, you'll need only the header file indicators.h and the static library, libindicators.a.

If you are having trouble with make, you can build manually by compiling every .c file in the indicators and utils sub-directories along with indicators_index.c in the main directory. The other .c files in the main directory are utilities used for testing or other purposes.

If you're still having trouble, contact us. It's important to me that everyone can easily build and use Tulip Indicators.

Indicator Interface

int ti_XXX_start(TI_REAL const *options);
int ti_XXX(int size,
             TI_REAL const *const *inputs,
             TI_REAL const *options,
             TI_REAL *const *outputs);

Each of the 104 indicators provided by Tulip Indicators implements the above two functions (where XXX is the indicator name).

The first function is the start function. Call the start function with a given set of option parameters, and it will tell you how much shorter the calculated output array(s) will be than the input array(s) length.

The second function does the actual calculation. Pass in the input array length, an array of input data, an array of options, and an array output data. The array of output data will be overwritten with the calculated output.

Inputs, options, and outputs are always passed as arrays. This is so that every Tulip Indicators function has exactly the same interface, regardless of how many inputs, options, or outputs it actually requires.

Tulip Indicators expects the oldest data in the time series to be in index 0. Order your data from oldest to most recent.

A first example

As an example, we'll look at calculating a Simple Moving Average on an example dataset. The Simple Moving Average function expects one input, one option, and provides one output. We'll setup those arguments before calling it.

Our example data set will be defined as:

/* Order data from oldest to newest (index 0 is oldest) */
const double data_in[] = {5,8,12,11,9,8,7,10,11,13};
const int input_length = sizeof(data_in) / sizeof(double); /* 10 in this example */

The first thing we need to figure out, is which options we are going to give to the indicator function. The Simple Moving Average function takes one option, which is the moving average period. In this example we'll use a period of 3. The Tulip Indicators function will expect all the options to be in an array. This may seem silly for passing in one option, but it allows a consistency with the functions that take multiple options.

We put our option, the period 3, into an array:

const double options[] = {3};

We can now calculate exactly how big our expected output is. Each Tulip Indicators function has a corresponding start function which takes a set of options and tells us how much smaller the output array will be than the input array. In this case, we call the start function for Simple Moving Average as follows:

/* Find start size for given options. */
const int start = ti_sma_start(options);

/* Output length is input length minus start size. */
const int output_length = input_length - start;

In this example, the start amount will be 2. This means that although we are passing in 10 input bars, we will only get 8 output bars (that is 10 minus 2). This is because the Simple Moving Average is not valid for the first 2 bars (given our option of 3 being the period).

We can now allocate memory for the output:

double *data_out = malloc(output_length * sizeof(double));
assert(data_out != 0);

There is one last step before doing the Simple Moving Average calculation. We must put all of our inputs and outputs into arrays. Again, this is because Tulip Indicators uses a consistent interface for all of the indicator functions. Many indicators take either multiple inputs or generate multiple outputs.

const double *all_inputs[] = {data_in};
double *all_outputs[] = {data_out};

And finally we are ready to do the actual Simple Moving Average calculation.

int error = ti_sma(input_length, all_inputs, options, all_outputs);
assert(error == TI_OKAY);

Take a look at example1.c to see it altogether.

Generic Interface

One of the best features of Tulip Indicators is that you can enumerate all the indicator functions at runtime. This means you can easily add all of the Tulip Indicator functions to your program with only a few lines of code. This is also the recommended method for using Tulip Indicators.

Tulip Indicators defines the following structure in indicators.h:

typedef struct ti_indicator_info {
    char *name;
    char *full_name;
    ti_indicator_start_function start;
    ti_indicator_function indicator;
    int type, inputs, options, outputs;
    char *input_names[TI_MAXINDPARAMS];
    char *option_names[TI_MAXINDPARAMS];
    char *output_names[TI_MAXINDPARAMS];
} ti_indicator_info;

If you know the name of an indicator you're looking for, you can get its info by calling ti_find_indicator().

/*Searches for an indicator by name. Returns 0 if not found.*/
const ti_indicator_info *ti_find_indicator(const char *name);

For example, you can find the Simple Moving Average function at runtime with:

ti_indicator_info *info = ti_find_indicator("sma");

if (info) {
    printf("Found indicator %s.\n It takes %d inputs, %d options, and provides %d outputs\n",
            info->full_name, info->inputs, info->options, info->outputs);

    /* TODO use the indicator (e.g. info->start(...), info->indicator(...)) */

} else {
    printf("Erorr, not found\n");
}

You can also quickly loop through all available functions with the ti_indicators array.

/* Set info to first indicators in array. */
const ti_indicator_info *info = ti_indicators;

/* The last item is all zeros, so loop until then. */
while (info->name != 0) {

    /* TODO use the info structure to do something.
       e.g. printf("Found %s\n", info->name); */

    ++info; /* Next indicator. */
}

Regardless of how you get the ti_indicator_info structure for a function, the usage is the same:

    /* Assuming 'info' is a pointer to a ti_indicator_info structure. */

    /* Get the name (e.g. "sma") and full name (e.g. "Simple Moving Average"). */
    printf("Have indicator %s (%s)\n", info->name, info->full_name);

    /* Get number of inputs, options, outputs. */
    printf("This indicator has %d inputs, %d options, and calculates %d outputs.\n",
            info->inputs, info->options, info->outputs);

    /* The arrays:
            info->input_names contains the names of each expected input
            info->option_names contains the names of each expected option
            info->output_names contains the names of each output
     */

    /* TODO have inputs and options. e.g.
        double *inputs[] = {a, b, c};
        double options[] = {5, 7, 10};
        double *outputs[] = {x, y, z};
     */

    /* Find how much shorter the outputs are than the inputs. */
    const int start = info->start(options);

    /* TODO allocate output array as (size - start) for each output. */

    /* Call the indicator to do the actual calculation. */
    const int r = info->indicator(size, inputs, options, outputs);

    if (r == TI_OKAY) {
        /* It worked, the output arrays have been filled. */
    } else {
        /* Something went wrong. */
    }

See example2.c for an example of looping through each indicator. Also see sample.c as an example that will either loop through each indicator, or search for one indicator by name and run it on some test data. The programs smoke.c and benchmark.c also use the generic interface, but are a bit more complicated.

If you have any questions or need help, don't hesitate to contact me.