summaryrefslogtreecommitdiffstats
path: root/ts.h
blob: 43761eaefa0dd314d4914fa458b3048711aede70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/*
 * 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: x on success, NULL if x is NULL or n == 0. Caller retains ownership.
 */
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 + ( old_mean - x ) / w->count;
	fp_t delta = x - old_mean;
	fp_t delta2 = x - w->mean;
	w->m2 -= delta * delta2;
	w->m2 = fmax( 0.0, w->m2 );
}

static inline fp_t ts_welford_variance( ts_welford_t *w )
{
	if ( !w ) return 0;
	return w->count > 0 ? w->m2 / w->count : 0;
}

static inline fp_t ts_welford_sample_variance( ts_welford_t *w )
{
	if ( !w ) return 0;
	return w->count > 1 ? w->m2 / ( w->count - 1 ) : 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 */