/* * ts — Time Series Analysis Library * * 354 financial and mathematical time-series indicators. * See README.md for the full indicator list and documentation. * * MIT License — Copyright (c) 2025-2026 Ethan Werner */ #ifndef TIME_SERIES_H #define TIME_SERIES_H #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif /* * THREAD SAFETY * * All indicator functions are stateless and thread-safe. Each function operates * solely on its input/output buffers and parameters. Multiple threads may call * indicator functions concurrently provided they use separate buffers. * * ts_constant() / ts_constant_free() use heap allocation and are the caller's * responsibility to manage per-thread. */ /* --- Endianness check --- */ /* ts_cast_byte macros assume little-endian. Big-endian is unsupported. */ #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #error "ts requires a little-endian platform. Big-endian is not supported." #endif #elif defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) #error "ts requires a little-endian platform. Big-endian is not supported." #elif defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN) #error "ts requires a little-endian platform. Big-endian is not supported." #endif /* --- API version --- */ #define TS_VERSION_MAJOR 0 #define TS_VERSION_MINOR 1 #define TS_VERSION_PATCH 0 /* --- Deprecation --- */ #ifdef _MSC_VER #define TS_DEPRECATED __declspec(deprecated) #else #define TS_DEPRECATED __attribute__((deprecated)) #endif /* --- Format specifiers --- */ #define TS_PRIuint PRIu64 #define TS_PRIfp "lf" typedef double fp_t; typedef uint64_t ts_uint_t; typedef int64_t ts_int_t; /* Backward-compatible aliases — prefer ts_uint_t / ts_int_t */ typedef ts_uint_t uint_t; typedef ts_int_t int_t; #define TS_PI 3.14159265358979323846264338327950288419716939937510 #define TS_HALF_PI 1.57079632679489661923132169163975144209858469968755 /* Backward-compatible aliases */ #ifndef PI #define PI TS_PI #endif #ifndef HALF_PI #define HALF_PI TS_HALF_PI #endif /* * OHLCV access macros. * CONTRACT: caller must ensure 0 <= i < n. These macros perform no bounds * checking. n is the data length passed to the indicator function. */ #define ts_o( x, i ) ( (x)[0][i] ) #define ts_h( x, i ) ( (x)[1][i] ) #define ts_l( x, i ) ( (x)[2][i] ) #define ts_c( x, i ) ( (x)[3][i] ) #define ts_v( x, i ) ( (x)[4][i] ) #ifndef NDEBUG /* * Safe OHLCV access macros with bounds checking. * Only active when assertions are enabled (i.e., NDEBUG is not defined). */ #define ts_o_safe( x, i, n ) ( assert( (i) >= 0 && (uint_t)(i) < (n) ), (x)[0][i] ) #define ts_h_safe( x, i, n ) ( assert( (i) >= 0 && (uint_t)(i) < (n) ), (x)[1][i] ) #define ts_l_safe( x, i, n ) ( assert( (i) >= 0 && (uint_t)(i) < (n) ), (x)[2][i] ) #define ts_c_safe( x, i, n ) ( assert( (i) >= 0 && (uint_t)(i) < (n) ), (x)[3][i] ) #define ts_v_safe( x, i, n ) ( assert( (i) >= 0 && (uint_t)(i) < (n) ), (x)[4][i] ) #endif #define TS_TEST_K 5 #define TS_CONSTANT_SIZE 64 #define TS_FILL_ZERO_THRESHOLD 1e-15 #define TS_MAX_LENGTH UINT64_MAX /* * Opaque constant buffer. * MAX PARAMETERS: TS_CONSTANT_SIZE bytes (64). Sufficient for up to 8 * uint64_t or 8 fp_t parameters. Larger parameter sets require custom * allocation via ts_constant(). * USAGE: pass a pointer to your params struct, e.g.: * uint_t k = 14; * ts_rsi(y, x, n, &k); // &k is cast to ts_constant_t const * * Or use the allocator: * ts_constant_t *c = ts_constant(); * *(uint_t *)c = 14; * ts_rsi(y, x, n, c); * ts_constant_free(c); */ typedef struct ts_constant_s { uint8_t data[TS_CONSTANT_SIZE]; } ts_constant_t; /* * Parameter metadata for indicator functions. * n_inputs: number of input arrays expected (e.g., 5 for OHLCV) * n_outputs: number of output arrays produced * n_constants: number of constant parameters * params_size: size in bytes of the params struct */ typedef struct { uint_t n_inputs; uint_t n_outputs; uint_t n_constants; uint_t params_size; } ts_parameter_t; /* Alias for registry introspection — identical layout to ts_parameter_t */ typedef ts_parameter_t ts_fn_info_t; uint_t ts_fn_count( void ); const ts_fn_info_t* ts_registry_get( uint_t index ); const char* ts_fn_name_get( uint_t index ); /* * TS_PROLOGUE — Common validation for all indicator functions. * USAGE: Place at the top of the function body. * TS_PROLOGUE( ts_rsi_p ) * Checks pointers, data length, and input/output array counts against * the indicator's ts_parameter_t metadata. Does NOT check `c` or inputs * — do that explicitly if your indicator requires them. */ #define TS_PROLOGUE( _p ) \ do { \ if ( !x || !y || n == 0 || \ x_n < (_p).n_inputs || y_n < (_p).n_outputs ) return -1; \ } while ( 0 ) #define TS_TEST_EPSILON 1.0e-5 /* * TS_CHECK_INPUT — Validate all input arrays for NaN/Inf. * USAGE: Place at the start of the function body. * TS_CHECK_INPUT(x, x_n, n) * Checks up to the first `n` elements of each of the `x_n` input arrays * for NaN or Inf. Returns -1 if any invalid value found. */ #define TS_CHECK_INPUT( x, x_n, n ) \ do { \ for ( uint_t _ts_c = 0; _ts_c < (x_n); ++_ts_c ) { \ for ( uint_t _ts_i = 0; _ts_i < (n); ++_ts_i ) { \ if ( isnan( (x)[_ts_c][_ts_i] ) || isinf( (x)[_ts_c][_ts_i] ) ) return -1; \ } \ } \ } while ( 0 ) /* --- Test / dev utilities (only when TS_IMPLEMENTATION is defined) --- */ /* * ts_cast_byte — Read a value of the given type from a byte offset in data. * NOTE: assumes little-endian byte order. Behavior is undefined on * big-endian platforms without adjustment. */ #define ts_cast_byte( data, byte, type ) \ _ts_read_##type( ( const uint8_t * )( data ) + ( byte ) ) static inline uint_t _ts_read_uint_t( const uint8_t *p ) { uint_t v; memcpy( &v, p, sizeof( v ) ); return v; } static inline fp_t _ts_read_fp_t( const uint8_t *p ) { fp_t v; memcpy( &v, p, sizeof( v ) ); return v; } static inline int_t _ts_read_int_t( const uint8_t *p ) { int_t v; memcpy( &v, p, sizeof( v ) ); return v; } /* * ts_fill — Linearly fill array x with values from x0 to x1. * RETURNS: the same pointer x (for convenience). Caller retains ownership. * PRECONDITION: x must not be NULL; n > 0. */ fp_t * ts_fill( fp_t *x, uint_t n, fp_t x0, fp_t x1 ); /* * ts_print — Print n elements of x to stream. * If stream is NULL, output goes to stdout. * PRECONDITION: x must not be NULL. */ void ts_print( fp_t const *x, uint_t n, FILE *stream ); /* * ts_pretty_print — Pretty-print m arrays of n elements each to stream. * If stream is NULL, output goes to stdout. * PRECONDITION: x must not be NULL. */ void ts_pretty_print( fp_t const * const *x, uint_t m, uint_t n, FILE *stream ); /* * ts_constant — Allocate a zero-initialized constant buffer on the heap. * RETURNS: heap-allocated ts_constant_t*, or NULL on allocation failure. * CALLER MUST free with ts_constant_free(). */ ts_constant_t* ts_constant( void ); /* * ts_constant_free — Free a constant buffer allocated by ts_constant(). * NULL-safe: passing NULL is a no-op. */ void ts_constant_free( ts_constant_t *c ); /* Internal helpers (underscore prefix indicates non-public API) */ uint_t _ts_circle_increment( uint_t, uint_t ); fp_t _ts_square( fp_t ); fp_t _ts_reciprocal( fp_t ); fp_t _ts_triangle_number( fp_t ); fp_t _ts_sum_to_covariance( fp_t, fp_t, fp_t, uint_t ); fp_t _ts_sum_to_variance( fp_t, fp_t, uint_t ); /* * Welford's online variance accumulator (numerically stable). * Fields are public for direct access; initialize with ts_welford_init(). * PRECONDITION: pointer w must not be NULL for all ts_welford_* functions. */ typedef struct { fp_t mean; fp_t m2; uint_t count; } ts_welford_t; static inline void ts_welford_init( ts_welford_t *w ) { if (!w) return; w->mean = 0; w->m2 = 0; w->count = 0; } static inline void ts_welford_add( ts_welford_t *w, fp_t x ) { if (!w) return; w->count++; fp_t delta = x - w->mean; w->mean += delta / w->count; fp_t delta2 = x - w->mean; w->m2 += delta * delta2; } static inline void ts_welford_remove( ts_welford_t *w, fp_t x ) { if (!w) return; if (w->count <= 1) { ts_welford_init(w); return; } fp_t old_mean = w->mean; w->count--; w->mean = (old_mean * (w->count + 1) - x) / w->count; fp_t delta = x - old_mean; fp_t delta2 = x - w->mean; w->m2 -= delta * delta2; if (w->m2 < 0) w->m2 = 0; } static inline fp_t ts_welford_variance( ts_welford_t *w ) { if (!w) return 0; return w->count > 0 ? w->m2 / w->count : 0; } /* --- Test harness (only when TS_IMPLEMENTATION is defined) --- */ #ifdef TS_IMPLEMENTATION /* * ts_test — Run an indicator function and compare output against expected values. * * CONTRACT: each expected array e[i] must have at least (n - offset) elements, * where offset = ts_offset(c). The test compares y[i][j + offset] against e[i][j] * for j in [0, n - offset). */ bool ts_test( int ( *function )( fp_t * const *y, uint_t y_n, fp_t const * const *x, uint_t x_n, uint_t n, ts_constant_t const *c ), uint_t ( *ts_offset )( ts_constant_t const *c ), fp_t const * const *x, fp_t const * const *e, uint_t n, ts_constant_t const *c, ts_parameter_t const *m ); #endif /* TS_IMPLEMENTATION */ /* * Auto-generated indicator declarations. * Generated by scripts/gen_all.py → scripts/gen_bindings.py. * Do not edit manually. * NOTE: ts_generated.h must exist before including this header. * Generate with: python3 scripts/gen_all.py */ #include "ts_generated.h" #endif /* TIME_SERIES_H */