summaryrefslogtreecommitdiffstats
path: root/ts.h
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
committerEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
commit07e18043145f3d91c8ea7ec3ad41b3c905585277 (patch)
tree2bdeae46973f4bdfce263679a4008ac768011f28 /ts.h
initial commit: ts time series analysis library v0.1.0
- 354 financial and mathematical time-series indicators - Uniform C11 API with consistent function signatures - 349 passing tests with -Werror - Static and shared library builds - pkg-config integration - Python bindings - Fuzzing and sanitizer support - Security hardening flags
Diffstat (limited to 'ts.h')
-rw-r--r--ts.h361
1 files changed, 361 insertions, 0 deletions
diff --git a/ts.h b/ts.h
new file mode 100644
index 0000000..fdb522e
--- /dev/null
+++ b/ts.h
@@ -0,0 +1,361 @@
+/*
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+#ifndef NDEBUG
+#include <assert.h>
+#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 */