diff options
725 files changed, 30360 insertions, 0 deletions
diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..b931987 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,28 @@ +#!/bin/sh +set -e + +# Pre-commit hook: run tests before allowing commit +# Skip with: SKIP_PRE_COMMIT=1 git commit +# Skip on force push: git commit --no-verify + +if [ "$SKIP_PRE_COMMIT" = "1" ]; then + exit 0 +fi + +# Only run tests if C/H/Makefile changed +staged=$(git diff --cached --name-only --diff-filter=d) +if echo "$staged" | grep -qE '\.(c|h)$|^Makefile$'; then + # Run build check + if ! make >/dev/null 2>&1; then + echo "PRE-COMMIT FAILED: build did not succeed." + exit 1 + fi + + # Run tests + if ! make test >/dev/null 2>&1; then + echo "PRE-COMMIT FAILED: tests did not pass." + exit 1 + fi +fi + +exit 0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b615ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# C build artifacts +build/ +*.o +*.so +*.a +*.d + +# Core dumps +core + +# Python +__pycache__/ +*.pyc +*.egg-info/ +dist/ +.pytest_cache/ +.ruff_cache/ + +# IDE / editors +.vscode/ +.idea/ +*.swp +*~ +.DS_Store + +# macOS +*.dSYM/ +*.app + +# Coverage +*.gcno +*.gcda +*.gcov + +# Distribution +*.tar.gz +*.tar.bz2 +*.zip + +# Patches +*.orig +*.rej + +# Generated files +ts_generated.h +test/main_generated.c +lib/ts_flat_generated.c +lib/ts_registry_generated.c +test/fuzz_table_generated.c +python/ts/__init__.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..db68a56 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.1.0] - 2026-06-29 + +### Added +- 354 financial and mathematical time-series indicators +- Uniform C API with consistent function signatures +- Comprehensive test suite (349 tests) +- Static and shared library builds +- pkg-config integration +- Python bindings scaffold +- Fuzzing support +- Security hardening flags (-D_FORTIFY_SOURCE=2, -fstack-protector-strong) +- Sanitizer support (ASAN, UBSan) +- Valgrind memory checking @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-2026 Ethan Werner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..572e23b --- /dev/null +++ b/Makefile @@ -0,0 +1,224 @@ +CC ?= gcc +PREFIX ?= /usr/local +VERSION := $(or $(shell tr -d '[:space:]' < VERSION 2>/dev/null), 0.0.0) +SOVERSION := $(word 1,$(subst ., ,$(VERSION))) +PYTHON3 := $(shell command -v python3 2>/dev/null) +ifeq ($(PYTHON3),) + $(error python3 is required but not found) +endif + +# Base flags +CFLAGS_BASE = -std=c11 -Wall -Wextra -Wshadow -Wdouble-promotion -Wnull-dereference -Wimplicit-fallthrough -Wmisleading-indentation -pedantic -O2 -I. -Ilib -MMD -MP + +# Platform-specific +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + CFLAGS_BASE += -D_DEFAULT_SOURCE +else ifeq ($(UNAME_S),Darwin) + CFLAGS_BASE += -D_DARWIN_C_SOURCE +else + # BSD and others + CFLAGS_BASE += -D_BSD_SOURCE +endif + +# Security hardening (optimized builds only) +CFLAGS_HARDENING = -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat-security -fno-common + +CFLAGS := $(CFLAGS_BASE) $(CFLAGS_HARDENING) +LDLIBS = -lm + +SRCDIR = lib +BUILDDIR = build +TESTDIR = test +SCRIPTDIR = scripts +TEST_BUILDDIR = $(BUILDDIR)/test + +SRCS = $(filter-out $(SRCDIR)/ts.c $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c $(SRCDIR)/test.c $(wildcard $(SRCDIR)/ts_*.c),$(wildcard $(SRCDIR)/*.c)) +OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS)) +DEPS = $(OBJS:.o=.d) $(BUILDDIR)/ts.d +TS_OBJ = $(BUILDDIR)/ts.o + +SHARED_OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.so.o,$(SRCS)) +SHARED_TS_OBJ = $(BUILDDIR)/ts.so.o +FLAT_OBJ = $(BUILDDIR)/ts_flat_generated.so.o +REG_OBJ = $(BUILDDIR)/ts_registry_generated.so.o + +TEST_SRCS = $(TESTDIR)/main.c $(filter-out $(TESTDIR)/main.c $(TESTDIR)/main_generated.c $(TESTDIR)/gen_test_data.c $(TESTDIR)/bench.c $(TESTDIR)/fuzz_table_generated.c,$(wildcard $(TESTDIR)/*.c)) +TEST_OBJS = $(patsubst $(TESTDIR)/%.c,$(BUILDDIR)/test/%.o,$(TEST_SRCS)) + +MAIN = $(BUILDDIR)/ts +TEST_BIN = $(BUILDDIR)/run_tests +BENCH_BIN = $(BUILDDIR)/bench +LIB = $(BUILDDIR)/libts.a +SHLIB = $(BUILDDIR)/libts.so + +.PHONY: all test test-one test-python check valgrind clean sanitize sanitize-test lib shared install uninstall distclean dist bench gen-bindings + +all: $(MAIN) + +lib: $(LIB) + +shared: $(SHLIB) gen-bindings + +$(LIB): $(OBJS) $(TS_OBJ) | $(BUILDDIR) + ar rcs $@ $^ + +ifeq ($(UNAME_S),Linux) + SHLIB_SONAME = -Wl,-soname,libts.so.$(SOVERSION) +else ifeq ($(UNAME_S),Darwin) + SHLIB_SONAME = -Wl,-install_name,@rpath/libts.so.$(SOVERSION) +else + SHLIB_SONAME = -Wl,-soname,libts.so.$(SOVERSION) +endif + +$(SHLIB): $(SHARED_OBJS) $(SHARED_TS_OBJ) $(FLAT_OBJ) $(REG_OBJ) | $(BUILDDIR) + $(CC) -shared $(SHLIB_SONAME) -Wl,-rpath,'$$ORIGIN' -o $@ $^ $(LDLIBS) + +$(BUILDDIR): + @mkdir -p $@ + +$(BUILDDIR)/%.o: $(SRCDIR)/%.c ts.h ts_generated.h | $(BUILDDIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILDDIR)/%.so.o: $(SRCDIR)/%.c ts.h ts_generated.h | $(BUILDDIR) + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +$(BUILDDIR)/ts.o: ts.c | $(BUILDDIR) + $(CC) $(CFLAGS) -c ts.c -o $@ + +$(BUILDDIR)/ts.so.o: ts.c | $(BUILDDIR) + $(CC) $(CFLAGS) -fPIC -c ts.c -o $@ + +ts_generated.h: $(SCRIPTDIR)/gen_all.py $(filter-out $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c,$(wildcard $(SRCDIR)/*.c)) + python3 $< || (echo "ERROR: gen_all.py failed" && exit 1) + +test/main_generated.c: ts_generated.h + @touch $@ + +$(SRCDIR)/ts_flat_generated.c: $(SCRIPTDIR)/gen_flat.py $(filter-out $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c,$(wildcard $(SRCDIR)/*.c)) + python3 $< || (echo "ERROR: gen_flat.py failed" && exit 1) + +$(FLAT_OBJ): $(SRCDIR)/ts_flat_generated.c ts.h | $(BUILDDIR) + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +$(SRCDIR)/ts_registry_generated.c: $(SCRIPTDIR)/gen_registry.py $(filter-out $(SRCDIR)/ts_registry_generated.c $(SRCDIR)/ts_flat_generated.c,$(wildcard $(SRCDIR)/*.c)) + python3 $< || (echo "ERROR: gen_registry.py failed" && exit 1) + +$(REG_OBJ): $(SRCDIR)/ts_registry_generated.c ts.h | $(BUILDDIR) + $(CC) $(CFLAGS) -fPIC -c $< -o $@ + +$(TESTDIR)/fuzz_table_generated.c: $(SCRIPTDIR)/gen_fuzz.py $(filter-out $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c,$(wildcard $(SRCDIR)/*.c)) + python3 $< || (echo "ERROR: gen_fuzz.py failed" && exit 1) + +$(BUILDDIR)/test/fuzz.o: $(TESTDIR)/fuzz.c $(TESTDIR)/fuzz_table_generated.c ts.h | $(TEST_BUILDDIR) + $(CC) $(CFLAGS) -DTS_IMPLEMENTATION -c $< -o $@ + +$(TEST_BUILDDIR): + @mkdir -p $@ + +$(BUILDDIR)/test/%.o: $(TESTDIR)/%.c ts.h | $(TEST_BUILDDIR) + $(CC) $(CFLAGS) -DTS_IMPLEMENTATION -c $< -o $@ + +$(MAIN): $(OBJS) $(TS_OBJ) main.c | $(BUILDDIR) + $(CC) $(CFLAGS) main.c $(OBJS) $(TS_OBJ) -o $@ $(LDLIBS) + +$(TEST_BIN): $(OBJS) $(TEST_OBJS) $(TS_OBJ) | $(BUILDDIR) + $(CC) $(CFLAGS) $(TEST_OBJS) $(OBJS) $(TS_OBJ) -o $@ $(LDLIBS) + +test: $(TEST_BIN) + ./$(TEST_BIN) + +$(BENCH_BIN): $(OBJS) $(TS_OBJ) test/bench.c | $(BUILDDIR) + $(CC) $(CFLAGS) test/bench.c $(OBJS) $(TS_OBJ) -o $@ $(LDLIBS) + +bench: $(BENCH_BIN) + ./$(BENCH_BIN) + +python/ts/__init__.py: $(SCRIPTDIR)/gen_bindings.py $(SCRIPTDIR)/ts_parser.py $(filter-out $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c,$(wildcard $(SRCDIR)/*.c)) + python3 $(SCRIPTDIR)/gen_bindings.py || (echo "ERROR: gen_bindings.py failed" && exit 1) + +gen-bindings: python/ts/__init__.py + +test-python: shared gen-bindings + LD_LIBRARY_PATH=$(BUILDDIR):$$LD_LIBRARY_PATH python3 $(TESTDIR)/test_python.py + +check: + $(MAKE) clean + $(MAKE) test CFLAGS="$(CFLAGS_BASE) $(CFLAGS_HARDENING) -Werror" + +test-one: $(OBJS) $(TS_OBJ) +ifndef TEST + $(error TEST is not set. Usage: make test-one TEST=<name>) +endif + @echo "$(TEST)" | grep -qE '^[a-z0-9_]+$$' || { echo "ERROR: TEST must match [a-z0-9_]+"; exit 1; } + @mkdir -p $(BUILDDIR) + @printf '#include <stdbool.h>\nextern bool ts_test_$(TEST)(void);\nint main(void){return ts_test_$(TEST)() ? 0 : 1;}\n' > $(BUILDDIR)/stub.c + $(CC) $(CFLAGS) -DTS_IMPLEMENTATION $(BUILDDIR)/stub.c $(TESTDIR)/$(TEST).c $(OBJS) $(TS_OBJ) -o $(BUILDDIR)/test_$(TEST) $(LDLIBS) + ./$(BUILDDIR)/test_$(TEST) + +valgrind: $(TEST_BIN) + valgrind --leak-check=full --error-exitcode=1 ./$(TEST_BIN) + +sanitize: + $(MAKE) clean + $(MAKE) CFLAGS="$(CFLAGS_BASE) -O0 -g -fsanitize=address,undefined -fno-common" + +sanitize-test: + @$(CC) -print-file-name=libasan.so || { echo "ERROR: AddressSanitizer (libasan) not found. Install libasan."; exit 1; } + $(MAKE) clean + $(MAKE) test CFLAGS="$(CFLAGS_BASE) -O0 -g -fsanitize=address,undefined -fno-common" + +install: ts_generated.h lib shared + install -d $(PREFIX)/include/ts + install -m 644 ts.h $(PREFIX)/include/ts/ + install -m 644 ts_generated.h $(PREFIX)/include/ts/ + install -d $(PREFIX)/lib + install -m 644 $(LIB) $(PREFIX)/lib/ + install -m 755 $(SHLIB) $(PREFIX)/lib/libts.so.$(VERSION) + ln -sf libts.so.$(VERSION) $(PREFIX)/lib/libts.so.$(SOVERSION) + ln -sf libts.so.$(SOVERSION) $(PREFIX)/lib/libts.so + @if [ "$(shell id -u)" = "0" ]; then ldconfig 2>/dev/null || true; else echo "WARNING: run 'sudo ldconfig' to update linker cache"; fi + install -d $(PREFIX)/lib/pkgconfig + @test -f ts.pc.in || { echo "ERROR: ts.pc.in not found" && exit 1; } + sed 's|@PREFIX@|$(PREFIX)|;s|@VERSION@|$(VERSION)|' ts.pc.in > $(PREFIX)/lib/pkgconfig/ts.pc + +uninstall: + rm -f $(PREFIX)/include/ts/ts.h + rm -f $(PREFIX)/include/ts/ts_generated.h + rm -rf $(PREFIX)/include/ts + rm -f $(PREFIX)/lib/libts.a + rm -f $(PREFIX)/lib/libts.so $(PREFIX)/lib/libts.so.$(SOVERSION) $(PREFIX)/lib/libts.so.$(VERSION) + rm -f $(PREFIX)/lib/pkgconfig/ts.pc + @if [ "$(shell id -u)" = "0" ]; then ldconfig 2>/dev/null || true; fi + +clean: + rm -rf $(BUILDDIR) + +distclean: clean + rm -f $(SRCDIR)/ts_flat_generated.c + rm -f $(TESTDIR)/fuzz_table_generated.c + rm -f ts_generated.h + rm -f python/ts/__init__.py + +DIST_NAME = ts-$(VERSION) +DIST_FILES = Makefile VERSION ts.pc.in LICENSE README.md ts.h ts_generated.h ts.c main.c .gitignore +DIST_LIB = $(filter-out $(SRCDIR)/ts_flat_generated.c $(SRCDIR)/ts_registry_generated.c $(SRCDIR)/test.c,$(wildcard $(SRCDIR)/*.c)) +DIST_SCRIPTS = $(wildcard $(SCRIPTDIR)/*.py) +DIST_TEST = $(filter-out $(TESTDIR)/fuzz_table_generated.c,$(wildcard $(TESTDIR)/*.c)) $(wildcard $(TESTDIR)/*.py) + +dist: + $(MAKE) clean + $(MAKE) lib + @test -s ts_generated.h || { echo "ERROR: ts_generated.h not found or empty" && exit 1; } + rm -rf $(DIST_NAME) + mkdir -p $(DIST_NAME)/lib $(DIST_NAME)/test $(DIST_NAME)/scripts + cp $(DIST_FILES) $(DIST_NAME)/ + cp $(DIST_LIB) $(DIST_NAME)/lib/ + cp $(DIST_SCRIPTS) $(DIST_NAME)/scripts/ + cp $(DIST_TEST) $(DIST_NAME)/test/ + tar czf $(DIST_NAME).tar.gz $(DIST_NAME) + rm -rf $(DIST_NAME) + @echo "Created $(DIST_NAME).tar.gz" + +-include $(DEPS) +-include $(TEST_OBJS:.o=.d) diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe8a3e2 --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +# ts — Time Series Analysis Library + +C11 library of 354 financial and mathematical time-series indicators. + +The library provides 354 indicators covering moving averages, oscillators, +volatility measures, statistical functions, and more. See the source files +in `lib/` for the complete list. + +**Requirements:** Any C11-compatible compiler, libm, Python 3 (for code generation). + +**Platform:** Little-endian only (x86, x86_64, AArch64). Big-endian is not supported. + +**Note:** Generated files (`ts_generated.h`, `lib/ts_flat_generated.c`, +`lib/ts_registry_generated.c`) are regenerated automatically by the Makefile. +If you modify indicator sources, run `make` to regenerate. + +## Quick Start + +```bash +make # Build example binary +make test # Run all tests +make lib # Build static library (libts.a) +make shared # Build shared library (libts.so) +make install # Install to /usr/local (PREFIX overrideable) +make check # Clean build with -Werror + tests +make sanitize-test # ASAN + UBSan +make uninstall # Remove installed files +``` + +## Usage + +```c +#define TS_IMPLEMENTATION +#include "ts.h" + +// Types: fp_t == double, ts_uint_t == uint64_t + +// Simple moving average (period=10) +uint_t period = 10; +fp_t input[100]; +ts_fill(input, 100, 0, 100); // fill with 0..100 +fp_t output[100]; +fp_t * const y[1] = { output }; +fp_t const * const x[1] = { input }; + +ts_constant_t c; +memset(&c, 0, sizeof(c)); +*(uint_t *)c.data = period; + +int ret = ts_average(y, 1, x, 1, 100, &c); +if (ret == -1) { /* error: invalid params or insufficient data */ } + +// Valid output starts at index ts_average_offset(&c) == period - 1 +uint_t offset = ts_average_offset(&c); +for (uint_t i = offset; i < 100; i++) { + // output[i] contains valid SMA values +} +``` + +## API + +Every indicator shares the same signature: + +```c +int ts_<name>( + fp_t * const *y, // output arrays + uint_t y_n, // number of output arrays + fp_t const * const *x, // input arrays + uint_t x_n, // number of input arrays + uint_t n, // number of data points + ts_constant_t const *c // parameters (opaque buffer) +); +``` + +**Return values:** `0` on success, `-1` on error (invalid parameters, insufficient data). + +**Offsets:** Each indicator has a `ts_<name>_offset(c)` function. Output values before the offset are undefined. Valid output starts at index `offset`. + +**Parameters:** Pass a struct matching the indicator's expected layout. For simple indicators, a raw `uint_t` period works: +```c +uint_t k = 14; +ts_rsi(y, 1, x, 1, n, &k); // k is read as ts_rsi_params_t{k} +``` + +**OHLCV access:** Multi-input functions use macros: +- `ts_o(x, i)` — open +- `ts_h(x, i)` — high +- `ts_l(x, i)` — low +- `ts_c(x, i)` — close +- `ts_v(x, i)` — volume + +## Thread Safety + +The library is **thread-safe for concurrent reads** but has the following constraints: + +- Each indicator function is stateless and does not use global mutable state. +- Multiple threads can safely call different indicator functions simultaneously. +- The same indicator function can be called from multiple threads as long as each thread uses its own `x`, `y`, and `c` buffers. +- `ts_constant()` allocates heap memory — `ts_constant_free()` must be called by the owning thread. +- The function registry (`ts_registry_get`, `ts_fn_name_get`) returns pointers to static read-only data and is thread-safe. + +## Building + +| Target | Description | +|--------|-------------| +| `make` | Build example binary | +| `make lib` | Static library | +| `make shared` | Shared library | +| `make test` | Run test suite | +| `make test-one TEST=rsi` | Run single test | +| `make check` | Clean build with `-Werror` + tests | +| `make valgrind` | Memory leak check | +| `make sanitize-test` | ASAN + UBSan (requires libasan) | +| `make clean` | Remove build artifacts | +| `make distclean` | Remove build + generated files | + +## Install + +```bash +make install # /usr/local +make install PREFIX=/opt/ts # custom prefix +``` + +Installs header, static library, shared library, and pkg-config file. Use with: + +```bash +pkg-config --cflags --libs ts +``` + +## License + +MIT — Copyright (c) 2025-2026 Ethan Werner @@ -0,0 +1 @@ +0.1.0 diff --git a/lib/absolute_value.c b/lib/absolute_value.c new file mode 100644 index 0000000..10714b0 --- /dev/null +++ b/lib/absolute_value.c @@ -0,0 +1,40 @@ +// Absolute Value +// +// absolute_value - abs +// +// y[t] = abs( x[t] ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_absolute_value_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_absolute_value_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_absolute_value( + 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 +) +{ + TS_PROLOGUE( ts_absolute_value_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fabs( x[0][i] ); + } + return 0; +} + diff --git a/lib/acceleration_bands.c b/lib/acceleration_bands.c new file mode 100644 index 0000000..776b990 --- /dev/null +++ b/lib/acceleration_bands.c @@ -0,0 +1,81 @@ +// Acceleration Bands +// +// acceleration_bands - acc_bands +// +// Upper: SMA of highest high over period k +// Middle: SMA of typical price over period k +// Lower: SMA of lowest low over period k +// +// Inputs: 4 (OHLCV) +// Outputs: 3 (Upper, Middle, Lower) +// Constants: 1 (period) + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_acceleration_bands_params_t; + +ts_parameter_t const ts_acceleration_bands_p = { .n_inputs = 4, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_acceleration_bands_params_t) }; + +uint_t ts_acceleration_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_acceleration_bands_params_t *)c)->k - 1 : 0 ); +} + +int ts_acceleration_bands( + 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 +) +{ + TS_PROLOGUE( ts_acceleration_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_acceleration_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t *hh_r = malloc( n * sizeof( fp_t ) ); + fp_t *ll_r = malloc( n * sizeof( fp_t ) ); + fp_t *tp = malloc( n * sizeof( fp_t ) ); + if ( !hh_r || !ll_r || !tp ) { + free( hh_r ); free( ll_r ); free( tp ); + return -1; + } + + for ( uint_t i = 0; i < n; ++i ) + { + tp[i] = ( ts_h( x, i ) + ts_l( x, i ) + ts_c( x, i ) ) / 3.0; + fp_t hh = ts_h( x, i ); + fp_t ll = ts_l( x, i ); + for ( uint_t j = 1; j < k && i >= j; ++j ) + { + if ( ts_h( x, i - j ) > hh ) hh = ts_h( x, i - j ); + if ( ts_l( x, i - j ) < ll ) ll = ts_l( x, i - j ); + } + hh_r[i] = hh; + ll_r[i] = ll; + } + + for ( uint_t j = 0; j < n && j < k - 1; ++j ) { y[0][j] = 0; y[1][j] = 0; y[2][j] = 0; } + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum_hh = 0, sum_ll = 0, sum_tp = 0; + for ( uint_t j = 0; j < k; ++j ) + { + sum_hh += hh_r[i - j]; + sum_ll += ll_r[i - j]; + sum_tp += tp[i - j]; + } + y[0][i] = sum_hh / (fp_t)k; + y[1][i] = sum_tp / (fp_t)k; + y[2][i] = sum_ll / (fp_t)k; + } + + free( hh_r ); free( ll_r ); free( tp ); + return 0; +} + + + diff --git a/lib/acosh.c b/lib/acosh.c new file mode 100644 index 0000000..2335168 --- /dev/null +++ b/lib/acosh.c @@ -0,0 +1,41 @@ +// Hyperbolic Arccosine +// +// acosh - inverse hyperbolic cosine +// +// y[t] = acosh( x[t] ) if x[t] >= 1 +// y[t] = NAN if x[t] < 1 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_acosh_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_acosh_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_acosh( + 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 +) +{ + TS_PROLOGUE( ts_acosh_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + y[0][i] = v >= 1.0 ? acosh( v ) : (fp_t)NAN; + } + return 0; +} + diff --git a/lib/ad.c b/lib/ad.c new file mode 100644 index 0000000..fd0e40e --- /dev/null +++ b/lib/ad.c @@ -0,0 +1,48 @@ +// Accumulation/Distribution Line +// +// ad - A/D +// +// Cumulative money flow: MF = ((C-L) - (H-C)) / (H-L) * V, AD = SUM(MF) +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (AD Line) +// Constants: 0 + +#include "ts.h" + + +ts_parameter_t const ts_ad_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_ad_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ad( + 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 +) +{ + TS_PROLOGUE( ts_ad_p ); + (void)c; + if( n == 0 ) return -1; + + fp_t hl0 = ts_h( x, 0 ) - ts_l( x, 0 ); + y[0][0] = hl0 != 0 + ? ( ( ts_c( x, 0 ) - ts_l( x, 0 ) ) - ( ts_h( x, 0 ) - ts_c( x, 0 ) ) ) / hl0 * ts_v( x, 0 ) + : 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hl = ts_h( x, i ) - ts_l( x, i ); + fp_t mf = hl != 0 + ? ( ( ts_c( x, i ) - ts_l( x, i ) ) - ( ts_h( x, i ) - ts_c( x, i ) ) ) / hl * ts_v( x, i ) + : 0; + y[0][i] = y[0][i - 1] + mf; + } + + return 0; +} + diff --git a/lib/ad_oscillator.c b/lib/ad_oscillator.c new file mode 100644 index 0000000..5021140 --- /dev/null +++ b/lib/ad_oscillator.c @@ -0,0 +1,67 @@ +// Accumulation/Distribution Oscillator +// +// ad_oscillator - ADX +// +// Difference between fast and slow EMA of the A/D line +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (Oscillator) +// Constants: 2 (fast period, slow period) + +#include "ts.h" + +typedef struct { + fp_t fast; + fp_t slow; +} ts_ad_oscillator_params_t; + +ts_parameter_t const ts_ad_oscillator_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_ad_oscillator_params_t) }; + +uint_t ts_ad_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ad_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_ad_oscillator_p ); + if ( !c ) return -1; + if( n == 0 ) return -1; + + fp_t fast = ((ts_ad_oscillator_params_t *)c)->fast; + fp_t slow = ((ts_ad_oscillator_params_t *)c)->slow; + if (fast == 0 || slow == 0) return -1; + fp_t a_fast = 1.0 / fast; + fp_t a_slow = 1.0 / slow; + + fp_t hl = ts_h( x, 0 ) - ts_l( x, 0 ); + fp_t mf = hl != 0 + ? ( ( ts_c( x, 0 ) - ts_l( x, 0 ) ) - ( ts_h( x, 0 ) - ts_c( x, 0 ) ) ) / hl * ts_v( x, 0 ) + : 0; + fp_t ad = mf; + fp_t ema_f = ad; + fp_t ema_s = ad; + y[0][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + hl = ts_h( x, i ) - ts_l( x, i ); + mf = hl != 0 + ? ( ( ts_c( x, i ) - ts_l( x, i ) ) - ( ts_h( x, i ) - ts_c( x, i ) ) ) / hl * ts_v( x, i ) + : 0; + ad += mf; + ema_f = ema_f + a_fast * ( ad - ema_f ); + ema_s = ema_s + a_slow * ( ad - ema_s ); + y[0][i] = ema_f - ema_s; + } + + return 0; +} + + + diff --git a/lib/addition.c b/lib/addition.c new file mode 100644 index 0000000..f5d0deb --- /dev/null +++ b/lib/addition.c @@ -0,0 +1,41 @@ +// Addition +// +// addition - add +// +// y[t] = x[0][t] + x[1][t] +// +// X_n - 2 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_addition_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_addition_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_addition( + 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 +) +{ + TS_PROLOGUE( ts_addition_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] + x[1][i]; + } + return 0; +} + + + diff --git a/lib/adx.c b/lib/adx.c new file mode 100644 index 0000000..505732b --- /dev/null +++ b/lib/adx.c @@ -0,0 +1,84 @@ +// Adx +// +// adx +// +// Time series function +// +// See implementation +// +// Inputs: 4 +// Outputs: 4 +// Constants: 1 + + +#include <math.h> +#include <stdint.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_adx_params_t; + +ts_parameter_t const ts_adx_p = { .n_inputs = 4, .n_outputs = 4, .n_constants = 1, .params_size = sizeof(ts_adx_params_t) }; + +uint_t ts_adx_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_adx_params_t *)c)->k + 1 : 0 ); +} + +int ts_adx( + 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 +) +{ + TS_PROLOGUE( ts_adx_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_adx_params_t *)c)->k; + if (k == 0) return -1; + + fp_t ema_plus = 0, ema_minus = 0, ema_tr = 0; + fp_t prev_dx = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hdmd = ts_h( x, i ) - ts_h( x, i - 1 ); + fp_t ldml = ts_l( x, i - 1 ) - ts_l( x, i ); + + fp_t plus_dm = hdmd > ldml && hdmd > 0 ? hdmd : 0; + fp_t minus_dm = ldml > hdmd && ldml > 0 ? ldml : 0; + + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), + fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), + fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + + ema_plus = i == 1 ? plus_dm + : ema_plus + ( 1.0 / k ) * ( plus_dm - ema_plus ); + ema_minus = i == 1 ? minus_dm + : ema_minus + ( 1.0 / k ) * ( minus_dm - ema_minus ); + ema_tr = i == 1 ? tr + : ema_tr + ( 1.0 / k ) * ( tr - ema_tr ); + + if( i > k ) + { + fp_t di_plus = ema_tr != 0 ? 100.0 * ema_plus / ema_tr : 0; + fp_t di_minus = ema_tr != 0 ? 100.0 * ema_minus / ema_tr : 0; + + y[0][i] = di_plus; + y[1][i] = di_minus; + + fp_t sum = di_plus + di_minus; + fp_t dx = sum != 0 ? 100.0 * fabs( di_plus - di_minus ) / sum : 0; + + y[2][i] = dx; + y[3][i] = i == k + 1 ? dx + : prev_dx + ( 1.0 / k ) * ( dx - prev_dx ); + prev_dx = y[3][i]; + } + } + + return 0; +} + diff --git a/lib/alligator.c b/lib/alligator.c new file mode 100644 index 0000000..81450b0 --- /dev/null +++ b/lib/alligator.c @@ -0,0 +1,70 @@ +// Alligator Indicator +// +// William Elder's Alligator uses three SMAs shifted forward in time: +// Jaw: SMA(13) shifted 8 bars +// Teeth: SMA(8) shifted 5 bars +// Lips: SMA(5) shifted 3 bars +// +// X_n - 1 +// Y_n - 3 +// C_n - 0 + +#include "ts.h" + + +ts_parameter_t const ts_alligator_p = { .n_inputs = 1, .n_outputs = 3, .n_constants = 0, .params_size = 0 }; + +uint_t ts_alligator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 12 ); +} + +int ts_alligator( + 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 +) +{ + TS_PROLOGUE( ts_alligator_p ); + (void)c; + if( n == 0 ) return -1; + /* Jaw: SMA(13) shifted 8 positions */ + /* Teeth: SMA(8) shifted 5 positions */ + /* Lips: SMA(5) shifted 3 positions */ + + /* Jaw: SMA13 of x[i-8..i+4], displayed at i */ + for (uint_t i = 8; i - 8 + 13 <= n; ++i) + { + fp_t sum = 0; + for (uint_t j = 0; j < 13; ++j) + { + sum += x[0][i - 8 + j]; + } + y[0][i] = sum / 13; + } + + /* Teeth: SMA8 of x[i-5..i+2], displayed at i */ + for (uint_t i = 5; i - 5 + 8 <= n; ++i) + { + fp_t sum = 0; + for (uint_t j = 0; j < 8; ++j) + { + sum += x[0][i - 5 + j]; + } + y[1][i] = sum / 8; + } + + /* Lips: SMA5 of x[i-3..i+1], displayed at i */ + for (uint_t i = 3; i - 3 + 5 <= n; ++i) + { + fp_t sum = 0; + for (uint_t j = 0; j < 5; ++j) + { + sum += x[0][i - 3 + j]; + } + y[2][i] = sum / 5; + } + return 0; +} + diff --git a/lib/angle_deg_to_rad.c b/lib/angle_deg_to_rad.c new file mode 100644 index 0000000..e6929fa --- /dev/null +++ b/lib/angle_deg_to_rad.c @@ -0,0 +1,40 @@ +// Angle Degrees to Radians +// +// angle_deg_to_rad - convert degrees to radians +// +// y[t] = x[0][t] * PI / 180.0 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include "ts.h" + + + +ts_parameter_t const ts_angle_deg_to_rad_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_angle_deg_to_rad_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_angle_deg_to_rad( + 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 +) +{ + TS_PROLOGUE( ts_angle_deg_to_rad_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] * PI / 180.0; + } + return 0; +} + + + diff --git a/lib/angle_normalize.c b/lib/angle_normalize.c new file mode 100644 index 0000000..8784f2c --- /dev/null +++ b/lib/angle_normalize.c @@ -0,0 +1,46 @@ +// Angle Normalize +// +// angle_normalize - normalize angle to [-PI, PI] +// +// y[t] = fmod( x[t], 2*PI ), adjusted to [-PI, PI] +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_angle_normalize_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_angle_normalize_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_angle_normalize( + 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 +) +{ + TS_PROLOGUE( ts_angle_normalize_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t r = fmod( x[0][i], 2.0 * PI ); + if( r > PI ) + r -= 2.0 * PI; + if( r < -PI ) + r += 2.0 * PI; + y[0][i] = r; + } + return 0; +} + + + diff --git a/lib/angle_rad_to_deg.c b/lib/angle_rad_to_deg.c new file mode 100644 index 0000000..124da05 --- /dev/null +++ b/lib/angle_rad_to_deg.c @@ -0,0 +1,40 @@ +// Angle Radians to Degrees +// +// angle_rad_to_deg - convert radians to degrees +// +// y[t] = x[0][t] * 180.0 / PI +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include "ts.h" + + + +ts_parameter_t const ts_angle_rad_to_deg_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_angle_rad_to_deg_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_angle_rad_to_deg( + 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 +) +{ + TS_PROLOGUE( ts_angle_rad_to_deg_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] * 180.0 / PI; + } + return 0; +} + + + diff --git a/lib/annualized_historical_volatility.c b/lib/annualized_historical_volatility.c new file mode 100644 index 0000000..c0c88cf --- /dev/null +++ b/lib/annualized_historical_volatility.c @@ -0,0 +1,61 @@ +// Annualized Historical Volatility +// +// annualized_historical_volatility - ahv +// +// y[t] = stddev(log(x[t-k..t])) * sqrt(252) +// +// X_n - 1 +// Y_n - 1 +// C_n - 1 + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_annualized_historical_volatility_params_t; + +ts_parameter_t const ts_annualized_historical_volatility_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_annualized_historical_volatility_params_t) }; + +uint_t ts_annualized_historical_volatility_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_annualized_historical_volatility_params_t *)c)->k : 0 ); +} + +int ts_annualized_historical_volatility( + 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 +) +{ + TS_PROLOGUE( ts_annualized_historical_volatility_p ); + if ( !c ) return -1; + uint_t k = ((ts_annualized_historical_volatility_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + /* Compute log returns, then stddev, then annualize */ + for (uint_t i = k; i < n; ++i) + { + fp_t sum = 0; + fp_t sum2 = 0; + uint_t count = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + { + if (x[0][j - 1] <= 0 || x[0][j] <= 0) continue; + fp_t ret = log(x[0][j] / x[0][j - 1]); + sum += ret; + sum2 += ret * ret; + count++; + } + if (count == 0) { y[0][i] = 0; continue; } + fp_t mean = sum / count; + fp_t variance = sum2 / count - mean * mean; + if (variance < 0) variance = 0; + y[0][i] = sqrt(variance) * sqrt(252.0); + } + return 0; +} + + + diff --git a/lib/apo.c b/lib/apo.c new file mode 100644 index 0000000..f888162 --- /dev/null +++ b/lib/apo.c @@ -0,0 +1,68 @@ +// Absolute Price Oscillator +// +// apo - APO +// +// Difference between fast and slow EMA: APO = EMA(fast) - EMA(slow) +// +// Inputs: 1 +// Outputs: 1 (Oscillator) +// Constants: 2 (fast period, slow period) + +#include "ts.h" + +typedef struct { + fp_t fast_k; + fp_t slow_k; +} ts_apo_params_t; + +ts_parameter_t const ts_apo_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_apo_params_t) }; + +uint_t ts_apo_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)((ts_apo_params_t *)c)->slow_k - 1 : 0 ); +} + +int ts_apo( + 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 +) +{ + TS_PROLOGUE( ts_apo_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t fast_k = ((ts_apo_params_t *)c)->fast_k; + fp_t slow_k = ((ts_apo_params_t *)c)->slow_k; + if (fast_k == 0 || slow_k == 0) return -1; + + fp_t fast_a = 1.0 / fast_k; + fp_t slow_a = 1.0 / slow_k; + + uint_t slow_offset = (uint_t)(slow_k - 1); + if (slow_offset >= n) return -1; + + fp_t fast_ema = x[0][0]; + fp_t slow_ema = x[0][0]; + + for( uint_t i = 1; i <= slow_offset; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + } + + y[0][slow_offset] = fast_ema - slow_ema; + + for( uint_t i = slow_offset + 1; i < n; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + + y[0][i] = fast_ema - slow_ema; + } + + return 0; +} + + diff --git a/lib/aroon.c b/lib/aroon.c new file mode 100644 index 0000000..539e97b --- /dev/null +++ b/lib/aroon.c @@ -0,0 +1,67 @@ +// Aroon +// +// aroon - ar +// +// Measures the time since the highest high and lowest low in a lookback window. +// Aroon Up = ((period - days since highest high) / period) * 100 +// Aroon Down = ((period - days since lowest low) / period) * 100 +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Aroon Up, Aroon Down) +// Constants: 1 (period) + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_aroon_params_t; + +ts_parameter_t const ts_aroon_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_aroon_params_t) }; + +uint_t ts_aroon_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_aroon_params_t *)c)->k - 1 : 0 ); +} + +int ts_aroon( + 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 +) +{ + TS_PROLOGUE( ts_aroon_p ); + if ( !c ) return -1; + uint_t k = ((ts_aroon_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t highest_high = ts_h( x, i - k + 1 ); + uint_t age_highest = 0; + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_h( x, j ) > highest_high ) + { + highest_high = ts_h( x, j ); + age_highest = i - j; + } + } + + fp_t lowest_low = ts_l( x, i - k + 1 ); + uint_t age_lowest = 0; + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_l( x, j ) < lowest_low ) + { + lowest_low = ts_l( x, j ); + age_lowest = i - j; + } + } + + y[0][i] = ( ( fp_t )( k - age_highest ) / ( fp_t )k ) * 100.0; + y[1][i] = ( ( fp_t )( k - age_lowest ) / ( fp_t )k ) * 100.0; + } + return 0; +} + diff --git a/lib/aroon_oscillator.c b/lib/aroon_oscillator.c new file mode 100644 index 0000000..db9fe9d --- /dev/null +++ b/lib/aroon_oscillator.c @@ -0,0 +1,71 @@ +// Aroon Oscillator +// +// aroon_oscillator - aro +// +// The difference between Aroon Up and Aroon Down. +// Aroon Oscillator = Aroon Up - Aroon Down +// +// Inputs: 4 (OHLC) +// Outputs: 1 (Oscillator) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_aroon_oscillator_params_t; + +ts_parameter_t const ts_aroon_oscillator_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_aroon_oscillator_params_t) }; + +uint_t ts_aroon_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_aroon_oscillator_params_t *)c)->k - 1 : 0 ); +} + +int ts_aroon_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_aroon_oscillator_p ); + if ( !c ) return -1; + uint_t k = ((ts_aroon_oscillator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t highest_high = ts_h( x, i - k + 1 ); + uint_t age_highest = 0; + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_h( x, j ) > highest_high ) + { + highest_high = ts_h( x, j ); + age_highest = i - j; + } + } + + fp_t lowest_low = ts_l( x, i - k + 1 ); + uint_t age_lowest = 0; + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_l( x, j ) < lowest_low ) + { + lowest_low = ts_l( x, j ); + age_lowest = i - j; + } + } + + fp_t aroon_up = ( ( fp_t )( k - age_highest ) / ( fp_t )k ) * 100.0; + fp_t aroon_dn = ( ( fp_t )( k - age_lowest ) / ( fp_t )k ) * 100.0; + + y[0][i] = aroon_up - aroon_dn; + } + return 0; +} + + + diff --git a/lib/asi.c b/lib/asi.c new file mode 100644 index 0000000..ac4225b --- /dev/null +++ b/lib/asi.c @@ -0,0 +1,53 @@ +// Accumulation Swing Index +// +// asi - ASI +// +// Cumulative swing indicator using OHLC data to measure buying/selling pressure +// +// Inputs: 4 (OHLC) +// Outputs: 1 (ASI) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_asi_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_asi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_asi( + 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 +) +{ + TS_PROLOGUE( ts_asi_p ); + (void)c; + if( n == 0 ) return -1; + + y[0][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t term1 = ts_c( x, i ) - ts_c( x, i - 1 ); + fp_t term2 = 0.5 * ( ts_o( x, i ) - ts_o( x, i - 1 ) ); + fp_t term3 = 0.25 * ( ts_c( x, i ) - ts_o( x, i ) + ts_o( x, i - 1 ) - ts_c( x, i - 1 ) ); + fp_t denom = fmax( ts_h( x, i ) - ts_l( x, i - 1 ), + fmax( ts_h( x, i - 1 ) - ts_l( x, i ), + ts_h( x, i - 1 ) - ts_l( x, i - 1 ) ) ); + fp_t component2 = denom != 0 ? ( term1 - term2 + term3 ) / denom * ( ts_h( x, i ) - ts_l( x, i ) ) : 0; + fp_t half_denom = 0.5 * denom; + fp_t component3 = half_denom != 0 ? ( term1 - term2 + term3 ) / half_denom * 0.5 : 0; + + y[0][i] = y[0][i - 1] + component2 + component3; + } + + return 0; +} + + diff --git a/lib/asinh.c b/lib/asinh.c new file mode 100644 index 0000000..992dbcc --- /dev/null +++ b/lib/asinh.c @@ -0,0 +1,42 @@ +// Hyperbolic Arcsine +// +// asinh - inverse hyperbolic sine +// +// y[t] = asinh( x[t] ) +// = ln( x + √( x^2 + 1 ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_asinh_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_asinh_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_asinh( + 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 +) +{ + TS_PROLOGUE( ts_asinh_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = asinh( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/atan2.c b/lib/atan2.c new file mode 100644 index 0000000..5242cb7 --- /dev/null +++ b/lib/atan2.c @@ -0,0 +1,42 @@ +// Atan2 +// +// atan2 - four-quadrant arctangent +// +// y[t] = atan2( x[0][t], x[1][t] ) +// +// X_n - 2 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_atan2_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_atan2_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_atan2( + 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 +) +{ + TS_PROLOGUE( ts_atan2_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = atan2( x[0][i], x[1][i] ); + } + return 0; +} + + + diff --git a/lib/atanh.c b/lib/atanh.c new file mode 100644 index 0000000..06896df --- /dev/null +++ b/lib/atanh.c @@ -0,0 +1,50 @@ +// Hyperbolic Arctangent +// +// atanh - inverse hyperbolic tangent +// +// y[t] = atanh( x[t] ) if |x[t]| < 1 +// y[t] = +INFINITY if x[t] == 1 +// y[t] = -INFINITY if x[t] == -1 +// y[t] = NAN if |x[t]| > 1 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_atanh_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_atanh_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_atanh( + 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 +) +{ + TS_PROLOGUE( ts_atanh_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + if ( v > -1.0 && v < 1.0 ) + y[0][i] = atanh( v ); + else if ( v == 1.0 ) + y[0][i] = INFINITY; + else if ( v == -1.0 ) + y[0][i] = -INFINITY; + else + y[0][i] = (fp_t)NAN; + } + return 0; +} + diff --git a/lib/atr.c b/lib/atr.c new file mode 100644 index 0000000..7c9f06f --- /dev/null +++ b/lib/atr.c @@ -0,0 +1,73 @@ +// Average True Range +// +// atr - ATR +// +// Welles Wilder's Average True Range. True Range is the maximum of: +// (1) High - Low +// (2) |High - Previous Close| +// (3) |Low - Previous Close| +// The first ATR is the SMA of the first k TR values; subsequent values +// use Wilder's smoothing: ATR = (prev_ATR * (k-1) + TR) / k +// +// Inputs: 4 (OHLC) +// Outputs: 1 (ATR) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_atr_params_t; + +ts_parameter_t const ts_atr_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_atr_params_t) }; + + +uint_t ts_atr_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_atr_params_t *)c)->k : 0 ); +} + +int ts_atr( + 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 +) +{ + TS_PROLOGUE( ts_atr_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_atr_params_t *)c)->k; + if (k == 0) return -1; + if (n < 2) return -1; + + fp_t tr_sum = 0; + + /* Cumulative SMA for first k TR values */ + for( uint_t i = 1; i < k && i < n; ++i ) + { + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + tr_sum += tr; + y[0][i] = tr_sum / (fp_t)i; + } + + /* At bar k, use SMA of first k TR values */ + if( k < n ) + { + fp_t tr = fmax( ts_h( x, k ) - ts_l( x, k ), fmax( fabs( ts_h( x, k ) - ts_c( x, k - 1 ) ), fabs( ts_l( x, k ) - ts_c( x, k - 1 ) ) ) ); + tr_sum += tr; + y[0][k] = tr_sum / (fp_t)k; + } + + /* Wilder's smoothing from bar k+1 */ + for( uint_t i = k + 1; i < n; ++i ) + { + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + y[0][i] = ( y[0][i - 1] * ( k - 1 ) + tr ) / k; + } + return 0; +} + + + diff --git a/lib/atr_bands.c b/lib/atr_bands.c new file mode 100644 index 0000000..73b7d44 --- /dev/null +++ b/lib/atr_bands.c @@ -0,0 +1,87 @@ +// ATR Bands +// +// atr_bands - ATR bands +// +// Upper: SMA(Close) + mult * ATR +// Middle: SMA(Close) +// Lower: SMA(Close) - mult * ATR +// +// Inputs: 4 (OHLC) +// Outputs: 3 (Upper, Middle, Lower) +// Constants: 2 (period, multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t mult; +} ts_atr_bands_params_t; + +ts_parameter_t const ts_atr_bands_p = { .n_inputs = 4, .n_outputs = 3, .n_constants = 2, .params_size = sizeof(ts_atr_bands_params_t) }; + + +uint_t ts_atr_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_atr_bands_params_t *)c)->k : 0 ); +} + +int ts_atr_bands( + 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 +) +{ + TS_PROLOGUE( ts_atr_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_atr_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + if (n < 2) return -1; + + fp_t mult = ((ts_atr_bands_params_t *)c)->mult; + + fp_t tr_sum = 0; + fp_t atr = 0; + + // Accumulate ATR from i=1, but only write output from i=k-1 (when SMA window is valid) + for( uint_t i = 1; i < n; ++i ) + { + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + + if( i < k ) + { + tr_sum += tr; + atr = tr_sum / (fp_t)i; + } + else if( i == k ) + { + tr_sum += tr; + atr = tr_sum / (fp_t)k; + } + else + { + atr = ( atr * ( k - 1 ) + tr ) / k; + } + + // Only write output when SMA window is fully within bounds (i >= k-1) + if( i >= k - 1 ) + { + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + sum += ts_c( x, i - k + 1 + j ); + } + fp_t mean = sum / k; + + y[0][i] = mean + mult * atr; + y[1][i] = mean; + y[2][i] = mean - mult * atr; + } + } + return 0; +} + + + diff --git a/lib/atr_trailing_stops.c b/lib/atr_trailing_stops.c new file mode 100644 index 0000000..2c7cd73 --- /dev/null +++ b/lib/atr_trailing_stops.c @@ -0,0 +1,88 @@ +// ATR Trailing Stops +// +// atr_trailing_stops - ATR stops +// +// Upper stop: max(prev, (H+L)/2 + mult*ATR) +// Lower stop: min(prev, (H+L)/2 - mult*ATR) +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Upper Stop, Lower Stop) +// Constants: 2 (period, multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t mult; +} ts_atr_trailing_stops_params_t; + +ts_parameter_t const ts_atr_trailing_stops_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_atr_trailing_stops_params_t) }; + + +uint_t ts_atr_trailing_stops_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_atr_trailing_stops_params_t *)c)->k : 0 ); +} + +int ts_atr_trailing_stops( + 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 +) +{ + TS_PROLOGUE( ts_atr_trailing_stops_p ); + if ( !c ) return -1; + uint_t k = ((ts_atr_trailing_stops_params_t *)c)->k; + if (k == 0) return -1; + if (n < k + 1) return -1; + + fp_t mult = ((ts_atr_trailing_stops_params_t *)c)->mult; + + fp_t tr_sum = 0; + fp_t atr = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + + if( i < k ) + { + tr_sum += tr; + atr = tr_sum / (fp_t)i; + } + else if( i == k ) + { + tr_sum += tr; + atr = tr_sum / (fp_t)k; + } + else + { + atr = ( atr * ( k - 1 ) + tr ) / k; + } + + if( i >= k ) + { + fp_t mid = ( ts_h( x, i ) + ts_l( x, i ) ) / 2.0; + fp_t calc_upper = mid + mult * atr; + fp_t calc_lower = mid - mult * atr; + + if( i == k ) + { + y[0][i] = calc_upper; + y[1][i] = calc_lower; + } + else + { + y[0][i] = fmax( y[0][i - 1], calc_upper ); + y[1][i] = fmin( y[1][i - 1], calc_lower ); + } + } + } + return 0; +} + + + diff --git a/lib/average.c b/lib/average.c new file mode 100644 index 0000000..391de93 --- /dev/null +++ b/lib/average.c @@ -0,0 +1,71 @@ +// Simple Moving Average +// +// average_simple - sma +// +// Unweighted mean of previous k data points +// +// SMA_k = ( 1 / k ) * SUM[ i = 1, k ] x_i +// +// Initial Values +// +// Cumulative moving average to n = k +// +// Successive Value +// +// SMA_n = x_n-1 + ( 1 / n )( x_n - x_n-1 ) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_average_params_t; + +ts_parameter_t const ts_average_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_params_t) }; + + +uint_t ts_average_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_average_params_t *)c)->k - 1 : 0 ); +} + +int ts_average( + 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 +) +{ + TS_PROLOGUE( ts_average_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_average_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + fp_t sum = 0; + + for( ; xi < k; ++xi ) + { + sum += x[0][xi]; + } + + y[0][k - 1] = sum / k; + + for( ; xi < n; ++xi, ++yi ) + { + y[0][yi] = y[0][yi - 1] + ( x[0][xi] - x[0][xi - k] ) / k; + } + return 0; +} + + + diff --git a/lib/average_cumulative.c b/lib/average_cumulative.c new file mode 100644 index 0000000..905ccfe --- /dev/null +++ b/lib/average_cumulative.c @@ -0,0 +1,47 @@ +// Cumulative Moving Average +// +// average_cumulative - cma +// +// Average of all data to the current point +// +// y[t] = ( 1 / k ) * SUM[ t = 1, k ]( x[t] ) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_average_cumulative_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_average_cumulative_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_average_cumulative( + 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 +) +{ + TS_PROLOGUE( ts_average_cumulative_p ); + (void)c; + fp_t sum = 0; + + for( uint_t i = 0; i < n; ++i ) + { + sum += x[0][i]; + y[0][i] = sum / ( i + 1 ); + } + return 0; +} + + + diff --git a/lib/average_exponential.c b/lib/average_exponential.c new file mode 100644 index 0000000..bae9d5f --- /dev/null +++ b/lib/average_exponential.c @@ -0,0 +1,56 @@ +// Exponential Moving Average +// +// average_exponential - ema +// +// Weighted moving average with exponentially decreasing weights determined by +// the constant c +// +// y[0] = x[0] +// y[t] = a * x[t] + ( 1 - a ) * EMA[t - 1] +// a = 1 / k; +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t k; +} ts_average_exponential_params_t; + +ts_parameter_t const ts_average_exponential_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_exponential_params_t) }; + + +uint_t ts_average_exponential_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_average_exponential( + 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 +) +{ + TS_PROLOGUE( ts_average_exponential_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + fp_t k = ((ts_average_exponential_params_t *)c)->k; + if (k <= 0 || isnan(k)) return -1; + + fp_t a = 1.0 / k; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + y[0][i] = y[0][i - 1] + a * ( x[0][i] - y[0][i - 1] ); + } + return 0; +} + diff --git a/lib/average_exponential_double.c b/lib/average_exponential_double.c new file mode 100644 index 0000000..eeb890f --- /dev/null +++ b/lib/average_exponential_double.c @@ -0,0 +1,57 @@ +// Double Exponential Moving Average +// +// average_exponential_double - dema +// +// y[i] = +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t a; +} ts_average_exponential_double_params_t; + +ts_parameter_t const ts_average_exponential_double_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_exponential_double_params_t) }; + + +uint_t ts_average_exponential_double_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_average_exponential_double( + 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 +) +{ + TS_PROLOGUE( ts_average_exponential_double_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + fp_t a = ((ts_average_exponential_double_params_t *)c)->a; + if (a == 0 || n == 0) return -1; + + fp_t ema1 = x[0][0]; + fp_t ema2 = x[0][0]; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + ema1 = ema1 + a * ( x[0][i] - ema1 ); + ema2 = ema2 + a * ( ema1 - ema2 ); + + y[0][i] = 2 * ema1 - ema2; + } + return 0; +} + + + diff --git a/lib/average_exponential_triple.c b/lib/average_exponential_triple.c new file mode 100644 index 0000000..467d87f --- /dev/null +++ b/lib/average_exponential_triple.c @@ -0,0 +1,57 @@ +// Triple Exponential Moving Average +// +// average_exponential_triple - tema +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t a; +} ts_average_exponential_triple_params_t; + +ts_parameter_t const ts_average_exponential_triple_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_exponential_triple_params_t) }; + + +uint_t ts_average_exponential_triple_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_average_exponential_triple( + 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 +) +{ + TS_PROLOGUE( ts_average_exponential_triple_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + fp_t a = ((ts_average_exponential_triple_params_t *)c)->a; + if (a == 0 || n == 0) return -1; + + fp_t ema1 = x[0][0]; + fp_t ema2 = x[0][0]; + fp_t ema3 = x[0][0]; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + ema1 = ema1 + a * ( x[0][i] - ema1 ); + ema2 = ema2 + a * ( ema1 - ema2 ); + ema3 = ema3 + a * ( ema2 - ema3 ); + + y[0][i] = 3 * ema1 - 3 * ema2 + ema3; + } + return 0; +} + + + diff --git a/lib/average_price.c b/lib/average_price.c new file mode 100644 index 0000000..8c270b8 --- /dev/null +++ b/lib/average_price.c @@ -0,0 +1,39 @@ +// Average Price +// +// average_price - avg_price +// +// Average of high, low, and close: (H + L + C) / 3 +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (Average Price) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_average_price_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_average_price_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_average_price( + 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 +) +{ + TS_PROLOGUE( ts_average_price_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ( x[1][i] + x[2][i] + x[3][i] ) / 3; + } + return 0; +} + + + diff --git a/lib/average_triangle.c b/lib/average_triangle.c new file mode 100644 index 0000000..9ac952d --- /dev/null +++ b/lib/average_triangle.c @@ -0,0 +1,107 @@ +// Triangular Moving Average +// +// average_triangle - tma +// +// A moving average of moving averages +// +// y[i] = +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 +// +// 012345678901234567890123456789 +// ---------01234567890123456 +// ------------------0 + + +#include <stdlib.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_average_triangle_params_t; + +ts_parameter_t const ts_average_triangle_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_triangle_params_t) }; + + +uint_t ts_average_triangle_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? 2 * ( ((ts_average_triangle_params_t *)c)->k - 1 ) : 0 ); +} + +int ts_average_triangle( + 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 +) +{ + TS_PROLOGUE( ts_average_triangle_p ); + if ( !c ) return -1; + uint_t k = ((ts_average_triangle_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t rk2 = _ts_reciprocal( _ts_square( k ) ); + uint_t xi = 0; + uint_t yi = 2 * ( k - 1 ); + + fp_t *xsum = malloc(k * sizeof(fp_t)); + if (!xsum) return -1; + uint_t xsumi = 0; + + fp_t sum_of_xsum = 0; + + xsum[0] = 0; + + for (uint_t j = 0; j < n && j < 2 * (k - 1); ++j) y[0][j] = 0; + + fp_t last_sum = 0; + + for( ; xi < k; xi++ ) + { + xsum[xsumi] += x[0][xi]; + last_sum = xsum[xsumi]; + } + + sum_of_xsum = xsum[xsumi]; + xsumi++; + + for( ; xi < 2 * k - 1; xi++ ) + { + xsum[xsumi] = xsum[xsumi - 1] + x[0][xi] - x[0][xi - k]; + last_sum = xsum[xsumi]; + sum_of_xsum += xsum[xsumi]; + xsumi++; + } + + y[0][yi++] = sum_of_xsum * rk2; + + for( ; xi < n; ++xi, ++yi ) + { + if( xsumi == k ) + { + xsumi = 0; + sum_of_xsum -= xsum[xsumi]; + xsum[xsumi] = last_sum + x[0][xi] - x[0][xi - k]; + } + else + { + sum_of_xsum -= xsum[xsumi]; + xsum[xsumi] = last_sum + x[0][xi] - x[0][xi - k]; + } + + last_sum = xsum[xsumi]; + sum_of_xsum += xsum[xsumi]; + y[0][yi] = sum_of_xsum * rk2; + + xsumi++; + } + + free(xsum); + return 0; +} + + + diff --git a/lib/average_weighted.c b/lib/average_weighted.c new file mode 100644 index 0000000..eb99a17 --- /dev/null +++ b/lib/average_weighted.c @@ -0,0 +1,67 @@ +// Weighted Moving Average +// +// average_weighted - wma +// +// Weighted mean of previous k data points +// +// y[t] = SUM[ i = t, t - k]( x[t] * ( ) ) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_average_weighted_params_t; + +ts_parameter_t const ts_average_weighted_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_weighted_params_t) }; + + +uint_t ts_average_weighted_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_average_weighted_params_t *)c)->k - 1 : 0 ); +} + +int ts_average_weighted( + 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 +) +{ + TS_PROLOGUE( ts_average_weighted_p ); + if ( !c ) return -1; + uint_t k = ((ts_average_weighted_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + fp_t sum = 0; + fp_t wsum = 0; + + for( ; xi < k; ++xi ) + { + sum += x[0][xi]; + wsum += x[0][xi] * ( xi + 1 ); + } + + fp_t rtri = 1.0 / _ts_triangle_number( k ); + + y[0][k - 1] = wsum * rtri; + + for( ; xi < n; ++xi, ++yi ) + { + wsum = wsum + ( k * x[0][xi] ) - sum; + sum = sum + x[0][xi] - x[0][xi - k]; + y[0][yi] = wsum * rtri; + } + return 0; +} + + + diff --git a/lib/average_weighted_series.c b/lib/average_weighted_series.c new file mode 100644 index 0000000..d7e2ccb --- /dev/null +++ b/lib/average_weighted_series.c @@ -0,0 +1,78 @@ +// Average Weighted Series +// +// average_weighted_series - aws +// +// SMA of the product of two series: SMA(x0 * x1, k) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 (period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_average_weighted_series_params_t; + +ts_parameter_t const ts_average_weighted_series_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_average_weighted_series_params_t) }; + + +uint_t ts_average_weighted_series_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_average_weighted_series_params_t *)c)->k - 1 : 0 ); +} + +int ts_average_weighted_series( + 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 +) +{ + TS_PROLOGUE( ts_average_weighted_series_p ); + if ( !c ) return -1; + uint_t k = ((ts_average_weighted_series_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + fp_t *b = malloc(k * sizeof(fp_t)); + if (!b) return -1; + uint_t bi = 0; + + fp_t sum = 0; + + while( xi < k ) + { + b[bi] = x[0][xi] * x[1][xi]; + sum += b[bi]; + + ++xi; + bi = _ts_circle_increment( bi, k ); + } + + y[0][k - 1] = sum / k; + uint_t tmp; + + while( xi < n ) + { + tmp = b[bi]; + b[bi] = x[0][xi] * x[1][xi]; + + y[0][yi] = y[0][yi - 1] + ( b[bi] - tmp ) / k; + + ++xi; + ++yi; + bi = _ts_circle_increment( bi, k ); + } + + free(b); + return 0; +} + + + diff --git a/lib/awesome_oscillator.c b/lib/awesome_oscillator.c new file mode 100644 index 0000000..be1932e --- /dev/null +++ b/lib/awesome_oscillator.c @@ -0,0 +1,66 @@ +// Awesome Oscillator +// +// awesome_oscillator - AO +// +// EMA5(MP) - EMA34(MP), where MP = (H + L) / 2 +// +// Inputs: 4 (OHLC) +// Outputs: 1 (Oscillator) +// Constants: 0 + +#include "ts.h" + + +ts_parameter_t const ts_awesome_oscillator_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_awesome_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 33 ); +} + +int ts_awesome_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_awesome_oscillator_p ); + (void)c; + if (n < 34) return -1; + + uint_t fast_period = 5; + uint_t slow_period = 34; + + fp_t fast_a = 2.0 / ( ( fp_t )fast_period + 1.0 ); + fp_t slow_a = 2.0 / ( ( fp_t )slow_period + 1.0 ); + + fp_t sum_fast = 0; + fp_t sum_slow = 0; + + for ( uint_t j = 0; j < slow_period; ++j ) + { + fp_t mp = ( ts_h( x, j ) + ts_l( x, j ) ) / 2.0; + sum_slow += mp; + if ( j < fast_period ) + sum_fast += mp; + } + + fp_t ema_fast = sum_fast / ( fp_t )fast_period; + fp_t ema_slow = sum_slow / ( fp_t )slow_period; + + for ( uint_t i = 0; i < slow_period - 1; ++i ) + y[0][i] = 0; + + y[0][slow_period - 1] = ema_fast - ema_slow; + + for( uint_t i = slow_period; i < n; ++i ) + { + fp_t mp = ( ts_h( x, i ) + ts_l( x, i ) ) / 2.0; + ema_fast = ema_fast + fast_a * ( mp - ema_fast ); + ema_slow = ema_slow + slow_a * ( mp - ema_slow ); + y[0][i] = ema_fast - ema_slow; + } + return 0; +} + diff --git a/lib/balance_of_power.c b/lib/balance_of_power.c new file mode 100644 index 0000000..26f3456 --- /dev/null +++ b/lib/balance_of_power.c @@ -0,0 +1,41 @@ +// Balance of Power +// +// balance_of_power - BOP +// +// Measures relationship between buyers and sellers: (C - O) / (H - L) +// +// Inputs: 4 (OHLC) +// Outputs: 1 (BOP) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_balance_of_power_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_balance_of_power_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_balance_of_power( + 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 +) +{ + TS_PROLOGUE( ts_balance_of_power_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t hl = ts_h( x, i ) - ts_l( x, i ); + y[0][i] = hl != 0 ? ( ts_c( x, i ) - ts_o( x, i ) ) / hl : 0; + } + + return 0; +} + + + diff --git a/lib/band_width.c b/lib/band_width.c new file mode 100644 index 0000000..822d835 --- /dev/null +++ b/lib/band_width.c @@ -0,0 +1,60 @@ +// Band Width +// +// band_width - BW +// +// Normalized width of Bollinger Bands: (Upper - Lower) / SMA * 100 +// +// Inputs: 1 +// Outputs: 1 (Width %) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_band_width_params_t; + +ts_parameter_t const ts_band_width_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_band_width_params_t) }; + +uint_t ts_band_width_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_band_width_params_t *)c)->k - 1 : 0 ); +} + +int ts_band_width( + 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 +) +{ + TS_PROLOGUE( ts_band_width_p ); + if ( !c ) return -1; + uint_t k = ((ts_band_width_params_t *)c)->k; + fp_t std_mult = ((ts_band_width_params_t *)c)->std_mult; + if (k == 0 || n < k) return -1; + + for (uint_t i = k - 1; i < n; ++i) + { + fp_t sum = 0; + fp_t sum2 = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + { + sum += x[0][j]; + sum2 += x[0][j] * x[0][j]; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + if (variance < 0) variance = 0; + fp_t stddev = sqrt(variance); + fp_t upper = mean + std_mult * stddev; + fp_t lower = mean - std_mult * stddev; + y[0][i] = mean != 0 ? (upper - lower) / mean * 100 : 0; + } + return 0; +} + + + diff --git a/lib/beta_pdf.c b/lib/beta_pdf.c new file mode 100644 index 0000000..a23ed68 --- /dev/null +++ b/lib/beta_pdf.c @@ -0,0 +1,52 @@ +// Beta PDF +// +// beta_pdf - beta probability density +// +// f(x; α, β) = x^(α-1) * (1-x)^(β-1) / B(α, β), for 0 < x < 1 +// +// Inputs: 1 +// Outputs: 1 (PDF value) +// Constants: 2 (alpha, beta) + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t alpha; + fp_t beta; +} ts_beta_pdf_params_t; + +ts_parameter_t const ts_beta_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_beta_pdf_params_t) }; + +uint_t ts_beta_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_beta_pdf( + 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 +) +{ + TS_PROLOGUE( ts_beta_pdf_p ); + if ( !c ) return -1; + fp_t alpha = ((ts_beta_pdf_params_t *)c)->alpha; + fp_t beta = ((ts_beta_pdf_params_t *)c)->beta; + if (isnan(alpha) || isnan(beta) || alpha <= 0 || beta <= 0) + { + for( uint_t i = 0; i < n; ++i ) y[0][i] = 0; + return -1; + } + fp_t beta_fn = exp( lgamma( alpha ) + lgamma( beta ) - lgamma( alpha + beta ) ); + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = ( xi > 0 && xi < 1 ) ? pow( xi, alpha - 1.0 ) * pow( 1.0 - xi, beta - 1.0 ) / beta_fn : 0.0; + } + return 0; +} + + + diff --git a/lib/binomial.c b/lib/binomial.c new file mode 100644 index 0000000..9b50698 --- /dev/null +++ b/lib/binomial.c @@ -0,0 +1,58 @@ +// Binomial Coefficient +// +// binomial - C(n, k) +// +// Number of combinations: C(n, k) = n! / (k! * (n-k)!) +// +// Inputs: 2 (n, k) +// Outputs: 1 (coefficient) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_binomial_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_binomial_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_binomial( + 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 +) +{ + TS_PROLOGUE( ts_binomial_p ); + (void)c; + /* C(n, k) = n! / (k! * (n-k)!) */ + for (uint_t i = 0; i < n; ++i) + { + fp_t n_val = floor(x[0][i]); + fp_t k_val = floor(x[1][i]); + + if (k_val > n_val || k_val < 0 || n_val < 0) + { + y[0][i] = 0; + continue; + } + + /* Use symmetry: C(n,k) = C(n, n-k) */ + if (k_val > n_val - k_val) + k_val = n_val - k_val; + + fp_t result = 1; + for (uint_t j = 0; j < (uint_t)k_val; ++j) + { + result = result * (n_val - j) / (j + 1); + } + y[0][i] = result; + } + return 0; +} + + + diff --git a/lib/binomial_pdf.c b/lib/binomial_pdf.c new file mode 100644 index 0000000..8b3a442 --- /dev/null +++ b/lib/binomial_pdf.c @@ -0,0 +1,63 @@ +// Binomial PDF +// +// binomial_pdf - binomial probability density +// +// P(X=k) = C(n, k) * p^k * (1-p)^(n-k) +// +// Inputs: 1 (k values) +// Outputs: 1 (PDF value) +// Constants: 1 (n trials, p probability) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t n_trials; + fp_t p; +} ts_binomial_pdf_params_t; + +ts_parameter_t const ts_binomial_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_binomial_pdf_params_t) }; + +uint_t ts_binomial_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_binomial_pdf( + 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 +) +{ + TS_PROLOGUE( ts_binomial_pdf_p ); + if ( !c ) return -1; + uint_t n_trials = ((ts_binomial_pdf_params_t *)c)->n_trials; + fp_t p = ((ts_binomial_pdf_params_t *)c)->p; + if (p < 0.0 || p > 1.0 || isnan(p) || isinf(p)) return -1; + for( uint_t i = 0; i < n; ++i ) + { + uint_t k = (uint_t)x[0][i]; + if( k > n_trials ) + { + y[0][i] = 0.0; + } + else if( p == 0.0 ) + { + y[0][i] = k == 0 ? 1.0 : 0.0; + } + else if( p == 1.0 ) + { + y[0][i] = k == n_trials ? 1.0 : 0.0; + } + else + { + fp_t log_comb = lgamma( n_trials + 1.0 ) - lgamma( k + 1.0 ) - lgamma( n_trials - k + 1.0 ); + y[0][i] = exp( log_comb + k * log( p ) + ( n_trials - k ) * log( 1.0 - p ) ); + } + } + return 0; +} + + + diff --git a/lib/bollinger_bands.c b/lib/bollinger_bands.c new file mode 100644 index 0000000..55da054 --- /dev/null +++ b/lib/bollinger_bands.c @@ -0,0 +1,88 @@ +// Bollinger Bands +// +// bollinger_bands - BB +// +// Upper: SMA + mult * stddev +// Middle: SMA +// Lower: SMA - mult * stddev +// +// Inputs: 1 +// Outputs: 3 (Upper, Middle, Lower) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_bollinger_bands_params_t; + +ts_parameter_t const ts_bollinger_bands_p = { .n_inputs = 1, .n_outputs = 3, .n_constants = 2, .params_size = sizeof(ts_bollinger_bands_params_t) }; + + +uint_t ts_bollinger_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_bollinger_bands_params_t *)c)->k - 1 : 0 ); +} + +int ts_bollinger_bands( + 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 +) +{ + TS_PROLOGUE( ts_bollinger_bands_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_bollinger_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t std_mult = ((ts_bollinger_bands_params_t *)c)->std_mult; + if (std_mult < 0 || isnan(std_mult)) return -1; + + // Two-pass initial window for numerical stability + { + fp_t mean = 0; + for( uint_t j = 0; j < k; ++j ) + mean += x[0][j]; + mean /= (fp_t)k; + fp_t variance = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t d = x[0][j] - mean; + variance += d * d; + } + variance /= (fp_t)k; + fp_t stddev = sqrt( variance ); + y[1][k - 1] = mean + std_mult * stddev; + y[0][k - 1] = mean; + y[2][k - 1] = mean - std_mult * stddev; + } + + // Sliding window: maintain running mean, two-pass variance + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + sum += x[0][j]; + fp_t rk = 1.0 / (fp_t)k; + for( uint_t i = k; i < n; ++i ) + { + sum = sum + x[0][i] - x[0][i - k]; + fp_t mean = sum * rk; + // Two-pass variance for stability + fp_t variance = 0; + for( uint_t j = i - k + 1; j <= i; ++j ) + { + fp_t d = x[0][j] - mean; + variance += d * d; + } + variance *= rk; + fp_t stddev = sqrt( variance ); + y[1][i] = mean + std_mult * stddev; + y[0][i] = mean; + y[2][i] = mean - std_mult * stddev; + } + return 0; +} + diff --git a/lib/bollinger_lower.c b/lib/bollinger_lower.c new file mode 100644 index 0000000..05a0883 --- /dev/null +++ b/lib/bollinger_lower.c @@ -0,0 +1,60 @@ +// Bollinger Lower Band +// +// bollinger_lower - BB lower +// +// Lower band: SMA - mult * stddev +// +// Inputs: 1 +// Outputs: 1 (Lower Band) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_bollinger_lower_params_t; + +ts_parameter_t const ts_bollinger_lower_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_bollinger_lower_params_t) }; + + +uint_t ts_bollinger_lower_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_bollinger_lower_params_t *)c)->k - 1 : 0 ); +} + +int ts_bollinger_lower( + 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 +) +{ + TS_PROLOGUE( ts_bollinger_lower_p ); + if ( !c ) return -1; + uint_t k = ((ts_bollinger_lower_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t std_mult = ((ts_bollinger_lower_params_t *)c)->std_mult; + if (isnan(std_mult) || isinf(std_mult)) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0, sum2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + sum += v; + sum2 += v * v; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + fp_t stddev = variance >= 0 ? sqrt( variance ) : 0; + y[0][i] = mean - std_mult * stddev; + } + return 0; +} + + + diff --git a/lib/bollinger_pctb.c b/lib/bollinger_pctb.c new file mode 100644 index 0000000..a3425cd --- /dev/null +++ b/lib/bollinger_pctb.c @@ -0,0 +1,63 @@ +// Bollinger %b +// +// bollinger_pctb - %b +// +// Position of price relative to bands: (Price - Lower) / (Upper - Lower) +// +// Inputs: 1 +// Outputs: 1 (%b) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_bollinger_pctb_params_t; + +ts_parameter_t const ts_bollinger_pctb_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_bollinger_pctb_params_t) }; + + +uint_t ts_bollinger_pctb_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_bollinger_pctb_params_t *)c)->k - 1 : 0 ); +} + +int ts_bollinger_pctb( + 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 +) +{ + TS_PROLOGUE( ts_bollinger_pctb_p ); + if ( !c ) return -1; + uint_t k = ((ts_bollinger_pctb_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t std_mult = ((ts_bollinger_pctb_params_t *)c)->std_mult; + if (isnan(std_mult) || isinf(std_mult)) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0, sum2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + sum += v; + sum2 += v * v; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + fp_t stddev = variance >= 0 ? sqrt( variance ) : 0; + fp_t upper = mean + std_mult * stddev; + fp_t lower = mean - std_mult * stddev; + fp_t width = upper - lower; + y[0][i] = width != 0 ? ( x[0][i] - lower ) / width : 0.5; + } + return 0; +} + + + diff --git a/lib/bollinger_upper.c b/lib/bollinger_upper.c new file mode 100644 index 0000000..9294cfb --- /dev/null +++ b/lib/bollinger_upper.c @@ -0,0 +1,60 @@ +// Bollinger Upper Band +// +// bollinger_upper - BB upper +// +// Upper band: SMA + mult * stddev +// +// Inputs: 1 +// Outputs: 1 (Upper Band) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_bollinger_upper_params_t; + +ts_parameter_t const ts_bollinger_upper_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_bollinger_upper_params_t) }; + + +uint_t ts_bollinger_upper_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_bollinger_upper_params_t *)c)->k - 1 : 0 ); +} + +int ts_bollinger_upper( + 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 +) +{ + TS_PROLOGUE( ts_bollinger_upper_p ); + if ( !c ) return -1; + uint_t k = ((ts_bollinger_upper_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t std_mult = ((ts_bollinger_upper_params_t *)c)->std_mult; + if (isnan(std_mult) || isinf(std_mult)) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0, sum2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + sum += v; + sum2 += v * v; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + fp_t stddev = variance >= 0 ? sqrt( variance ) : 0; + y[0][i] = mean + std_mult * stddev; + } + return 0; +} + + + diff --git a/lib/bollinger_width.c b/lib/bollinger_width.c new file mode 100644 index 0000000..d9bca25 --- /dev/null +++ b/lib/bollinger_width.c @@ -0,0 +1,62 @@ +// Bollinger Width +// +// bollinger_width - BB width +// +// Normalized band width: (Upper - Lower) / SMA * 100 +// +// Inputs: 1 +// Outputs: 1 (Width %) +// Constants: 2 (period, std multiplier) + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t std_mult; +} ts_bollinger_width_params_t; + +ts_parameter_t const ts_bollinger_width_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_bollinger_width_params_t) }; + + +uint_t ts_bollinger_width_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_bollinger_width_params_t *)c)->k - 1 : 0 ); +} + +int ts_bollinger_width( + 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 +) +{ + TS_PROLOGUE( ts_bollinger_width_p ); + if ( !c ) return -1; + uint_t k = ((ts_bollinger_width_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t std_mult = ((ts_bollinger_width_params_t *)c)->std_mult; + if (isnan(std_mult) || isinf(std_mult)) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0, sum2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + sum += v; + sum2 += v * v; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + fp_t stddev = variance >= 0 ? sqrt( variance ) : 0; + fp_t upper = mean + std_mult * stddev; + fp_t lower = mean - std_mult * stddev; + y[0][i] = mean != 0 ? ( upper - lower ) / mean * 100.0 : 0; + } + return 0; +} + + + diff --git a/lib/candlestick_formations.c b/lib/candlestick_formations.c new file mode 100644 index 0000000..835e5d5 --- /dev/null +++ b/lib/candlestick_formations.c @@ -0,0 +1,58 @@ +// Candlestick Formations +// +// candlestick_formations - candle +// +// Detects doji, engulfing, shooting star, and hammer patterns +// +// Inputs: 4 (OHLC) +// Outputs: 1 (formation flag) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_candlestick_formations_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_candlestick_formations_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_candlestick_formations( + 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 +) +{ + TS_PROLOGUE( ts_candlestick_formations_p ); + (void)c; + if (n > 0) y[0][0] = 0; + for( uint_t i = 1; i < n; ++i ) + { + fp_t const body = ts_c( x, i ) - ts_o( x, i ); + fp_t const prev_body = ts_c( x, i - 1 ) - ts_o( x, i - 1 ); + fp_t const range = ts_h( x, i ) - ts_l( x, i ); + fp_t const body_abs = fabs( body ); + fp_t const upper_wick = ts_h( x, i ) - fmax( ts_o( x, i ), ts_c( x, i ) ); + fp_t const lower_wick = fmin( ts_o( x, i ), ts_c( x, i ) ) - ts_l( x, i ); + + y[0][i] = 0; + + if( range > 0 && body_abs / range < 0.1 ) + y[0][i] = 1; // doji + else if( prev_body > 0 && body < 0 && body_abs >= fabs( prev_body ) ) + y[0][i] = 2; // bearish engulfing + else if( prev_body < 0 && body > 0 && body_abs >= fabs( prev_body ) ) + y[0][i] = 3; // bullish engulfing + else if( upper_wick >= 2 * body_abs && body <= 0 ) + y[0][i] = 4; // shooting star + else if( lower_wick >= 2 * body_abs && body >= 0 ) + y[0][i] = 5; // hammer + } + return 0; +} + + + diff --git a/lib/catalan.c b/lib/catalan.c new file mode 100644 index 0000000..4a6ed9e --- /dev/null +++ b/lib/catalan.c @@ -0,0 +1,54 @@ +// Catalan Number +// +// catalan - C(n) +// +// nth Catalan number: C(n) = (2n)! / ((n+1)! * n!) +// +// Inputs: 1 (n) +// Outputs: 1 (Catalan number) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_catalan_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_catalan_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_catalan( + 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 +) +{ + TS_PROLOGUE( ts_catalan_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if (isnan(xv) || isinf(xv) || xv < 0 || xv != (fp_t)(uint_t)xv) { + y[0][i] = 0; + continue; + } + uint_t n_val = (uint_t)xv; + fp_t result = 1.0; + for( uint_t j = 0; j < n_val; ++j ) + { + result *= (fp_t)(j + n_val + 1); + result /= (fp_t)(j + 1); + } + result /= (fp_t)(n_val + 1); + y[0][i] = result; + } + return 0; +} + + + diff --git a/lib/cauchy_cdf.c b/lib/cauchy_cdf.c new file mode 100644 index 0000000..dadaaf3 --- /dev/null +++ b/lib/cauchy_cdf.c @@ -0,0 +1,40 @@ +// Cauchy CDF +// +// cauchy_cdf - Cauchy cumulative distribution +// +// F(x) = 0.5 + atan(x) / PI +// +// Inputs: 1 +// Outputs: 1 (CDF value) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cauchy_cdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cauchy_cdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cauchy_cdf( + 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 +) +{ + TS_PROLOGUE( ts_cauchy_cdf_p ); + (void)c; + /* CDF = 0.5 + atan(x) / PI */ + for (uint_t i = 0; i < n; ++i) + { + y[0][i] = 0.5 + atan(x[0][i]) / PI; + } + return 0; +} + + + diff --git a/lib/cauchy_pdf.c b/lib/cauchy_pdf.c new file mode 100644 index 0000000..0d427c1 --- /dev/null +++ b/lib/cauchy_pdf.c @@ -0,0 +1,39 @@ +// Cauchy PDF +// +// cauchy_pdf - Cauchy probability density +// +// f(x) = 1 / (PI * (1 + x^2)) +// +// Inputs: 1 +// Outputs: 1 (PDF value) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cauchy_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cauchy_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cauchy_pdf( + 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 +) +{ + TS_PROLOGUE( ts_cauchy_pdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = 1.0 / ( PI * ( 1.0 + x[0][i] * x[0][i] ) ); + } + return 0; +} + + + diff --git a/lib/cbrt.c b/lib/cbrt.c new file mode 100644 index 0000000..2de15d2 --- /dev/null +++ b/lib/cbrt.c @@ -0,0 +1,41 @@ +// Cube Root +// +// cbrt - cube root +// +// y[t] = ∛(x[t]) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cbrt_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cbrt_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cbrt( + 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 +) +{ + TS_PROLOGUE( ts_cbrt_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = cbrt( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/cci.c b/lib/cci.c new file mode 100644 index 0000000..956f619 --- /dev/null +++ b/lib/cci.c @@ -0,0 +1,89 @@ +// Commodity Channel Index +// +// cci - CCI +// +// (TP - SMA(TP)) / (0.015 * MAD(TP)), where TP = (H+L+C)/3 +// +// Inputs: 4 (OHLC) +// Outputs: 1 (CCI) +// Constants: 0 + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_cci_params_t; + +ts_parameter_t const ts_cci_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_cci_params_t) }; + +uint_t ts_cci_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_cci_params_t *)c)->k - 1 : 0 ); +} + +int ts_cci( + 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 +) +{ + TS_PROLOGUE( ts_cci_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_cci_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + // Compute initial typical prices + fp_t tp_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + tp_sum += ( ts_h( x, j ) + ts_l( x, j ) + ts_c( x, j ) ) / 3.0; + } + fp_t tp_sma = tp_sum / (fp_t)k; + + // Compute initial MAD + fp_t mad_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t tp = ( ts_h( x, j ) + ts_l( x, j ) + ts_c( x, j ) ) / 3.0; + mad_sum += fabs( tp - tp_sma ); + } + fp_t mad = mad_sum / (fp_t)k; + + { + fp_t tp = ( ts_h( x, k - 1 ) + ts_l( x, k - 1 ) + ts_c( x, k - 1 ) ) / 3.0; + if( mad == 0 ) + y[0][k - 1] = 0; + else + y[0][k - 1] = ( tp - tp_sma ) / ( 0.015 * mad ); + } + + // Sliding window: incremental tp_sum, but MAD needs recompute when tp_sma changes + // MAD depends on tp_sma which changes each step, so we can't fully incremental MAD + // However, we can still use incremental tp_sum + for( uint_t i = k; i < n; ++i ) + { + fp_t tp_new = ( ts_h( x, i ) + ts_l( x, i ) + ts_c( x, i ) ) / 3.0; + fp_t tp_old = ( ts_h( x, i - k ) + ts_l( x, i - k ) + ts_c( x, i - k ) ) / 3.0; + tp_sum = tp_sum + tp_new - tp_old; + tp_sma = tp_sum / (fp_t)k; + + mad_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t tp = ( ts_h( x, i - k + 1 + j ) + ts_l( x, i - k + 1 + j ) + ts_c( x, i - k + 1 + j ) ) / 3.0; + mad_sum += fabs( tp - tp_sma ); + } + mad = mad_sum / (fp_t)k; + + if( mad == 0 ) + y[0][i] = 0; + else + y[0][i] = ( tp_new - tp_sma ) / ( 0.015 * mad ); + } + + return 0; +} + diff --git a/lib/ceiling.c b/lib/ceiling.c new file mode 100644 index 0000000..3dc53d8 --- /dev/null +++ b/lib/ceiling.c @@ -0,0 +1,42 @@ +// Ceiling +// +// ceiling - ceil +// +// f( x ) = ⌈ x ⌉ +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_ceiling_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_ceiling_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ceiling( + 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 +) +{ + TS_PROLOGUE( ts_ceiling_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ceil( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/center_of_gravity.c b/lib/center_of_gravity.c new file mode 100644 index 0000000..3186c44 --- /dev/null +++ b/lib/center_of_gravity.c @@ -0,0 +1,56 @@ +// Center of Gravity +// +// center_of_gravity - COG +// +// 1 - 2 * SUM(i*x_i) / ((k+1) * SUM(x_i)), oscillates around 0 +// +// Inputs: 1 +// Outputs: 1 (COG) +// Constants: 1 (period) + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_center_of_gravity_params_t; + +ts_parameter_t const ts_center_of_gravity_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_center_of_gravity_params_t) }; + +uint_t ts_center_of_gravity_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_center_of_gravity_params_t *)c)->k - 1 : 0 ); +} + +int ts_center_of_gravity( + 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 +) +{ + TS_PROLOGUE( ts_center_of_gravity_p ); + if ( !c ) return -1; + uint_t k = ((ts_center_of_gravity_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum_ix = 0; + fp_t sum_x = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t idx = (fp_t)(j + 1); + fp_t val = x[0][i - k + 1 + j]; + sum_ix += idx * val; + sum_x += val; + } + if( sum_x == 0 ) + y[0][i] = 0; + else + y[0][i] = 1.0 - 2.0 * sum_ix / ( ( (fp_t)k + 1.0 ) * sum_x ); + } + return 0; +} + + + diff --git a/lib/chande_forecast_oscillator.c b/lib/chande_forecast_oscillator.c new file mode 100644 index 0000000..6e65bbe --- /dev/null +++ b/lib/chande_forecast_oscillator.c @@ -0,0 +1,67 @@ +// Chande Forecast Oscillator +// +// chande_forecast_oscillator - CFO +// +// (Actual - Forecast) / Forecast * 100, using linear regression forecast +// +// Inputs: 1 +// Outputs: 1 (Oscillator %) +// Constants: 1 (period) + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_chande_forecast_oscillator_params_t; + +ts_parameter_t const ts_chande_forecast_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_chande_forecast_oscillator_params_t) }; + +uint_t ts_chande_forecast_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_chande_forecast_oscillator_params_t *)c)->k - 1 : 0 ); +} + +int ts_chande_forecast_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_chande_forecast_oscillator_p ); + if ( !c ) return -1; + uint_t k = ((ts_chande_forecast_oscillator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + fp_t yv = x[0][i - k + 1 + j]; + sum_x += xv; + sum_y += yv; + sum_xy += xv * yv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t forecast; + if( denom == 0 ) + forecast = sum_y / (fp_t)k; + else + { + fp_t slope = ( (fp_t)k * sum_xy - sum_x * sum_y ) / denom; + fp_t intercept = ( sum_y - slope * sum_x ) / (fp_t)k; + forecast = intercept + slope * (fp_t)k; + } + if( forecast != 0 ) + y[0][i] = ( x[0][i] - forecast ) / forecast * 100.0; + else + y[0][i] = 0; + } + return 0; +} + + + diff --git a/lib/change.c b/lib/change.c new file mode 100644 index 0000000..84815a5 --- /dev/null +++ b/lib/change.c @@ -0,0 +1,47 @@ +// Percentage Change +// +// change - pct_change +// +// (x[t] - x[t-k]) / x[t-k] * 100 +// +// Inputs: 1 +// Outputs: 1 (Change %) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_change_params_t; + +ts_parameter_t const ts_change_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_change_params_t) }; + +uint_t ts_change_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_change_params_t *)c)->k : 0 ); +} + +int ts_change( + 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 +) +{ + TS_PROLOGUE( ts_change_p ); + if ( !c ) return -1; + uint_t k = ((ts_change_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = ( x[0][i] - x[0][i - k] ) / x[0][i - k] * 100; + } + + return 0; +} + + + diff --git a/lib/channel.c b/lib/channel.c new file mode 100644 index 0000000..a2d8bd9 --- /dev/null +++ b/lib/channel.c @@ -0,0 +1,64 @@ +// Channel +// +// channel - Donchian-style channel +// +// Upper: rolling highest high, Lower: rolling lowest low over period k +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Upper, Lower) +// Constants: 1 (period) + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_channel_params_t; + +ts_parameter_t const ts_channel_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_channel_params_t) }; + +uint_t ts_channel_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_channel_params_t *)c)->k - 1 : 0 ); +} + +int ts_channel( + 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 +) +{ + TS_PROLOGUE( ts_channel_p ); + if ( !c ) return -1; + uint_t k = ((ts_channel_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k - 1; + uint_t xj = 0; + uint_t yi = k - 1; + + for( ; xi < n; xi++, xj++, yi++ ) + { + y[0][yi] = x[1][xj]; + for( uint_t i = xj; i <= xi; i++ ) + { + if( x[1][i] > y[0][yi] ) + { + y[0][yi] = x[1][i]; + } + } + + y[1][yi] = x[2][xj]; + for( uint_t i = xj; i <= xi; i++ ) + { + if( x[2][i] < y[1][yi] ) + { + y[1][yi] = x[2][i]; + } + } + } + return 0; +} + + + diff --git a/lib/chebyshev_distance.c b/lib/chebyshev_distance.c new file mode 100644 index 0000000..58c055d --- /dev/null +++ b/lib/chebyshev_distance.c @@ -0,0 +1,39 @@ +// Chebyshev Distance +// +// chebyshev_distance - L∞ norm +// +// max(|x|, |y|) +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_chebyshev_distance_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_chebyshev_distance_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_chebyshev_distance( + 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 +) +{ + TS_PROLOGUE( ts_chebyshev_distance_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmax( fabs( x[0][i] ), fabs( x[1][i] ) ); + } + return 0; +} + + + diff --git a/lib/chebyshev_t.c b/lib/chebyshev_t.c new file mode 100644 index 0000000..a0230b8 --- /dev/null +++ b/lib/chebyshev_t.c @@ -0,0 +1,61 @@ +// Chebyshev Polynomial of the First Kind +// +// chebyshev_t - T_n(x) +// +// T_0 = 1, T_1 = x, T_n = 2x*T_(n-1) - T_(n-2) +// +// Inputs: 2 (x, n) +// Outputs: 1 (T_n(x)) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_chebyshev_t_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_chebyshev_t_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_chebyshev_t( + 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 +) +{ + TS_PROLOGUE( ts_chebyshev_t_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + uint_t n_val = (uint_t)x[1][i]; + if (x[1][i] < 0 || x[1][i] > 10000) { y[0][i] = 0; continue; } + if( n_val == 0 ) + { + y[0][i] = 1.0; + } + else if( n_val == 1 ) + { + y[0][i] = xv; + } + else + { + fp_t t0 = 1.0; + fp_t t1 = xv; + for( uint_t k = 2; k <= n_val; ++k ) + { + fp_t t2 = 2.0 * xv * t1 - t0; + t0 = t1; + t1 = t2; + } + y[0][i] = t1; + } + } + return 0; +} + + + diff --git a/lib/chi_squared_pdf.c b/lib/chi_squared_pdf.c new file mode 100644 index 0000000..7f2ba09 --- /dev/null +++ b/lib/chi_squared_pdf.c @@ -0,0 +1,49 @@ +// Chi-Squared PDF +// +// chi_squared_pdf - χ² probability density +// +// f(x; k) = x^(k/2-1) * e^(-x/2) / (2^(k/2) * Γ(k/2)), for x > 0 +// +// Inputs: 1 +// Outputs: 1 (PDF value) +// Constants: 1 (degrees of freedom) + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t k; +} ts_chi_squared_pdf_params_t; + +ts_parameter_t const ts_chi_squared_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_chi_squared_pdf_params_t) }; + +uint_t ts_chi_squared_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_chi_squared_pdf( + 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 +) +{ + TS_PROLOGUE( ts_chi_squared_pdf_p ); + if ( !c ) return -1; + fp_t k = ((ts_chi_squared_pdf_params_t *)c)->k; + if ( k <= 0 ) + { + for( uint_t i = 0; i < n; ++i ) y[0][i] = 0; + return -1; + } + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi > 0 ? pow( xi, k / 2.0 - 1.0 ) * exp( -xi / 2.0 ) / ( pow( 2.0, k / 2.0 ) * tgamma( k / 2.0 ) ) : 0.0; + } + return 0; +} + + + diff --git a/lib/choppiness_index.c b/lib/choppiness_index.c new file mode 100644 index 0000000..55ecb46 --- /dev/null +++ b/lib/choppiness_index.c @@ -0,0 +1,68 @@ +// Choppiness Index +// +// choppiness_index - CHOP +// +// 100 * log(SUM(H-L) / Range) / log(k), measures trend vs chop +// +// Inputs: 3 (HLC) +// Outputs: 1 (CHOP) +// Constants: 0 + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_choppiness_index_params_t; + +ts_parameter_t const ts_choppiness_index_p = { .n_inputs = 3, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_choppiness_index_params_t) }; + +uint_t ts_choppiness_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? 2 * ((ts_choppiness_index_params_t *)c)->k : 0 ); +} + +int ts_choppiness_index( + 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 +) +{ + TS_PROLOGUE( ts_choppiness_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_choppiness_index_params_t *)c)->k; + uint_t range_period = 2 * k; + if (k == 0 || n < range_period) return -1; + + for ( uint_t i = range_period; i < n; ++i ) + { + fp_t sum_range = 0; + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + sum_range += fabs( ts_h( x, j ) - ts_l( x, j ) ); + } + + fp_t hh = ts_h( x, i - range_period + 1 ); + fp_t ll = ts_l( x, i - range_period + 1 ); + for ( uint_t j = i - range_period + 1; j <= i; ++j ) + { + if ( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if ( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + + fp_t range = hh - ll; + if ( range == 0 || sum_range == 0 || k <= 1 ) + { + y[0][i] = 0; + } + else + { + y[0][i] = 100.0 * log( sum_range / range ) / log( ( fp_t )k ); + } + } + return 0; +} + + + diff --git a/lib/close_minus_open.c b/lib/close_minus_open.c new file mode 100644 index 0000000..45932cb --- /dev/null +++ b/lib/close_minus_open.c @@ -0,0 +1,39 @@ +// Close Minus Open +// +// close_minus_open - C - O +// +// Difference between close and open price +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (C - O) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_close_minus_open_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_close_minus_open_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_close_minus_open( + 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 +) +{ + TS_PROLOGUE( ts_close_minus_open_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[3][i] - x[0][i]; + } + return 0; +} + + + diff --git a/lib/close_to_high.c b/lib/close_to_high.c new file mode 100644 index 0000000..dc7cf7b --- /dev/null +++ b/lib/close_to_high.c @@ -0,0 +1,40 @@ +// Close to High Ratio +// +// close_to_high - CT_H +// +// (H - C) / (H - L), position of close within daily range +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (ratio) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_close_to_high_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_close_to_high_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_close_to_high( + 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 +) +{ + TS_PROLOGUE( ts_close_to_high_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t denom = x[1][i] - x[2][i]; + y[0][i] = denom != 0 ? (x[1][i] - x[3][i]) / denom : 0; + } + return 0; +} + + + diff --git a/lib/close_to_open_ratio.c b/lib/close_to_open_ratio.c new file mode 100644 index 0000000..d857dd2 --- /dev/null +++ b/lib/close_to_open_ratio.c @@ -0,0 +1,39 @@ +// Close to Open Ratio +// +// close_to_open_ratio - C/O +// +// Ratio of close to open price +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (ratio) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_close_to_open_ratio_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_close_to_open_ratio_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_close_to_open_ratio( + 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 +) +{ + TS_PROLOGUE( ts_close_to_open_ratio_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] != 0 ? x[3][i] / x[0][i] : 0; + } + return 0; +} + + + diff --git a/lib/cmf.c b/lib/cmf.c new file mode 100644 index 0000000..7ac0402 --- /dev/null +++ b/lib/cmf.c @@ -0,0 +1,67 @@ +// Chaikin Money Flow +// +// cmf - CMF +// +// SUM(MF * V) / SUM(V) over period, where MF = ((C-L)-(H-C))/(H-L) +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (CMF) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_cmf_params_t; + +ts_parameter_t const ts_cmf_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_cmf_params_t) }; + +uint_t ts_cmf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_cmf_params_t *)c)->k - 1 : 0 ); +} + +int ts_cmf( + 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 +) +{ + TS_PROLOGUE( ts_cmf_p ); + if ( !c ) return -1; + uint_t k = ((ts_cmf_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_mf = 0; + fp_t sum_vol = 0; + + for ( uint_t i = 0; i < k; ++i ) + { + fp_t hl = ts_h( x, i ) - ts_l( x, i ); + fp_t mf = hl != 0 ? ( ( ts_c( x, i ) - ts_l( x, i ) ) - ( ts_h( x, i ) - ts_c( x, i ) ) ) / hl * ts_v( x, i ) : 0; + sum_mf += mf; + sum_vol += ts_v( x, i ); + } + + y[0][k - 1] = sum_vol != 0 ? sum_mf / sum_vol : 0; + + for ( uint_t i = k; i < n; ++i ) + { + fp_t hl_prev = ts_h( x, i - k ) - ts_l( x, i - k ); + fp_t mf_prev = hl_prev != 0 ? ( ( ts_c( x, i - k ) - ts_l( x, i - k ) ) - ( ts_h( x, i - k ) - ts_c( x, i - k ) ) ) / hl_prev * ts_v( x, i - k ) : 0; + + fp_t hl_cur = ts_h( x, i ) - ts_l( x, i ); + fp_t mf_cur = hl_cur != 0 ? ( ( ts_c( x, i ) - ts_l( x, i ) ) - ( ts_h( x, i ) - ts_c( x, i ) ) ) / hl_cur * ts_v( x, i ) : 0; + + sum_mf = sum_mf - mf_prev + mf_cur; + sum_vol = sum_vol - ts_v( x, i - k ) + ts_v( x, i ); + + y[0][i] = sum_vol != 0 ? sum_mf / sum_vol : 0; + } + return 0; +} + + + diff --git a/lib/cmo.c b/lib/cmo.c new file mode 100644 index 0000000..9db5622 --- /dev/null +++ b/lib/cmo.c @@ -0,0 +1,72 @@ +// Chande Momentum Oscillator +// +// cmo - CMO +// +// 100 * (SUM(up) - SUM(down)) / (SUM(up) + SUM(down)) +// +// Inputs: 1 +// Outputs: 1 (CMO) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_cmo_params_t; + +ts_parameter_t const ts_cmo_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_cmo_params_t) }; + +uint_t ts_cmo_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_cmo_params_t *)c)->k : 0 ); +} + +int ts_cmo( + 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 +) +{ + TS_PROLOGUE( ts_cmo_p ); + if ( !c ) return -1; + uint_t k = ((ts_cmo_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + for (uint_t j = 0; j < k; ++j) y[0][j] = 0; + + fp_t up_sum = 0; + fp_t down_sum = 0; + + for( uint_t i = 1; i <= k; ++i ) + { + fp_t diff = x[0][i] - x[0][i - 1]; + if( diff > 0 ) + up_sum += diff; + else if( diff < 0 ) + down_sum += (-diff); + } + + y[0][k] = (up_sum + down_sum) == 0 ? 0 : 100.0 * (up_sum - down_sum) / (up_sum + down_sum); + + for( uint_t i = k + 1; i < n; ++i ) + { + fp_t diff_new = x[0][i] - x[0][i - 1]; + fp_t diff_old = x[0][i - k] - x[0][i - k - 1]; + + fp_t up_new = diff_new > 0 ? diff_new : 0; + fp_t down_new = diff_new < 0 ? (-diff_new) : 0; + fp_t up_old = diff_old > 0 ? diff_old : 0; + fp_t down_old = diff_old < 0 ? (-diff_old) : 0; + + up_sum = up_sum - up_old + up_new; + down_sum = down_sum - down_old + down_new; + + y[0][i] = (up_sum + down_sum) == 0 ? 0 : 100.0 * (up_sum - down_sum) / (up_sum + down_sum); + } + return 0; +} + + + diff --git a/lib/coppock_curve.c b/lib/coppock_curve.c new file mode 100644 index 0000000..dfa1ad2 --- /dev/null +++ b/lib/coppock_curve.c @@ -0,0 +1,75 @@ +// Coppock Curve +// +// coppock_curve - WMS +// +// WMA14(ROC14 + ROC11), weighted moving average of combined ROCs +// +// Inputs: 1 +// Outputs: 1 (Coppock) +// Constants: 0 + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + + +ts_parameter_t const ts_coppock_curve_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_coppock_curve_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 14 ); +} + +int ts_coppock_curve( + 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 +) +{ + TS_PROLOGUE( ts_coppock_curve_p ); + (void)c; + uint_t roc_long = 14; + uint_t roc_short = 11; + uint_t wma_period = 10; + + if (n == 0 || n < roc_long) return -1; + + fp_t *combined = calloc( n, sizeof( fp_t ) ); + if ( !combined ) return -1; + + for ( uint_t i = roc_long; i < n; ++i ) + { + if ( x[0][i - roc_long] == 0 || x[0][i - roc_short] == 0 ) + { + combined[i - roc_long] = 0; + continue; + } + fp_t roc14 = ( x[0][i] - x[0][i - roc_long] ) / x[0][i - roc_long] * 100.0; + fp_t roc11 = ( x[0][i] - x[0][i - roc_short] ) / x[0][i - roc_short] * 100.0; + combined[i - roc_long] = roc14 + roc11; + } + + for (uint_t j = 0; j < n && j < roc_long; ++j) y[0][j] = 0; + + for ( uint_t i = roc_long; i < n; ++i ) + { + uint_t ci = i - roc_long; + fp_t wsum = 0; + fp_t wtotal = 0; + uint_t start = ci + 1 >= wma_period ? ci + 1 - wma_period : 0; + for ( uint_t j = start; j <= ci; ++j ) + { + uint_t weight = ci - j + 1; + wsum += combined[j] * (fp_t)weight; + wtotal += (fp_t)weight; + } + y[0][ci + roc_long] = wtotal != 0 ? wsum / wtotal : 0; + } + + free( combined ); + return 0; +} + + + diff --git a/lib/copysign.c b/lib/copysign.c new file mode 100644 index 0000000..f146741 --- /dev/null +++ b/lib/copysign.c @@ -0,0 +1,39 @@ +// Copy Sign +// +// copysign - copy sign +// +// Returns x[0] with the sign of x[1] +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_copysign_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_copysign_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_copysign( + 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 +) +{ + TS_PROLOGUE( ts_copysign_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = copysign( x[0][i], x[1][i] ); + } + return 0; +} + + + diff --git a/lib/correlation_coefficient.c b/lib/correlation_coefficient.c new file mode 100644 index 0000000..d20d1ea --- /dev/null +++ b/lib/correlation_coefficient.c @@ -0,0 +1,75 @@ +// Correlation Coefficient +// +// correlation_coefficient - corr +// +// Pearson r = (n*Σxy - Σx*Σy) / √((n*Σx²-Σx²)(n*Σy²-Σy²)) +// +// Inputs: 2 +// Outputs: 1 (r) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_correlation_coefficient_params_t; + +ts_parameter_t const ts_correlation_coefficient_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_correlation_coefficient_params_t) }; + +uint_t ts_correlation_coefficient_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_correlation_coefficient_params_t *)c)->k - 1 : 0 ); +} + +int ts_correlation_coefficient( + 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 +) +{ + TS_PROLOGUE( ts_correlation_coefficient_p ); + if ( !c ) return -1; + uint_t k = ((ts_correlation_coefficient_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0, sum_y2 = 0; + + for ( uint_t i = 0; i < k; ++i ) + { + fp_t xi = x[0][i]; + fp_t yi = x[1][i]; + sum_x += xi; + sum_y += yi; + sum_xy += xi * yi; + sum_x2 += xi * xi; + sum_y2 += yi * yi; + } + + fp_t denom = ( k * sum_x2 - sum_x * sum_x ) * ( k * sum_y2 - sum_y * sum_y ); + fp_t numer = k * sum_xy - sum_x * sum_y; + y[0][k - 1] = denom > 0 ? numer / sqrt( denom ) : 0; + + for ( uint_t i = k; i < n; ++i ) + { + fp_t xi_old = x[0][i - k]; + fp_t yi_old = x[1][i - k]; + fp_t xi_new = x[0][i]; + fp_t yi_new = x[1][i]; + + sum_x = sum_x - xi_old + xi_new; + sum_y = sum_y - yi_old + yi_new; + sum_xy = sum_xy - xi_old * yi_old + xi_new * yi_new; + sum_x2 = sum_x2 - xi_old * xi_old + xi_new * xi_new; + sum_y2 = sum_y2 - yi_old * yi_old + yi_new * yi_new; + + denom = ( k * sum_x2 - sum_x * sum_x ) * ( k * sum_y2 - sum_y * sum_y ); + numer = k * sum_xy - sum_x * sum_y; + y[0][i] = denom > 0 ? numer / sqrt( denom ) : 0; + } + return 0; +} + + + diff --git a/lib/cosecant.c b/lib/cosecant.c new file mode 100644 index 0000000..e2aea17 --- /dev/null +++ b/lib/cosecant.c @@ -0,0 +1,43 @@ +// Cosecant +// +// cosecant - csc +// +// f( x ) = csc( x ) = 1 / sin( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosecant_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosecant_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosecant( + 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 +) +{ + TS_PROLOGUE( ts_cosecant_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t s = sin( x[0][i] ); + y[0][i] = ( fabs( s ) > 1e-15 ) ? _ts_reciprocal( s ) : ( s > 0 ? (fp_t)INFINITY : (fp_t)-INFINITY ); + } + return 0; +} + + + diff --git a/lib/cosecant_hyperbolic.c b/lib/cosecant_hyperbolic.c new file mode 100644 index 0000000..96898fc --- /dev/null +++ b/lib/cosecant_hyperbolic.c @@ -0,0 +1,45 @@ +// Hyperbolic Cosecant +// +// cosecant_hyperbolic - csch +// +// f( x ) = csch( x ) +// = 1 / sinh( x ) +// = 2 / ( e^x - e^( -x ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosecant_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosecant_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosecant_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_cosecant_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t s = sinh( x[0][i] ); + y[0][i] = ( fabs( s ) > 1e-15 ) ? _ts_reciprocal( s ) : ( s > 0 ? (fp_t)INFINITY : (fp_t)-INFINITY ); + } + return 0; +} + + + diff --git a/lib/cosecant_hyperbolic_inverse.c b/lib/cosecant_hyperbolic_inverse.c new file mode 100644 index 0000000..a9f0984 --- /dev/null +++ b/lib/cosecant_hyperbolic_inverse.c @@ -0,0 +1,44 @@ +// Inverse Hyperbolic Cosecant +// +// cosecant_hyperbolic_inverse - acsch +// +// f( x ) = acsch( x ) +// = ln( ( 1 / x ) + √( ( 1 / x^2 ) + 1 ) ) x ≠ 0 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosecant_hyperbolic_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosecant_hyperbolic_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosecant_hyperbolic_inverse( + 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 +) +{ + TS_PROLOGUE( ts_cosecant_hyperbolic_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + y[0][i] = xv == 0.0 ? (fp_t)NAN : asinh( _ts_reciprocal( xv ) ); + } + return 0; +} + + + diff --git a/lib/cosecant_inverse.c b/lib/cosecant_inverse.c new file mode 100644 index 0000000..b5f0bbd --- /dev/null +++ b/lib/cosecant_inverse.c @@ -0,0 +1,44 @@ +// Inverse Cosecant +// +// cosecant_inverse - acsc +// +// f( x ) = acsc( x ) +// = asin( 1 / x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosecant_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosecant_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosecant_inverse( + 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 +) +{ + TS_PROLOGUE( ts_cosecant_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t r = _ts_reciprocal( x[0][i] ); + y[0][i] = ( r >= -1.0 && r <= 1.0 ) ? asin( r ) : 0; + } + return 0; +} + + + diff --git a/lib/cosine.c b/lib/cosine.c new file mode 100644 index 0000000..ee039e7 --- /dev/null +++ b/lib/cosine.c @@ -0,0 +1,42 @@ +// Cosine +// +// cosine - cos +// +// f( x ) = cos( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosine_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosine_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosine( + 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 +) +{ + TS_PROLOGUE( ts_cosine_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = cos( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/cosine_hyperbolic.c b/lib/cosine_hyperbolic.c new file mode 100644 index 0000000..51f84a0 --- /dev/null +++ b/lib/cosine_hyperbolic.c @@ -0,0 +1,43 @@ +// Hyperbolic Cosine +// +// cosine_hyperbolic - cosh +// +// f( x ) = cosh( x ) +// = 0.5 * ( e^x + e^( -x ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosine_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosine_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosine_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_cosine_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = cosh( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/cosine_inverse.c b/lib/cosine_inverse.c new file mode 100644 index 0000000..dead58a --- /dev/null +++ b/lib/cosine_inverse.c @@ -0,0 +1,43 @@ +// Inverse Cosine +// +// cosine_inverse - acos +// +// f( x ) = acos( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cosine_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cosine_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosine_inverse( + 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 +) +{ + TS_PROLOGUE( ts_cosine_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + y[0][i] = ( v >= -1.0 && v <= 1.0 ) ? acos( v ) : 0; + } + return 0; +} + + + diff --git a/lib/cosine_similarity.c b/lib/cosine_similarity.c new file mode 100644 index 0000000..f93f9b7 --- /dev/null +++ b/lib/cosine_similarity.c @@ -0,0 +1,40 @@ +// Cosine Similarity +// +// cosine_similarity - cos_sim +// +// Sign of the product: +1 if positive, -1 if negative, 0 if zero +// +// Inputs: 2 +// Outputs: 1 (similarity) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cosine_similarity_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cosine_similarity_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cosine_similarity( + 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 +) +{ + TS_PROLOGUE( ts_cosine_similarity_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t p = x[0][i] * x[1][i]; + y[0][i] = p > 0.0 ? 1.0 : ( p < 0.0 ? -1.0 : 0.0 ); + } + return 0; +} + + + diff --git a/lib/cotangent.c b/lib/cotangent.c new file mode 100644 index 0000000..b23b6da --- /dev/null +++ b/lib/cotangent.c @@ -0,0 +1,43 @@ +// Cotangent +// +// cotangent - cot +// +// f( x ) = cot( x ) = 1 / tan( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cotangent_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cotangent_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cotangent( + 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 +) +{ + TS_PROLOGUE( ts_cotangent_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t t = tan( x[0][i] ); + y[0][i] = ( fabs( t ) > 1e-15 ) ? _ts_reciprocal( t ) : ( t > 0 ? (fp_t)INFINITY : (fp_t)-INFINITY ); + } + return 0; +} + + + diff --git a/lib/cotangent_hyperbolic.c b/lib/cotangent_hyperbolic.c new file mode 100644 index 0000000..dca6a4f --- /dev/null +++ b/lib/cotangent_hyperbolic.c @@ -0,0 +1,46 @@ +// Hyperbolic Cotangent +// +// cotangent_hyperbolic - coth +// +// f( x ) = coth( x ) +// = 1 / tanh( x ) +// = cosh( x ) / sinh( x ) +// = ( e^2x + 1 ) / ( e^2x - 1 ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cotangent_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cotangent_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cotangent_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_cotangent_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t t = tanh( x[0][i] ); + y[0][i] = ( fabs( t ) > 1e-15 ) ? _ts_reciprocal( t ) : ( t > 0 ? (fp_t)INFINITY : (fp_t)-INFINITY ); + } + return 0; +} + + + diff --git a/lib/cotangent_hyperbolic_inverse.c b/lib/cotangent_hyperbolic_inverse.c new file mode 100644 index 0000000..3fe2c14 --- /dev/null +++ b/lib/cotangent_hyperbolic_inverse.c @@ -0,0 +1,45 @@ +// Inverse Hyperbolic Cotangent +// +// cottangent_hyperbolic_inverse - acoth +// +// f( x ) = acoth( x ) +// = 0.5 * ln( ( x + 1 ) / ( x - 1 ) ) |x| > 1 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cotangent_hyperbolic_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cotangent_hyperbolic_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cotangent_hyperbolic_inverse( + 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 +) +{ + TS_PROLOGUE( ts_cotangent_hyperbolic_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + fp_t r = _ts_reciprocal( xi ); + y[0][i] = ( r > -1.0 && r < 1.0 ) ? atanh( r ) : 0; + } + return 0; +} + + + diff --git a/lib/cotangent_inverse.c b/lib/cotangent_inverse.c new file mode 100644 index 0000000..6ad3318 --- /dev/null +++ b/lib/cotangent_inverse.c @@ -0,0 +1,46 @@ +// Inverse Cotangent +// +// cotangent_inverse - acot +// +// f( x ) = acot( x ) +// = atan( 1 / x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_cotangent_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_cotangent_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cotangent_inverse( + 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 +) +{ + TS_PROLOGUE( ts_cotangent_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + if ( v > 0 ) y[0][i] = atan( 1.0 / v ); + else if ( v < 0 ) y[0][i] = atan( 1.0 / v ) + PI; + else y[0][i] = PI / 2.0; + } + return 0; +} + + + diff --git a/lib/crossover.c b/lib/crossover.c new file mode 100644 index 0000000..92a0c9e --- /dev/null +++ b/lib/crossover.c @@ -0,0 +1,60 @@ +// Crossover +// +// crossover - crossover +// +// ⎧ -1 if x0_i < x1_i and x0_i-1 > x1_i-1 +// y_i = ⎨ 0 if x = 0 +// ⎩ 1 if x0_i > x1_i and x0_i-1 < x1_i-1 +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_crossover_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_crossover_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_crossover( + 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 +) +{ + TS_PROLOGUE( ts_crossover_p ); + (void)c; + if ( n < 2 ) return -1; + + uint_t xi; + uint_t yi; + + xi = 1; + yi = 1; + + y[0][0] = 0; + for( ; xi < n; xi++, yi++ ) + { + if( x[0][xi] > x[1][xi] && x[0][xi - 1] <= x[1][xi - 1] ) + y[0][yi] = 1; + + else if( x[0][xi] < x[1][xi] && x[0][xi - 1] >= x[1][xi - 1] ) + y[0][yi] = -1; + + else + y[0][yi] = 0; + } + return 0; +} + + + diff --git a/lib/cumulative_product.c b/lib/cumulative_product.c new file mode 100644 index 0000000..5701deb --- /dev/null +++ b/lib/cumulative_product.c @@ -0,0 +1,42 @@ +// Cumulative Product +// +// cumulative_product - cumprod +// +// y[i] = x[0] * x[1] * ... * x[i] +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cumulative_product_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cumulative_product_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cumulative_product( + 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 +) +{ + TS_PROLOGUE( ts_cumulative_product_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if( i == 0 ) + y[0][i] = x[0][i]; + else + y[0][i] = y[0][i - 1] * x[0][i]; + } + return 0; +} + + + diff --git a/lib/cumulative_sum.c b/lib/cumulative_sum.c new file mode 100644 index 0000000..417a37f --- /dev/null +++ b/lib/cumulative_sum.c @@ -0,0 +1,42 @@ +// Cumulative Sum +// +// cumulative_sum - cumsum +// +// y[i] = x[0] + x[1] + ... + x[i] +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cumulative_sum_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cumulative_sum_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cumulative_sum( + 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 +) +{ + TS_PROLOGUE( ts_cumulative_sum_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if( i == 0 ) + y[0][i] = x[0][i]; + else + y[0][i] = y[0][i - 1] + x[0][i]; + } + return 0; +} + + + diff --git a/lib/cumulative_volume_delta.c b/lib/cumulative_volume_delta.c new file mode 100644 index 0000000..d76da1c --- /dev/null +++ b/lib/cumulative_volume_delta.c @@ -0,0 +1,44 @@ +// Cumulative Volume Delta +// +// cumulative_volume_delta - CVD +// +// Running sum of volume weighted by close-open direction +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (CVD) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_cumulative_volume_delta_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_cumulative_volume_delta_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_cumulative_volume_delta( + 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 +) +{ + TS_PROLOGUE( ts_cumulative_volume_delta_p ); + (void)c; + fp_t cum = 0; + + for( uint_t i = 0; i < n; ++i ) + { + fp_t diff = x[3][i] - x[0][i]; + fp_t sign = ( diff > 0 ) ? 1.0 : ( diff < 0 ) ? -1.0 : 0.0; + cum += x[4][i] * sign; + y[0][i] = cum; + } + return 0; +} + + + diff --git a/lib/cv.c b/lib/cv.c new file mode 100644 index 0000000..0c06250 --- /dev/null +++ b/lib/cv.c @@ -0,0 +1,56 @@ +// ConnorsRSI Volatility +// +// cv - CV +// +// EMA_fast(H-L) - EMA_slow(H-L), volatility measure +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (CV) +// Constants: 2 (fast, slow periods) + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t fast; + fp_t slow; +} ts_cv_params_t; + +ts_parameter_t const ts_cv_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_cv_params_t) }; + +uint_t ts_cv_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)((ts_cv_params_t *)c)->slow : 0 ); +} + +int ts_cv( + 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 +) +{ + TS_PROLOGUE( ts_cv_p ); + if ( !c ) return -1; + fp_t fast = ((ts_cv_params_t *)c)->fast; + fp_t slow = ((ts_cv_params_t *)c)->slow; + if (fast == 0 || slow == 0 || n == 0) return -1; + + fp_t a_fast = 1.0 / fast; + fp_t a_slow = 1.0 / slow; + + fp_t ema_fast = ts_h( x, 0 ) - ts_l( x, 0 ); + fp_t ema_slow = ema_fast; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t range = ts_h( x, i ) - ts_l( x, i ); + ema_fast = ema_fast + a_fast * ( range - ema_fast ); + ema_slow = ema_slow + a_slow * ( range - ema_slow ); + y[0][i] = ema_fast - ema_slow; + } + return 0; +} + + + diff --git a/lib/darvas_box.c b/lib/darvas_box.c new file mode 100644 index 0000000..573ed1e --- /dev/null +++ b/lib/darvas_box.c @@ -0,0 +1,54 @@ +// Darvas Box +// +// darvas_box - DB +// +// Upper: highest high over period k +// Lower: lowest low over period k +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Upper, Lower) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_darvas_box_params_t; + +ts_parameter_t const ts_darvas_box_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_darvas_box_params_t) }; + +uint_t ts_darvas_box_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_darvas_box_params_t *)c)->k > 0 ? ((ts_darvas_box_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_darvas_box( + 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 +) +{ + TS_PROLOGUE( ts_darvas_box_p ); + if ( !c ) return -1; + uint_t k = ((ts_darvas_box_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t hh = ts_h( x, i - k + 1 ); + fp_t ll = ts_l( x, i - k + 1 ); + for( uint_t j = i - k + 1; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + y[0][i] = hh; + y[1][i] = ll; + } + return 0; +} + + + diff --git a/lib/dawsn.c b/lib/dawsn.c new file mode 100644 index 0000000..e77d8b0 --- /dev/null +++ b/lib/dawsn.c @@ -0,0 +1,64 @@ +// Dawson Integral +// +// dawsn - Dawson function +// +// F(x) = e^(-x²) * ∫₀ˣ e^(t²) dt +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_dawsn_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_dawsn_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_dawsn( + 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 +) +{ + TS_PROLOGUE( ts_dawsn_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t val = x[0][i]; + fp_t ax = fabs( val ); + fp_t y_val; + + if( ax < 3.0 ) + { + fp_t t = ax; + fp_t s = t; + for( uint_t k = 1; k < 20; ++k ) + { + t *= -2.0 * ax * ax / (fp_t)(2 * k + 1); + s += t; + } + y_val = s; + } + else + { + y_val = 1.0 / ( 2.0 * ax + 1.0 / ( 2.0 * ax + 3.0 / ( 2.0 * ax + 5.0 / ( 2.0 * ax ) ) ) ); + } + + if( val < 0 ) + y_val = -y_val; + + y[0][i] = y_val; + } + return 0; +} + + + diff --git a/lib/decay_exponential.c b/lib/decay_exponential.c new file mode 100644 index 0000000..97afe36 --- /dev/null +++ b/lib/decay_exponential.c @@ -0,0 +1,58 @@ +// Exponential Decay +// +// decay_exponential - edecay +// +// DESCRIPTION +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_decay_exponential_params_t; + +ts_parameter_t const ts_decay_exponential_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_decay_exponential_params_t) }; + + +uint_t ts_decay_exponential_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_decay_exponential_params_t *)c)->k : 0 ); +} + +int ts_decay_exponential( + 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 +) +{ + TS_PROLOGUE( ts_decay_exponential_p ); + if ( !c ) return -1; + uint_t k = ((ts_decay_exponential_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + fp_t a = ( fp_t ) 1.0 - _ts_reciprocal( k ); + + for( uint_t i = 0; i < k; ++i ) + y[0][i] = 0; + + fp_t prev = x[0][0]; + for( uint_t i = 1; i <= k; ++i ) + prev = fmax( x[0][i], prev * a ); + y[0][k] = prev; + + for( uint_t i = k + 1; i < n; ++i ) + { + y[0][i] = fmax( x[0][i], y[0][i - 1] * a ); + } + return 0; +} + + + diff --git a/lib/decay_linear.c b/lib/decay_linear.c new file mode 100644 index 0000000..8519d13 --- /dev/null +++ b/lib/decay_linear.c @@ -0,0 +1,85 @@ +// Linear Decay +// +// decay_linear - decay +// +// Propogates the maximum values of a function into the future for at most k periods +// +// y_i = max( x_i, y_i-1 - k ) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_decay_linear_params_t; + +ts_parameter_t const ts_decay_linear_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_decay_linear_params_t) }; + + +uint_t ts_decay_linear_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_decay_linear_params_t *)c)->k : 0 ); +} + +int ts_decay_linear( + 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 +) +{ + TS_PROLOGUE( ts_decay_linear_p ); + if ( !c ) return -1; + uint_t k = ((ts_decay_linear_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t zi = 0; zi < k; ++zi) y[0][zi] = 0; + + uint_t xi = 1; + + fp_t prev = x[0][0]; + fp_t r = prev / k; + + for( ; xi < k; xi++ ) + { + prev -= r; + + if( x[0][xi] > prev ) + { + prev = x[0][xi]; + r = x[0][xi] / k; + } + else + { + prev = fmax( prev, 0 ); + } + } + + for( uint_t yi = k; xi < n; xi++, yi++ ) + { + fp_t curr = prev - r; + + if( x[0][xi] > curr ) + { + curr = x[0][xi]; + r = x[0][xi] / k; + } + else + { + curr = fmax( curr, 0 ); + } + + y[0][yi] = curr; + prev = curr; + } + return 0; +} + + + diff --git a/lib/degree_radian.c b/lib/degree_radian.c new file mode 100644 index 0000000..821b67f --- /dev/null +++ b/lib/degree_radian.c @@ -0,0 +1,39 @@ +// Degrees to Radians +// +// degree_radian - deg2rad +// +// y = x * PI / 180 +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_degree_radian_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_degree_radian_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_degree_radian( + 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 +) +{ + TS_PROLOGUE( ts_degree_radian_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] * PI / 180.0; + } + return 0; +} + + + diff --git a/lib/delta.c b/lib/delta.c new file mode 100644 index 0000000..02ccb66 --- /dev/null +++ b/lib/delta.c @@ -0,0 +1,50 @@ +// Delta +// +// delta - delta +// +// Calculates the delta between the current value and the value k samples ago +// +// y_i = x_i - x_i-k +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_delta_params_t; + +ts_parameter_t const ts_delta_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_delta_params_t) }; + + +uint_t ts_delta_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_delta_params_t *)c)->k : 0 ); +} + +int ts_delta( + 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 +) +{ + TS_PROLOGUE( ts_delta_p ); + if ( !c ) return -1; + uint_t k = ((ts_delta_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k; + + for( ; xi < n; xi++ ) + { + y[0][xi] = x[0][xi] - x[0][xi - k]; + } + return 0; +} + + diff --git a/lib/deviation_average.c b/lib/deviation_average.c new file mode 100644 index 0000000..5ac7109 --- /dev/null +++ b/lib/deviation_average.c @@ -0,0 +1,78 @@ +// Moving Mean Deviation +// +// mean_deviation - md +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_deviation_average_params_t; + +ts_parameter_t const ts_deviation_average_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_deviation_average_params_t) }; + + +uint_t ts_deviation_average_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_deviation_average_params_t *)c)->k > 0 ? ((ts_deviation_average_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_deviation_average( + 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 +) +{ + TS_PROLOGUE( ts_deviation_average_p ); + if ( !c ) return -1; + uint_t k = ((ts_deviation_average_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k - 1; + + fp_t mean = 0; + + for( ; xi < k; ++xi ) + { + mean += x[0][xi]; + } + + fp_t rk = _ts_reciprocal( k ); + mean = rk * mean; + + { + fp_t sum = 0; + for( uint_t i = 0; i < k; i++ ) + { + fp_t tmp = x[0][i] - mean; + sum += fabs( tmp ); + } + y[0][yi] = sum * rk; + } + + for( xi = k, yi = k; xi < n; ++xi, ++yi ) + { + mean = mean + ( x[0][xi] - x[0][xi - k] ) * rk; + fp_t sum = 0; + + for( uint_t i = xi - k + 1; i <= xi; i++ ) + { + fp_t tmp = x[0][i] - mean; + sum += fabs( tmp ); + } + + y[0][yi] = sum * rk; + } + return 0; +} + + + diff --git a/lib/deviation_standard.c b/lib/deviation_standard.c new file mode 100644 index 0000000..a2a0a76 --- /dev/null +++ b/lib/deviation_standard.c @@ -0,0 +1,70 @@ +// Moving Standard Deviation +// +// standard_deviation - sd +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_deviation_standard_params_t; + +ts_parameter_t const ts_deviation_standard_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_deviation_standard_params_t) }; + + +uint_t ts_deviation_standard_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_deviation_standard_params_t *)c)->k > 0 ? ((ts_deviation_standard_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_deviation_standard( + 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 +) +{ + TS_PROLOGUE( ts_deviation_standard_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_deviation_standard_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + fp_t mean = 0; // Mean + fp_t mean2 = 0; // Mean of squares + + for( ; xi < k; ++xi ) + { + mean += x[0][xi]; + mean2 += _ts_square( x[0][xi] ); + } + + mean /= k; + mean2 /= k; + + fp_t variance = mean2 - _ts_square( mean ); + y[0][k - 1] = variance >= 0 ? sqrt( variance ) : 0; + + fp_t rk = _ts_reciprocal( k ); + + for( ; xi < n; ++xi, ++yi ) + { + mean = mean + ( x[0][xi] - x[0][xi - k] ) * rk; + mean2 = mean2 + ( _ts_square( x[0][xi] ) - _ts_square( x[0][xi - k] ) ) * rk; + + variance = mean2 - _ts_square( mean ); + y[0][yi] = variance >= 0 ? sqrt( variance ) : 0; + } + return 0; +} + + + diff --git a/lib/deviation_standard_average_exponential.c b/lib/deviation_standard_average_exponential.c new file mode 100644 index 0000000..8c484f5 --- /dev/null +++ b/lib/deviation_standard_average_exponential.c @@ -0,0 +1,57 @@ +// Exponential Moving Standard Deviation +// +// standard_deviation_exponential - sde +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t a; +} ts_deviation_standard_average_exponential_params_t; + +ts_parameter_t const ts_deviation_standard_average_exponential_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_deviation_standard_average_exponential_params_t) }; + + +uint_t ts_deviation_standard_average_exponential_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_deviation_standard_average_exponential( + 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 +) +{ + TS_PROLOGUE( ts_deviation_standard_average_exponential_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t a = ((ts_deviation_standard_average_exponential_params_t *)c)->a; + if (a == 0) return -1; + + y[0][0] = x[0][0]; + + fp_t delta; + fp_t ema = x[0][0]; + fp_t variance = 0; + + for( uint_t i = 1; i < n; ++i ) + { + delta = x[0][i] - ema; + ema = ema + a * delta; + variance = ( 1 - a ) * variance + a * _ts_square( delta ); + y[0][i] = variance >= 0 ? sqrt( variance ) : 0; + } + return 0; +} + + + diff --git a/lib/deviation_vector.c b/lib/deviation_vector.c new file mode 100644 index 0000000..4f10037 --- /dev/null +++ b/lib/deviation_vector.c @@ -0,0 +1,61 @@ +// Vector Standard Deviation +// +// deviation_vector - std_dev +// +// Rolling population standard deviation over period k +// +// Inputs: 1 +// Outputs: 1 (stddev) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_deviation_vector_params_t; + +ts_parameter_t const ts_deviation_vector_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_deviation_vector_params_t) }; + +uint_t ts_deviation_vector_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_deviation_vector_params_t *)c)->k > 0 ? ((ts_deviation_vector_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_deviation_vector( + 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 +) +{ + TS_PROLOGUE( ts_deviation_vector_p ); + if ( !c ) return -1; + uint_t k = ((ts_deviation_vector_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = 0; i < n; ++i) + { + if (i < k - 1) + { + y[0][i] = 0; + } + else + { + fp_t sum = 0; + fp_t sum2 = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + { + sum += x[0][j]; + sum2 += x[0][j] * x[0][j]; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + y[0][i] = variance >= 0 ? sqrt(variance) : 0; + } + } + return 0; +} + + + diff --git a/lib/di_minus.c b/lib/di_minus.c new file mode 100644 index 0000000..95b0559 --- /dev/null +++ b/lib/di_minus.c @@ -0,0 +1,69 @@ +// Minus Directional Indicator +// +// di_minus - DI- +// +// 100 * EMA(-DM) / (EMA(+DM) + EMA(-DM)), measures downward pressure +// +// Inputs: 4 (OHLC) +// Outputs: 1 (DI-) +// Constants: 1 (period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_di_minus_params_t; + +ts_parameter_t const ts_di_minus_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_di_minus_params_t) }; + +uint_t ts_di_minus_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_di_minus_params_t *)c)->k : 0 ); +} + +int ts_di_minus( + 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 +) +{ + TS_PROLOGUE( ts_di_minus_p ); + if ( !c ) return -1; + uint_t k = ((ts_di_minus_params_t *)c)->k; + if (k == 0 || n < 2) return -1; + fp_t *ema_minus = calloc( n, sizeof( fp_t ) ); + fp_t *ema_plus = calloc( n, sizeof( fp_t ) ); + if( !ema_minus || !ema_plus ) { free( ema_minus ); free( ema_plus ); return -1; } + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hdmd = ts_h( x, i ) - ts_h( x, i - 1 ); + fp_t ldml = ts_l( x, i - 1 ) - ts_l( x, i ); + + fp_t minus_dm = ldml > hdmd && ldml > 0 ? ldml : 0; + fp_t plus_dm = hdmd > ldml && hdmd > 0 ? hdmd : 0; + + ema_minus[i] = i == 1 ? minus_dm + : ema_minus[i - 1] + ( 1.0 / k ) * ( minus_dm - ema_minus[i - 1] ); + ema_plus[i] = i == 1 ? plus_dm + : ema_plus[i - 1] + ( 1.0 / k ) * ( plus_dm - ema_plus[i - 1] ); + } + + for (uint_t j = 0; j < n && j < k; ++j) y[0][j] = 0; + + for( uint_t i = k; i < n; ++i ) + { + fp_t denom = ema_minus[i] + ema_plus[i]; + y[0][i] = denom != 0 ? 100.0 * ema_minus[i] / denom : 0; + } + + free( ema_minus ); + free( ema_plus ); + return 0; +} + + + diff --git a/lib/di_plus.c b/lib/di_plus.c new file mode 100644 index 0000000..fb3c076 --- /dev/null +++ b/lib/di_plus.c @@ -0,0 +1,69 @@ +// Plus Directional Indicator +// +// di_plus - DI+ +// +// 100 * EMA(+DM) / (EMA(+DM) + EMA(-DM)), measures upward pressure +// +// Inputs: 4 (OHLC) +// Outputs: 1 (DI+) +// Constants: 1 (period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_di_plus_params_t; + +ts_parameter_t const ts_di_plus_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_di_plus_params_t) }; + +uint_t ts_di_plus_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_di_plus_params_t *)c)->k : 0 ); +} + +int ts_di_plus( + 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 +) +{ + TS_PROLOGUE( ts_di_plus_p ); + if ( !c ) return -1; + uint_t k = ((ts_di_plus_params_t *)c)->k; + if (k == 0 || n < 2) return -1; + fp_t *ema_plus = calloc( n, sizeof( fp_t ) ); + fp_t *ema_minus = calloc( n, sizeof( fp_t ) ); + if( !ema_plus || !ema_minus ) { free( ema_plus ); free( ema_minus ); return -1; } + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hdmd = ts_h( x, i ) - ts_h( x, i - 1 ); + fp_t ldml = ts_l( x, i - 1 ) - ts_l( x, i ); + + fp_t plus_dm = hdmd > ldml && hdmd > 0 ? hdmd : 0; + fp_t minus_dm = ldml > hdmd && ldml > 0 ? ldml : 0; + + ema_plus[i] = i == 1 ? plus_dm + : ema_plus[i - 1] + ( 1.0 / k ) * ( plus_dm - ema_plus[i - 1] ); + ema_minus[i] = i == 1 ? minus_dm + : ema_minus[i - 1] + ( 1.0 / k ) * ( minus_dm - ema_minus[i - 1] ); + } + + for (uint_t j = 0; j < n && j < k; ++j) y[0][j] = 0; + + for( uint_t i = k; i < n; ++i ) + { + fp_t denom = ema_plus[i] + ema_minus[i]; + y[0][i] = denom != 0 ? 100.0 * ema_plus[i] / denom : 0; + } + + free( ema_plus ); + free( ema_minus ); + return 0; +} + + + diff --git a/lib/digamma.c b/lib/digamma.c new file mode 100644 index 0000000..267fefc --- /dev/null +++ b/lib/digamma.c @@ -0,0 +1,72 @@ +// Digamma Function +// +// digamma - ψ(z) +// +// Logarithmic derivative of the gamma function: ψ(z) = Γ'(z) / Γ(z) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + +static fp_t digamma_impl( fp_t z ) +{ + if (z > 7.0) + { + fp_t sum = log(z) - 1.0 / (2.0 * z) - 1.0 / (12.0 * z * z); + return sum; + } + + fp_t sum = 0.0; + if (z > 0.0 && z < 7.0) + { + fp_t tmp = z; + while (tmp < 7.0) + { + sum -= 1.0 / tmp; + tmp += 1.0; + } + return sum + digamma_impl(tmp); + } + else if (z == 0.0) + { + return -INFINITY; + } + else if (z < 0.0) + { + fp_t n = floor(z); + fp_t frac = z - n; + return digamma_impl(1.0 - frac) - PI / tan(PI * frac); + } + + return digamma_impl(z + 1.0) - 1.0 / z; +} + + +ts_parameter_t const ts_digamma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_digamma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_digamma( + 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 +) +{ + TS_PROLOGUE( ts_digamma_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = digamma_impl( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/disparity_index.c b/lib/disparity_index.c new file mode 100644 index 0000000..ebf5e0d --- /dev/null +++ b/lib/disparity_index.c @@ -0,0 +1,58 @@ +// Disparity Index +// +// disparity_index - DI +// +// (Price - SMA) / SMA * 100, deviation from moving average +// +// Inputs: 1 +// Outputs: 1 (DI %) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_disparity_index_params_t; + +ts_parameter_t const ts_disparity_index_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_disparity_index_params_t) }; + +uint_t ts_disparity_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_disparity_index_params_t *)c)->k > 0 ? ((ts_disparity_index_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_disparity_index( + 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 +) +{ + TS_PROLOGUE( ts_disparity_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_disparity_index_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum = 0; + for ( uint_t i = 0; i < k; ++i ) + { + sum += x[0][i]; + } + + fp_t sma = sum / ( fp_t )k; + + for ( uint_t i = k - 1; i < n; ++i ) + { + if ( i >= k ) + { + sma = sma + ( x[0][i] - x[0][i - k] ) / ( fp_t )k; + } + + y[0][i] = sma != 0 ? ( x[0][i] - sma ) / sma * 100.0 : 0; + } + return 0; +} + + + diff --git a/lib/distance.c b/lib/distance.c new file mode 100644 index 0000000..5e473f8 --- /dev/null +++ b/lib/distance.c @@ -0,0 +1,39 @@ +// Euclidean Distance +// +// distance - dist +// +// √(x² + y²), 2D Euclidean distance from origin +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_distance_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_distance_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_distance( + 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 +) +{ + TS_PROLOGUE( ts_distance_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = sqrt( x[0][i] * x[0][i] + x[1][i] * x[1][i] ); + } + return 0; +} + + + diff --git a/lib/division.c b/lib/division.c new file mode 100644 index 0000000..e132a77 --- /dev/null +++ b/lib/division.c @@ -0,0 +1,41 @@ +// Division +// +// division - div +// +// f( x_0, x_1 ) = x_0 / x_1 +// +// X_n - 2 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_division_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_division_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_division( + 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 +) +{ + TS_PROLOGUE( ts_division_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[1][i] == 0.0 ? 0.0 : x[0][i] / x[1][i]; + } + return 0; +} + + + diff --git a/lib/donchian_channel.c b/lib/donchian_channel.c new file mode 100644 index 0000000..760c396 --- /dev/null +++ b/lib/donchian_channel.c @@ -0,0 +1,60 @@ +// Donchian Channel +// +// donchian_channel - DC +// +// Upper: highest high over k periods +// Lower: lowest low over k periods +// Middle: (Upper + Lower) / 2 +// +// Inputs: 5 (OHLCV) +// Outputs: 3 (Upper, Lower, Middle) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_donchian_channel_params_t; + +ts_parameter_t const ts_donchian_channel_p = { .n_inputs = 4, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_donchian_channel_params_t) }; + +uint_t ts_donchian_channel_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_donchian_channel_params_t *)c)->k > 0 ? ((ts_donchian_channel_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_donchian_channel( + 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 +) +{ + TS_PROLOGUE( ts_donchian_channel_p ); + if ( !c ) return -1; + uint_t k = ((ts_donchian_channel_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t upper = ts_h( x, i - k + 1 ); + fp_t lower = ts_l( x, i - k + 1 ); + + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_h( x, j ) > upper ) + upper = ts_h( x, j ); + if ( ts_l( x, j ) < lower ) + lower = ts_l( x, j ); + } + + y[0][i] = upper; + y[1][i] = lower; + y[2][i] = ( upper + lower ) / 2.0; + } + return 0; +} + + + diff --git a/lib/donchian_width.c b/lib/donchian_width.c new file mode 100644 index 0000000..c69936f --- /dev/null +++ b/lib/donchian_width.c @@ -0,0 +1,56 @@ +// Donchian Width +// +// donchian_width - DW +// +// Highest high - lowest low over k periods +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (Width) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_donchian_width_params_t; + +ts_parameter_t const ts_donchian_width_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_donchian_width_params_t) }; + +uint_t ts_donchian_width_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_donchian_width_params_t *)c)->k > 0 ? ((ts_donchian_width_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_donchian_width( + 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 +) +{ + TS_PROLOGUE( ts_donchian_width_p ); + if ( !c ) return -1; + uint_t k = ((ts_donchian_width_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t upper = ts_h( x, i - k + 1 ); + fp_t lower = ts_l( x, i - k + 1 ); + + for ( uint_t j = i - k + 1; j <= i; ++j ) + { + if ( ts_h( x, j ) > upper ) + upper = ts_h( x, j ); + if ( ts_l( x, j ) < lower ) + lower = ts_l( x, j ); + } + + y[0][i] = upper - lower; + } + return 0; +} + + + diff --git a/lib/dpo.c b/lib/dpo.c new file mode 100644 index 0000000..5bf069b --- /dev/null +++ b/lib/dpo.c @@ -0,0 +1,78 @@ +// Detrended Price Oscillator +// +// dpo - DPO +// +// Price[t] - SMA[k][t - k/2 - 1], centerless moving average +// +// Inputs: 1 +// Outputs: 1 (DPO) +// Constants: 1 (period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_dpo_params_t; + +ts_parameter_t const ts_dpo_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_dpo_params_t) }; + +uint_t ts_dpo_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_dpo_params_t *)c)->k / 2 + 1 : 0 ); +} + +int ts_dpo( + 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 +) +{ + TS_PROLOGUE( ts_dpo_p ); + if ( !c ) return -1; + uint_t k = ((ts_dpo_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t sma_period = k; + uint_t offset = k / 2 + 1; + + if (n < sma_period) return -1; + + fp_t sum = 0; + for ( uint_t i = 0; i < sma_period; ++i ) + { + sum += x[0][i]; + } + + fp_t *sma_values = calloc( n, sizeof( fp_t ) ); + if ( !sma_values ) return -1; + + for (uint_t i = 0; i < offset; ++i) y[0][i] = 0; + + uint_t sma_idx = 0; + + for ( uint_t i = sma_period - 1; i < n; ++i ) + { + if ( i == sma_period - 1 ) + { + sma_values[sma_idx] = sum / ( fp_t )sma_period; + } + else + { + sma_values[sma_idx] = sma_values[sma_idx - 1] + ( x[0][i] - x[0][i - sma_period] ) / ( fp_t )sma_period; + } + sma_idx++; + + if ( i >= offset ) + { + y[0][i] = x[0][i] - sma_values[i - offset]; + } + } + + free( sma_values ); + return 0; +} + + diff --git a/lib/dx.c b/lib/dx.c new file mode 100644 index 0000000..b4552fa --- /dev/null +++ b/lib/dx.c @@ -0,0 +1,79 @@ +// Directional Movement Index +// +// dx - DX +// +// 100 * |DI+ - DI-| / (DI+ + DI-), trend strength without smoothing +// +// Inputs: 4 (OHLC) +// Outputs: 1 (DX) +// Constants: 1 (period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_dx_params_t; + +ts_parameter_t const ts_dx_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_dx_params_t) }; + +uint_t ts_dx_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_dx_params_t *)c)->k : 0 ); +} + +int ts_dx( + 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 +) +{ + TS_PROLOGUE( ts_dx_p ); + if ( !c ) return -1; + uint_t k = ((ts_dx_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t *plus_dm_arr = calloc( n, sizeof( fp_t ) ); + fp_t *minus_dm_arr = calloc( n, sizeof( fp_t ) ); + fp_t *tr_arr = calloc( n, sizeof( fp_t ) ); + if( !plus_dm_arr || !minus_dm_arr || !tr_arr ) { free( plus_dm_arr ); free( minus_dm_arr ); free( tr_arr ); return -1; } + + y[0][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hdmd = ts_h( x, i ) - ts_h( x, i - 1 ); + fp_t ldml = ts_l( x, i - 1 ) - ts_l( x, i ); + + fp_t plus_dm = hdmd > ldml && hdmd > 0 ? hdmd : 0; + fp_t minus_dm = ldml > hdmd && ldml > 0 ? ldml : 0; + + fp_t tr = fmax( ts_h( x, i ) - ts_l( x, i ), + fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), + fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + + plus_dm_arr[i] = i == 1 ? plus_dm + : plus_dm_arr[i - 1] + ( 1.0 / k ) * ( plus_dm - plus_dm_arr[i - 1] ); + minus_dm_arr[i] = i == 1 ? minus_dm + : minus_dm_arr[i - 1] + ( 1.0 / k ) * ( minus_dm - minus_dm_arr[i - 1] ); + tr_arr[i] = i == 1 ? tr + : tr_arr[i - 1] + ( 1.0 / k ) * ( tr - tr_arr[i - 1] ); + } + + for( uint_t i = k; i < n; ++i ) + { + fp_t di_plus = tr_arr[i] != 0 ? 100.0 * plus_dm_arr[i] / tr_arr[i] : 0; + fp_t di_minus = tr_arr[i] != 0 ? 100.0 * minus_dm_arr[i] / tr_arr[i] : 0; + fp_t sum = di_plus + di_minus; + y[0][i] = sum != 0 ? 100.0 * fabs( di_plus - di_minus ) / sum : 0; + } + + free( plus_dm_arr ); + free( minus_dm_arr ); + free( tr_arr ); + return 0; +} + + diff --git a/lib/ease_of_movement.c b/lib/ease_of_movement.c new file mode 100644 index 0000000..d16f32c --- /dev/null +++ b/lib/ease_of_movement.c @@ -0,0 +1,71 @@ +// Ease of Movement +// +// ease_of_movement - EOM +// +// SMA of (midpoint change) / (volume / range), volume-based momentum +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (EOM) +// Constants: 1 (period) + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_ease_of_movement_params_t; + +ts_parameter_t const ts_ease_of_movement_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_ease_of_movement_params_t) }; + +uint_t ts_ease_of_movement_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 3 ); +} + +int ts_ease_of_movement( + 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 +) +{ + TS_PROLOGUE( ts_ease_of_movement_p ); + if ( !c ) return -1; + uint_t k = ((ts_ease_of_movement_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + fp_t *eom = calloc( n, sizeof( fp_t ) ); + if( !eom ) return -1; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t hl = ts_h( x, i ) - ts_l( x, i ); + if( hl == 0 ) continue; + fp_t br = ts_v( x, i ) / hl; + if( br == 0 ) continue; + fp_t cur_mid = ( ts_h( x, i ) + ts_l( x, i ) ) / 2.0; + fp_t prev_mid = ( ts_h( x, i - 1 ) + ts_l( x, i - 1 ) ) / 2.0; + eom[i] = ( cur_mid - prev_mid ) / br; + } + + { + for (uint_t i = 0; i < k; ++i) y[0][i] = 0; + + fp_t sum = 0; + for( uint_t i = 1; i <= k; ++i ) + { + sum += eom[i]; + } + y[0][k] = sum / (fp_t)k; + for( uint_t i = k + 1; i < n; ++i ) + { + sum = sum - eom[i - k] + eom[i]; + y[0][i] = sum / (fp_t)k; + } + } + + free( eom ); + return 0; +} + + diff --git a/lib/elder_force_index.c b/lib/elder_force_index.c new file mode 100644 index 0000000..23cce66 --- /dev/null +++ b/lib/elder_force_index.c @@ -0,0 +1,52 @@ +// Elder Force Index +// +// elder_force_index - EFI +// +// EMA((Close - PrevClose) * Volume), volume-weighted price change +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (EFI) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_elder_force_index_params_t; + +ts_parameter_t const ts_elder_force_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_elder_force_index_params_t) }; + +uint_t ts_elder_force_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_elder_force_index( + 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 +) +{ + TS_PROLOGUE( ts_elder_force_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_elder_force_index_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t a = 1.0 / k; + + fp_t fi = 0; + if ( n > 1 ) + fi = ( ts_c( x, 1 ) - ts_c( x, 0 ) ) * ts_v( x, 1 ); + y[0][0] = fi; + + for( uint_t i = 1; i < n; ++i ) + { + fi = ( ts_c( x, i ) - ts_c( x, i - 1 ) ) * ts_v( x, i ); + y[0][i] = y[0][i - 1] + a * ( fi - y[0][i - 1] ); + } + + return 0; +} + + diff --git a/lib/elder_impulse.c b/lib/elder_impulse.c new file mode 100644 index 0000000..d629e7c --- /dev/null +++ b/lib/elder_impulse.c @@ -0,0 +1,70 @@ +// Elder Impulse System +// +// elder_impulse - EIS +// +// Impulse: Price - SMA(k), Trend: SMA(Impulse, k) +// +// Inputs: 1 +// Outputs: 2 (Impulse, Trend) +// Constants: 1 (period) + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_elder_impulse_params_t; + +ts_parameter_t const ts_elder_impulse_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_elder_impulse_params_t) }; + +uint_t ts_elder_impulse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? 2 * ((ts_elder_impulse_params_t *)c)->k - 1 : 0 ); +} + +int ts_elder_impulse( + 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 +) +{ + TS_PROLOGUE( ts_elder_impulse_p ); + if ( !c ) return -1; + uint_t k = ((ts_elder_impulse_params_t *)c)->k; + if (k == 0 || n < 2 * k - 1) return -1; + + fp_t *sma = malloc( n * sizeof( fp_t ) ); + fp_t *impulse = malloc( n * sizeof( fp_t ) ); + if ( !sma || !impulse ) { free( sma ); free( impulse ); return -1; } + + for (uint_t i = 0; i < 2 * k - 1; ++i) { y[0][i] = 0; y[1][i] = 0; } + + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0; + for ( uint_t j = 0; j < k; ++j ) + { + sum += x[0][i - j]; + } + sma[i] = sum / (fp_t)k; + impulse[i] = x[0][i] - sma[i]; + } + + for ( uint_t i = 2 * k - 1; i < n; ++i ) + { + y[0][i] = impulse[i]; + + fp_t sum = 0; + for ( uint_t j = 0; j < k; ++j ) + { + sum += impulse[i - j]; + } + y[1][i] = sum / (fp_t)k; + } + + free( sma ); free( impulse ); + return 0; +} + + diff --git a/lib/elder_ray.c b/lib/elder_ray.c new file mode 100644 index 0000000..a0f330f --- /dev/null +++ b/lib/elder_ray.c @@ -0,0 +1,58 @@ +// Elder Ray Index +// +// elder_ray - ERI +// +// Bull Power: High - EMA(Close) +// Bear Power: Low - EMA(Close) +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Bull Power, Bear Power) +// Constants: 1 (EMA period) + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + fp_t k; +} ts_elder_ray_params_t; + +ts_parameter_t const ts_elder_ray_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_elder_ray_params_t) }; + +uint_t ts_elder_ray_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_elder_ray( + 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 +) +{ + TS_PROLOGUE( ts_elder_ray_p ); + if ( !c ) return -1; + fp_t k = ((ts_elder_ray_params_t *)c)->k; + if (k == 0 || n < k) return -1; + fp_t a = 1.0 / k; + fp_t *ema = calloc( n, sizeof( fp_t ) ); + if( !ema ) return -1; + + ema[0] = ts_c( x, 0 ); + + for( uint_t i = 0; i < n; ++i ) + { + if( i > 0 ) + { + ema[i] = ema[i - 1] + a * ( ts_c( x, i ) - ema[i - 1] ); + } + y[0][i] = ts_h( x, i ) - ema[i]; + y[1][i] = ts_l( x, i ) - ema[i]; + } + + free( ema ); + return 0; +} + + diff --git a/lib/ellint_1.c b/lib/ellint_1.c new file mode 100644 index 0000000..8e0bab7 --- /dev/null +++ b/lib/ellint_1.c @@ -0,0 +1,65 @@ +// Complete Elliptic Integral of the First Kind +// +// ellint_1 - K(m) +// +// K(m) = ∫₀^(π/2) dθ / √(1 - m*sin²(θ)), computed via AGM +// +// Inputs: 1 (parameter m) +// Outputs: 1 (K(m)) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_ellint_1_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_ellint_1_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ellint_1( + 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 +) +{ + TS_PROLOGUE( ts_ellint_1_p ); + (void)c; + /* Complete elliptic integral of the first kind K(m) */ + for (uint_t i = 0; i < n; ++i) + { + fp_t m = x[0][i]; + if (m <= 0.0) + { + y[0][i] = PI / 2.0; + } + else if (m >= 1.0) + { + y[0][i] = INFINITY; + } + else + { + /* Arithmetic-geometric mean method */ + fp_t a = 1.0; + fp_t b = sqrt(1.0 - m); + fp_t cn = (a - b) / 2.0; + for (uint_t j = 0; j < 30; ++j) + { + fp_t a_new = (a + b) / 2.0; + fp_t b_new = sqrt(a * b); + fp_t cn_new = (a_new - b_new) / 2.0; + a = a_new; + b = b_new; + cn = cn_new; + if (cn / a < 1e-15) break; + } + y[0][i] = PI / (2.0 * a); + } + } + return 0; +} + + diff --git a/lib/ellint_2.c b/lib/ellint_2.c new file mode 100644 index 0000000..59660cd --- /dev/null +++ b/lib/ellint_2.c @@ -0,0 +1,64 @@ +// Complete Elliptic Integral of the Second Kind +// +// ellint_2 - E(m) +// +// E(m) = ∫₀^(π/2) √(1 - m*sin²(θ)) dθ, computed via AGM +// +// Inputs: 1 (parameter m) +// Outputs: 1 (E(m)) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_ellint_2_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_ellint_2_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ellint_2( + 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 +) +{ + TS_PROLOGUE( ts_ellint_2_p ); + (void)c; + for (uint_t i = 0; i < n; ++i) + { + fp_t m = x[0][i]; + if (m <= 0.0) + { + y[0][i] = PI / 2.0; + } + else if (m >= 1.0) + { + y[0][i] = 1.0; + } + else + { + fp_t a = 1.0; + fp_t b = sqrt(1.0 - m); + fp_t s = 0.0; + fp_t scale = 0.5; + for (uint_t j = 0; j < 30; ++j) + { + fp_t a_new = (a + b) / 2.0; + fp_t b_new = sqrt(a * b); + fp_t c_new = (a - b) / 2.0; + s += scale * c_new * c_new; + a = a_new; + b = b_new; + scale *= 2.0; + if (c_new < 1e-15) break; + } + y[0][i] = (PI / (2.0 * a)) * (1.0 - s); + } + } + return 0; +} + diff --git a/lib/ema_variable.c b/lib/ema_variable.c new file mode 100644 index 0000000..c20fee2 --- /dev/null +++ b/lib/ema_variable.c @@ -0,0 +1,48 @@ +// Variable EMA +// +// ema_variable - EMA +// +// EMA with alpha = 2/(k+1): y = prev + alpha * (x - prev) +// +// Inputs: 1 +// Outputs: 1 (EMA) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t k; +} ts_ema_variable_params_t; + +ts_parameter_t const ts_ema_variable_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_ema_variable_params_t) }; + +uint_t ts_ema_variable_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ema_variable( + 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 +) +{ + TS_PROLOGUE( ts_ema_variable_p ); + if ( !c ) return -1; + fp_t k = ((ts_ema_variable_params_t *)c)->k; + if (k <= 0) return -1; + if (n == 0) return -1; + + fp_t alpha = 2.0 / (k + 1.0); + y[0][0] = x[0][0]; + + for (uint_t i = 1; i < n; ++i) + { + y[0][i] = y[0][i - 1] + alpha * (x[0][i] - y[0][i - 1]); + } + return 0; +} + + diff --git a/lib/envelope.c b/lib/envelope.c new file mode 100644 index 0000000..1ddda0c --- /dev/null +++ b/lib/envelope.c @@ -0,0 +1,60 @@ +// Price Envelope +// +// envelope - ENV +// +// Upper: SMA * (1 + pct), Lower: SMA * (1 - pct) +// +// Inputs: 1 +// Outputs: 2 (Upper, Lower) +// Constants: 2 (period, percentage) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t pct; +} ts_envelope_params_t; + +ts_parameter_t const ts_envelope_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_envelope_params_t) }; + +uint_t ts_envelope_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_envelope_params_t *)c)->k > 0 ? ((ts_envelope_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_envelope( + 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 +) +{ + TS_PROLOGUE( ts_envelope_p ); + if ( !c ) return -1; + uint_t k = ((ts_envelope_params_t *)c)->k; + fp_t pct = ((ts_envelope_params_t *)c)->pct; + if (k == 0 || n < k) return -1; + + fp_t sum = 0; + for ( uint_t i = 0; i < k; ++i ) + { + sum += x[0][i]; + } + + fp_t sma = sum / ( fp_t )k; + + for ( uint_t i = k - 1; i < n; ++i ) + { + if ( i >= k ) + { + sma = sma + ( x[0][i] - x[0][i - k] ) / ( fp_t )k; + } + + y[0][i] = sma * ( 1.0 + pct ); + y[1][i] = sma * ( 1.0 - pct ); + } + return 0; +} + + diff --git a/lib/erf.c b/lib/erf.c new file mode 100644 index 0000000..54bf625 --- /dev/null +++ b/lib/erf.c @@ -0,0 +1,38 @@ +// Error Function +// +// erf - error function +// +// erf(x) = (2/√π) * ∫₀ˣ e^(-t²) dt +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_erf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_erf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_erf( + 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 +) +{ + TS_PROLOGUE( ts_erf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = erf( x[0][i] ); + } + return 0; +} + + diff --git a/lib/erfc.c b/lib/erfc.c new file mode 100644 index 0000000..e6e1492 --- /dev/null +++ b/lib/erfc.c @@ -0,0 +1,38 @@ +// Complementary Error Function +// +// erfc - complementary error function +// +// erfc(x) = 1 - erf(x) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_erfc_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_erfc_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_erfc( + 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 +) +{ + TS_PROLOGUE( ts_erfc_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = erfc( x[0][i] ); + } + return 0; +} + + diff --git a/lib/euclidean_norm.c b/lib/euclidean_norm.c new file mode 100644 index 0000000..6de406d --- /dev/null +++ b/lib/euclidean_norm.c @@ -0,0 +1,38 @@ +// Euclidean Norm +// +// euclidean_norm - L2 norm +// +// √(x² + y²), 2D vector magnitude +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_euclidean_norm_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_euclidean_norm_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_euclidean_norm( + 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 +) +{ + TS_PROLOGUE( ts_euclidean_norm_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = hypot( x[0][i], x[1][i] ); + } + return 0; +} + + diff --git a/lib/exp10.c b/lib/exp10.c new file mode 100644 index 0000000..b4bb568 --- /dev/null +++ b/lib/exp10.c @@ -0,0 +1,40 @@ +// Base-10 Exponential +// +// exp10 - 10^x +// +// y = 10^x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_exp10_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_exp10_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exp10( + 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 +) +{ + TS_PROLOGUE( ts_exp10_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = pow( 10.0, x[0][i] ); + } + return 0; +} + + diff --git a/lib/exp10m1.c b/lib/exp10m1.c new file mode 100644 index 0000000..bf19615 --- /dev/null +++ b/lib/exp10m1.c @@ -0,0 +1,38 @@ +// Base-10 Exponential Minus 1 +// +// exp10m1 - 10^x - 1 +// +// y = 10^x - 1, computed as expm1(x * ln(10)) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_exp10m1_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_exp10m1_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exp10m1( + 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 +) +{ + TS_PROLOGUE( ts_exp10m1_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = expm1( x[0][i] * log( 10.0 ) ); + } + return 0; +} + + diff --git a/lib/exp2.c b/lib/exp2.c new file mode 100644 index 0000000..b70bf1c --- /dev/null +++ b/lib/exp2.c @@ -0,0 +1,40 @@ +// Base-2 Exponential +// +// exp2 - 2^x +// +// y = 2^x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_exp2_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_exp2_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exp2( + 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 +) +{ + TS_PROLOGUE( ts_exp2_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = exp2( x[0][i] ); + } + return 0; +} + + diff --git a/lib/exp2m1.c b/lib/exp2m1.c new file mode 100644 index 0000000..d0c6fb1 --- /dev/null +++ b/lib/exp2m1.c @@ -0,0 +1,38 @@ +// Base-2 Exponential Minus 1 +// +// exp2m1 - 2^x - 1 +// +// y = 2^x - 1, computed as expm1(x * ln(2)) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_exp2m1_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_exp2m1_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exp2m1( + 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 +) +{ + TS_PROLOGUE( ts_exp2m1_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = expm1( x[0][i] * log( 2.0 ) ); + } + return 0; +} + + diff --git a/lib/expit.c b/lib/expit.c new file mode 100644 index 0000000..e58b528 --- /dev/null +++ b/lib/expit.c @@ -0,0 +1,40 @@ +// Inverse Logit (Expit) +// +// expit - logistic function +// +// y = 1 / (1 + e^(-x)) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_expit_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_expit_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_expit( + 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 +) +{ + TS_PROLOGUE( ts_expit_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = 1.0 / ( 1.0 + exp( -x[0][i] ) ); + } + return 0; +} + + diff --git a/lib/expm1.c b/lib/expm1.c new file mode 100644 index 0000000..c6cb7a2 --- /dev/null +++ b/lib/expm1.c @@ -0,0 +1,40 @@ +// Exponential Minus 1 +// +// expm1 - e^x - 1 +// +// y = e^x - 1, accurate for small x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_expm1_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_expm1_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_expm1( + 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 +) +{ + TS_PROLOGUE( ts_expm1_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = expm1( x[0][i] ); + } + return 0; +} + + diff --git a/lib/exponential.c b/lib/exponential.c new file mode 100644 index 0000000..b3a6205 --- /dev/null +++ b/lib/exponential.c @@ -0,0 +1,49 @@ +// Exponential +// +// exponential - exp +// +// f( x ) = b^x +// +// X_n - 1 +// Y_n - 1 +// C_n - 1 +// +// c[0] = b + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t b; +} ts_exponential_params_t; + +ts_parameter_t const ts_exponential_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_exponential_params_t) }; + + +uint_t ts_exponential_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exponential( + 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 +) +{ + TS_PROLOGUE( ts_exponential_p ); + if ( !c ) return -1; + fp_t b = ((ts_exponential_params_t *)c)->b; + if (b <= 0) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = pow( b, x[0][i] ); + } + return 0; +} + + diff --git a/lib/exponential_cdf.c b/lib/exponential_cdf.c new file mode 100644 index 0000000..95c7ba8 --- /dev/null +++ b/lib/exponential_cdf.c @@ -0,0 +1,38 @@ +// Exponential CDF +// +// exponential_cdf - exponential cumulative distribution +// +// F(x) = 1 - e^(-x) for x >= 0, else 0 +// +// Inputs: 1 +// Outputs: 1 (CDF value) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_exponential_cdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_exponential_cdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exponential_cdf( + 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 +) +{ + TS_PROLOGUE( ts_exponential_cdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] >= 0 ? 1.0 - exp( -x[0][i] ) : 0.0; + } + return 0; +} + + diff --git a/lib/exponential_natural.c b/lib/exponential_natural.c new file mode 100644 index 0000000..61a217c --- /dev/null +++ b/lib/exponential_natural.c @@ -0,0 +1,41 @@ +// Natural Exponential +// +// exponential_natural - expn +// +// f( x ) = e^x +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_exponential_natural_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_exponential_natural_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exponential_natural( + 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 +) +{ + TS_PROLOGUE( ts_exponential_natural_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = exp( x[0][i] ); + } + return 0; +} + + diff --git a/lib/exponential_pdf.c b/lib/exponential_pdf.c new file mode 100644 index 0000000..ccb942a --- /dev/null +++ b/lib/exponential_pdf.c @@ -0,0 +1,38 @@ +// Exponential PDF +// +// exponential_pdf - exponential probability density +// +// f(x) = e^(-x) for x >= 0, else 0 +// +// Inputs: 1 +// Outputs: 1 (PDF value) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_exponential_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_exponential_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_exponential_pdf( + 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 +) +{ + TS_PROLOGUE( ts_exponential_pdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] >= 0 ? exp( -x[0][i] ) : 0.0; + } + return 0; +} + + diff --git a/lib/factorial.c b/lib/factorial.c new file mode 100644 index 0000000..8e64fa3 --- /dev/null +++ b/lib/factorial.c @@ -0,0 +1,47 @@ +// Factorial +// +// factorial - n! +// +// n! = 1 * 2 * ... * n, capped at 170! +// +// Inputs: 1 (n) +// Outputs: 1 (n!) +// Constants: 0 + +#include "ts.h" + + + +ts_parameter_t const ts_factorial_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_factorial_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_factorial( + 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 +) +{ + TS_PROLOGUE( ts_factorial_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] < 0) { y[0][i] = NAN; continue; } + fp_t result = 1.0; + uint_t k = (uint_t)x[0][i]; + if (k > 170) k = 170; + for( uint_t j = 1; j <= k; ++j ) + { + result *= (fp_t)j; + } + y[0][i] = result; + } + return 0; +} + + diff --git a/lib/factorial_int.c b/lib/factorial_int.c new file mode 100644 index 0000000..51250aa --- /dev/null +++ b/lib/factorial_int.c @@ -0,0 +1,45 @@ +// Integer Factorial +// +// factorial_int - n! +// +// n! = 1 * 2 * ... * n, capped at 170! +// +// Inputs: 1 (n) +// Outputs: 1 (n!) +// Constants: 0 + +#include "ts.h" + + +ts_parameter_t const ts_factorial_int_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_factorial_int_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_factorial_int( + 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 +) +{ + TS_PROLOGUE( ts_factorial_int_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] < 0) { y[0][i] = NAN; continue; } + uint_t n_val = (uint_t)x[0][i]; + if (n_val > 170) n_val = 170; + fp_t result = 1.0; + for( uint_t k = 2; k <= n_val; ++k ) + { + result *= (fp_t)k; + } + y[0][i] = result; + } + return 0; +} + + diff --git a/lib/fdim_2input.c b/lib/fdim_2input.c new file mode 100644 index 0000000..8d841ad --- /dev/null +++ b/lib/fdim_2input.c @@ -0,0 +1,38 @@ +// Positive Difference +// +// fdim_2input - fdim(x, 0) +// +// max(x - 0, 0), positive part of x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fdim_2input_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fdim_2input_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fdim_2input( + 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 +) +{ + TS_PROLOGUE( ts_fdim_2input_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fdim( x[0][i], 0.0 ); + } + return 0; +} + + diff --git a/lib/fibonacci.c b/lib/fibonacci.c new file mode 100644 index 0000000..43cd9a0 --- /dev/null +++ b/lib/fibonacci.c @@ -0,0 +1,49 @@ +// Fibonacci Number +// +// fibonacci - F(n) +// +// F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2) +// +// Inputs: 1 (n) +// Outputs: 1 (F(n)) +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fibonacci_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fibonacci_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fibonacci( + 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 +) +{ + TS_PROLOGUE( ts_fibonacci_p ); + (void)c; + for (uint_t i = 0; i < n; ++i) + { + if (x[0][i] < 0) { y[0][i] = 0; continue; } + uint_t k = (uint_t)floor(x[0][i]); + if (k == 0) { y[0][i] = 0; continue; } + if (k == 1) { y[0][i] = 1; continue; } + fp_t a = 0, b = 1, cv = 0; + for (uint_t j = 2; j <= k; ++j) + { + cv = a + b; + a = b; + b = cv; + } + y[0][i] = cv; + } + return 0; +} + + diff --git a/lib/fill_indicator.c b/lib/fill_indicator.c new file mode 100644 index 0000000..9cb9cbc --- /dev/null +++ b/lib/fill_indicator.c @@ -0,0 +1,63 @@ +// Fill Indicator +// +// fill_indicator - FI +// +// SMA of |C-O|/(H-L)*100, measures candle body fill ratio +// +// Inputs: 4 (OHLC) +// Outputs: 1 (FI) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_fill_indicator_params_t; + +ts_parameter_t const ts_fill_indicator_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_fill_indicator_params_t) }; + +uint_t ts_fill_indicator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_fill_indicator_params_t *)c)->k > 0 ? ((ts_fill_indicator_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_fill_indicator( + 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 +) +{ + TS_PROLOGUE( ts_fill_indicator_p ); + if ( !c ) return -1; + uint_t k = ((ts_fill_indicator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k - 1; + + fp_t sum = 0; + + for( ; xi < k; ++xi ) + { + fp_t range = ts_h( x, xi ) - ts_l( x, xi ); + if (range != 0) + sum += fabs( ts_c( x, xi ) - ts_o( x, xi ) ) / range * 100; + } + + y[0][yi] = sum / k; + ++yi; + + for( ; xi < n; ++xi, ++yi ) + { + fp_t range = ts_h( x, xi ) - ts_l( x, xi ); + fp_t fv = range != 0 ? fabs( ts_c( x, xi ) - ts_o( x, xi ) ) / range * 100 : 0; + fp_t range_old = ts_h( x, xi - k ) - ts_l( x, xi - k ); + fp_t fo = range_old != 0 ? fabs( ts_c( x, xi - k ) - ts_o( x, xi - k ) ) / range_old * 100 : 0; + y[0][yi] = y[0][yi - 1] + ( fv - fo ) / k; + } + return 0; +} + + diff --git a/lib/fisher_transform.c b/lib/fisher_transform.c new file mode 100644 index 0000000..2b416ba --- /dev/null +++ b/lib/fisher_transform.c @@ -0,0 +1,78 @@ +// Fisher Transform +// +// fisher_transform - FT +// +// 0.5 * log(price_position / (1 - price_position)), normalized to range +// +// Inputs: 4 (OHLC) +// Outputs: 2 (Transform, Trigger) +// Constants: 1 (period) + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_fisher_transform_params_t; + +ts_parameter_t const ts_fisher_transform_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_fisher_transform_params_t) }; + +uint_t ts_fisher_transform_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_fisher_transform_params_t *)c)->k - 1 : 0 ); +} + +int ts_fisher_transform( + 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 +) +{ + TS_PROLOGUE( ts_fisher_transform_p ); + if ( !c ) return -1; + uint_t k = ((ts_fisher_transform_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = k - 1; i < n; ++i) + { + /* Find high and low over window */ + fp_t hh = x[1][i - k + 1]; + fp_t ll = x[2][i - k + 1]; + for (uint_t j = i - k + 2; j <= i; ++j) + { + if (x[1][j] > hh) hh = x[1][j]; + if (x[2][j] < ll) ll = x[2][j]; + } + + fp_t range = hh - ll; + fp_t val = range != 0 ? (x[3][i] - ll) / range : 0.5; + + /* Clamp to [0.01, 0.99] to avoid log(0) */ + if (val < 0.01) val = 0.01; + if (val > 0.99) val = 0.99; + + fp_t fisher = 0.5 * log(val / (1.0 - val)); + + /* Smooth with previous value (skip first output) */ + uint_t out_i = i; + if (i > k - 1) + { + fisher = fisher + 0.5 * y[0][out_i - 1]; + } + y[0][out_i] = fisher; + + /* Trigger line = previous fisher */ + if (out_i > 0) + { + y[1][out_i] = y[0][out_i - 1]; + } + else + { + y[1][out_i] = 0; + } + } + return 0; +} + + diff --git a/lib/floor.c b/lib/floor.c new file mode 100644 index 0000000..0a77858 --- /dev/null +++ b/lib/floor.c @@ -0,0 +1,41 @@ +// Floor +// +// floor - floor +// +// f( x ) = ⌊ x ⌋ +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_floor_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_floor_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_floor( + 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 +) +{ + TS_PROLOGUE( ts_floor_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = floor( x[0][i] ); + } + return 0; +} + + diff --git a/lib/fma.c b/lib/fma.c new file mode 100644 index 0000000..795dc08 --- /dev/null +++ b/lib/fma.c @@ -0,0 +1,38 @@ +// Fused Multiply-Add +// +// fma - fused multiply-add +// +// x * y + z, computed in single precision +// +// Inputs: 3 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fma_p = { .n_inputs = 3, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fma( + 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 +) +{ + TS_PROLOGUE( ts_fma_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fma( x[0][i], x[1][i], x[2][i] ); + } + return 0; +} + + diff --git a/lib/fmax.c b/lib/fmax.c new file mode 100644 index 0000000..63eb5a7 --- /dev/null +++ b/lib/fmax.c @@ -0,0 +1,38 @@ +// Maximum +// +// fmax - max(x, y) +// +// Returns the maximum of two values +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fmax_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fmax_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fmax( + 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 +) +{ + TS_PROLOGUE( ts_fmax_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmax( x[0][i], x[1][i] ); + } + return 0; +} + + diff --git a/lib/fmax_2input.c b/lib/fmax_2input.c new file mode 100644 index 0000000..b29eee5 --- /dev/null +++ b/lib/fmax_2input.c @@ -0,0 +1,38 @@ +// Maximum with Zero +// +// fmax_2input - max(x, 0) +// +// Returns the maximum of x and 0 +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fmax_2input_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fmax_2input_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fmax_2input( + 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 +) +{ + TS_PROLOGUE( ts_fmax_2input_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmax( x[0][i], 0.0 ); + } + return 0; +} + + diff --git a/lib/fmin.c b/lib/fmin.c new file mode 100644 index 0000000..5820841 --- /dev/null +++ b/lib/fmin.c @@ -0,0 +1,38 @@ +// Minimum +// +// fmin - min(x, y) +// +// Returns the minimum of two values +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fmin_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fmin_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fmin( + 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 +) +{ + TS_PROLOGUE( ts_fmin_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmin( x[0][i], x[1][i] ); + } + return 0; +} + + diff --git a/lib/fmin_2input.c b/lib/fmin_2input.c new file mode 100644 index 0000000..ba4cd5c --- /dev/null +++ b/lib/fmin_2input.c @@ -0,0 +1,38 @@ +// Minimum with Zero +// +// fmin_2input - min(x, 0) +// +// Returns the minimum of x and 0 +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fmin_2input_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fmin_2input_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fmin_2input( + 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 +) +{ + TS_PROLOGUE( ts_fmin_2input_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmin( x[0][i], 0.0 ); + } + return 0; +} + + diff --git a/lib/fmod.c b/lib/fmod.c new file mode 100644 index 0000000..75317bd --- /dev/null +++ b/lib/fmod.c @@ -0,0 +1,38 @@ +// Floating Modulo +// +// fmod - floating-point remainder +// +// x mod y, remainder of x/y +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_fmod_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fmod_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_fmod( + 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 +) +{ + TS_PROLOGUE( ts_fmod_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fmod( x[0][i], x[1][i] ); + } + return 0; +} + + diff --git a/lib/fobv.c b/lib/fobv.c new file mode 100644 index 0000000..7134cca --- /dev/null +++ b/lib/fobv.c @@ -0,0 +1,43 @@ +// On-Balance Volume +// +// fobv - OBV +// +// Cumulative volume: add on up days, subtract on down days +// +// Inputs: 5 (OHLCV) +// Outputs: 1 (OBV) +// Constants: 0 + +#include "ts.h" + + +ts_parameter_t const ts_fobv_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_fobv_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_fobv( + 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 +) +{ + TS_PROLOGUE( ts_fobv_p ); + (void)c; + if (n > 0) y[0][0] = 0; + fp_t obv = 0; + + for (uint_t i = 1; i < n; ++i) + { + if (x[3][i] > x[3][i - 1]) + obv += x[4][i]; + else if (x[3][i] < x[3][i - 1]) + obv -= x[4][i]; + y[0][i] = obv; + } + return 0; +} + diff --git a/lib/fractal_chaos_bands.c b/lib/fractal_chaos_bands.c new file mode 100644 index 0000000..57bbf00 --- /dev/null +++ b/lib/fractal_chaos_bands.c @@ -0,0 +1,74 @@ +// Fractal Chaos Bands +// +// fractal_chaos_bands +// +// Elder's Fractal Chaos Bands using fractal channels +// +// Upper/Lower: fractal-based channel boundaries +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_fractal_chaos_bands_params_t; + +ts_parameter_t const ts_fractal_chaos_bands_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_fractal_chaos_bands_params_t) }; + +uint_t ts_fractal_chaos_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_fractal_chaos_bands_params_t *)c)->k / 2 : 0 ); +} + +int ts_fractal_chaos_bands( + 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 +) +{ + TS_PROLOGUE( ts_fractal_chaos_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_fractal_chaos_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t alpha = 2.0 / (k + 1.0); + uint_t half_k = k / 2; + + fp_t ema_upper = 0; + fp_t ema_lower = 0; + uint_t ema_count = 0; + + for (uint_t i = half_k; i < n - half_k; ++i) + { + fp_t fh = x[1][i]; + fp_t fl = x[2][i]; + for (int_t j = -(int_t)half_k; j <= (int_t)half_k; ++j) + { + uint_t idx = (uint_t)((int_t)i + j); + if (x[1][idx] > fh) fh = x[1][idx]; + if (x[2][idx] < fl) fl = x[2][idx]; + } + + if (ema_count == 0) + { + ema_upper = fh; + ema_lower = fl; + } + else + { + ema_upper = ema_upper + alpha * (fh - ema_upper); + ema_lower = ema_lower + alpha * (fl - ema_lower); + } + ema_count++; + + y[0][i] = ema_upper; + y[1][i] = ema_lower; + } + return 0; +} + diff --git a/lib/fractal_chaos_oscillator.c b/lib/fractal_chaos_oscillator.c new file mode 100644 index 0000000..5c845d3 --- /dev/null +++ b/lib/fractal_chaos_oscillator.c @@ -0,0 +1,73 @@ +// Fractal Chaos Oscillator +// +// fractal_chaos_oscillator +// +// Fractal Chaos Oscillator +// +// Oscillator derived from fractal chaos bands +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_fractal_chaos_oscillator_params_t; + +ts_parameter_t const ts_fractal_chaos_oscillator_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_fractal_chaos_oscillator_params_t) }; + +uint_t ts_fractal_chaos_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_fractal_chaos_oscillator_params_t *)c)->k / 2 : 0 ); +} + +int ts_fractal_chaos_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_fractal_chaos_oscillator_p ); + if ( !c ) return -1; + uint_t k = ((ts_fractal_chaos_oscillator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t alpha = 2.0 / (k + 1.0); + fp_t ema_upper = 0; + fp_t ema_lower = 0; + uint_t half_k = k / 2; + uint_t init = 0; + + for (uint_t i = half_k; i < n - half_k; ++i) + { + fp_t fh = x[1][i]; + fp_t fl = x[2][i]; + for (int_t j = -(int_t)half_k; j <= (int_t)half_k; ++j) + { + uint_t idx = (uint_t)((int_t)i + j); + if (x[1][idx] > fh) fh = x[1][idx]; + if (x[2][idx] < fl) fl = x[2][idx]; + } + + if (!init) + { + ema_upper = fh; + ema_lower = fl; + init = 1; + } + else + { + ema_upper = ema_upper + alpha * (fh - ema_upper); + ema_lower = ema_lower + alpha * (fl - ema_lower); + } + + fp_t denom = ema_upper + ema_lower; + y[0][i] = (denom != 0) ? (ema_upper - ema_lower) / denom * 100.0 : 0; + } + return 0; +} + diff --git a/lib/gamma_pdf.c b/lib/gamma_pdf.c new file mode 100644 index 0000000..977f8b7 --- /dev/null +++ b/lib/gamma_pdf.c @@ -0,0 +1,48 @@ +// Gamma Pdf +// +// gamma_pdf +// +// Gamma probability density function +// +// f(x; α, β) = β^α * x^(α-1) * e^(-βx) / Γ(α) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t shape; +} ts_gamma_pdf_params_t; + +ts_parameter_t const ts_gamma_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_gamma_pdf_params_t) }; + +uint_t ts_gamma_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_gamma_pdf( + 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 +) +{ + TS_PROLOGUE( ts_gamma_pdf_p ); + if ( !c ) return -1; + fp_t shape = ((ts_gamma_pdf_params_t *)c)->shape; + if (shape <= 0.0) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi > 0 ? pow( xi, shape - 1.0 ) * exp( -xi ) / tgamma( shape ) : 0.0; + } + return 0; +} + + diff --git a/lib/gator_oscillator.c b/lib/gator_oscillator.c new file mode 100644 index 0000000..02bbc59 --- /dev/null +++ b/lib/gator_oscillator.c @@ -0,0 +1,64 @@ +// Gator Oscillator +// +// gator_oscillator +// +// Gator Oscillator from Alligator lines +// +// Histogram of Alligator Teeth-Lips and Jaw-Teeth +// +// Inputs: 1 +// Outputs: 2 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_gator_oscillator_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 0, .params_size = 0 }; + +uint_t ts_gator_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 12 ); +} + +int ts_gator_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_gator_oscillator_p ); + (void)c; + /* Gator Oscillator uses Alligator lines: + * Lips = SMA(5), Teeth = SMA(8), Jaw = SMA(13) + * Upper = |Lips - Teeth|, Lower = |Jaw - Teeth| + * Offset 12 because Jaw (SMA13) starts at index 12 + */ + for (uint_t j = 0; j < n && j < 12; ++j) { y[0][j] = 0; y[1][j] = 0; } + for (uint_t i = 12; i < n; ++i) + { + /* SMA(5) - Lips */ + fp_t lips = 0; + for (uint_t j = i - 4; j <= i; ++j) lips += x[0][j]; + lips /= 5; + + /* SMA(8) - Teeth */ + fp_t teeth = 0; + for (uint_t j = i - 7; j <= i; ++j) teeth += x[0][j]; + teeth /= 8; + + /* SMA(13) - Jaw */ + fp_t jaw = 0; + for (uint_t j = i - 12; j <= i; ++j) jaw += x[0][j]; + jaw /= 13; + + y[0][i] = fabs(lips - teeth); + y[1][i] = fabs(jaw - teeth); + } + return 0; +} + + + diff --git a/lib/gaussian_cdf.c b/lib/gaussian_cdf.c new file mode 100644 index 0000000..2111aae --- /dev/null +++ b/lib/gaussian_cdf.c @@ -0,0 +1,43 @@ +// Gaussian Cdf +// +// gaussian_cdf +// +// Gaussian cumulative distribution function +// +// F(x) = 0.5 * (1 + erf(x/√2)) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_gaussian_cdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_gaussian_cdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_gaussian_cdf( + 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 +) +{ + TS_PROLOGUE( ts_gaussian_cdf_p ); + (void)c; + /* CDF = 0.5 * (1 + erf(x / sqrt(2))) */ + for (uint_t i = 0; i < n; ++i) + { + y[0][i] = 0.5 * (1.0 + erf(x[0][i] / sqrt(2.0))); + } + return 0; +} + + + diff --git a/lib/gaussian_pdf.c b/lib/gaussian_pdf.c new file mode 100644 index 0000000..01acc34 --- /dev/null +++ b/lib/gaussian_pdf.c @@ -0,0 +1,42 @@ +// Gaussian Pdf +// +// gaussian_pdf +// +// Gaussian probability density function +// +// f(x) = (1/√(2π)) * e^(-x²/2) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_gaussian_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_gaussian_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_gaussian_pdf( + 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 +) +{ + TS_PROLOGUE( ts_gaussian_pdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = exp( -0.5 * x[0][i] * x[0][i] ) / sqrt( 2.0 * PI ); + } + return 0; +} + + + diff --git a/lib/gaussian_quantile.c b/lib/gaussian_quantile.c new file mode 100644 index 0000000..337262c --- /dev/null +++ b/lib/gaussian_quantile.c @@ -0,0 +1,94 @@ +// Gaussian Quantile +// +// gaussian_quantile +// +// Gaussian quantile function (inverse CDF) +// +// Q(p) = Φ^(-1)(p) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_gaussian_quantile_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_gaussian_quantile_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_gaussian_quantile( + 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 +) +{ + TS_PROLOGUE( ts_gaussian_quantile_p ); + (void)c; + /* Peter Acklam's approximation for inverse normal CDF */ + /* Coefficients for rational approximation */ + fp_t const a[] = { + -3.969683028665376e+01, 2.209460984245205e+02, + -2.759285104469687e+02, 1.383577518672690e+02, + -3.066479806614716e+01, 2.506628277459239e+00 + }; + fp_t const b[] = { + -5.447609879822406e+01, 1.615858368580409e+02, + -1.556989798598866e+02, 6.680131188771972e+01, + -1.328068155288572e+01 + }; + fp_t const cc[] = { + -7.784894002430993e-03, 3.223964580411365e-01, + -2.400758277161838e+00, -2.549732539343734e+00, + 4.374664141464968e+00, 2.938163982698783e+00 + }; + fp_t const d[] = { + 7.784695709041462e-03, 3.224671290700398e-01, + 2.445134137142996e+00, 3.754408661907416e+00 + }; + + fp_t const p_low = 0.02425; + fp_t const p_high = 1.0 - p_low; + + for (uint_t i = 0; i < n; ++i) + { + fp_t p = x[0][i]; + if (p <= 0.0) + { + y[0][i] = -INFINITY; + continue; + } + if (p >= 1.0) + { + y[0][i] = INFINITY; + continue; + } + + fp_t q, r; + + if (p < p_low) { + q = sqrt(-2.0 * log(p)); + y[0][i] = -(((((cc[0]*q + cc[1])*q + cc[2])*q + cc[3])*q + cc[4])*q + cc[5]) / + ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0); + } else if (p <= p_high) { + q = p - 0.5; + r = q * q; + y[0][i] = (((((a[0]*r + a[1])*r + a[2])*r + a[3])*r + a[4])*r + a[5])*q / + (((((b[0]*r + b[1])*r + b[2])*r + b[3])*r + b[4])*r + 1.0); + } else { + q = sqrt(-2.0 * log(1.0 - p)); + y[0][i] = (((((cc[0]*q + cc[1])*q + cc[2])*q + cc[3])*q + cc[4])*q + cc[5]) / + ((((d[0]*q + d[1])*q + d[2])*q + d[3])*q + 1.0); + } + } + return 0; +} + + + diff --git a/lib/gcd.c b/lib/gcd.c new file mode 100644 index 0000000..44068cf --- /dev/null +++ b/lib/gcd.c @@ -0,0 +1,50 @@ +// Gcd +// +// gcd +// +// Greatest common divisor +// +// gcd(a, b) via Euclidean algorithm +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_gcd_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_gcd_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_gcd( + 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 +) +{ + TS_PROLOGUE( ts_gcd_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + long a = (long)fabs(x[0][i]); + long b = (long)fabs(x[1][i]); + while( b ) + { + long t = b; + b = a % b; + a = t; + } + y[0][i] = (fp_t)a; + } + return 0; +} + + + diff --git a/lib/gopalakrishnan_range_index.c b/lib/gopalakrishnan_range_index.c new file mode 100644 index 0000000..80da0b4 --- /dev/null +++ b/lib/gopalakrishnan_range_index.c @@ -0,0 +1,64 @@ +// Gopalakrishnan Range Index +// +// gopalakrishnan_range_index +// +// Gopalakrishnan Range Index +// +// Measures price range relative to ATR +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_gopalakrishnan_range_index_params_t; + +ts_parameter_t const ts_gopalakrishnan_range_index_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_gopalakrishnan_range_index_params_t) }; + +uint_t ts_gopalakrishnan_range_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_gopalakrishnan_range_index_params_t *)c)->k : 0 ); +} + +int ts_gopalakrishnan_range_index( + 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 +) +{ + TS_PROLOGUE( ts_gopalakrishnan_range_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_gopalakrishnan_range_index_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + fp_t sum_abs = 0; + fp_t sum_range = 0; + + for (uint_t j = 1; j <= k; ++j) + { + sum_abs += fabs( ts_c( x, j ) - ts_c( x, j - 1 ) ); + sum_range += ts_h( x, j ) - ts_l( x, j ); + } + + y[0][k] = sum_range != 0 ? sum_abs / sum_range : 0; + + for (uint_t i = k + 1; i < n; ++i) + { + uint_t dj = i - k; + sum_abs -= fabs( ts_c( x, dj ) - ts_c( x, dj - 1 ) ); + sum_range -= ts_h( x, dj ) - ts_l( x, dj ); + sum_abs += fabs( ts_c( x, i ) - ts_c( x, i - 1 ) ); + sum_range += ts_h( x, i ) - ts_l( x, i ); + y[0][i] = sum_range != 0 ? sum_abs / sum_range : 0; + } + return 0; +} + + + diff --git a/lib/hard_sigmoid.c b/lib/hard_sigmoid.c new file mode 100644 index 0000000..f54d4af --- /dev/null +++ b/lib/hard_sigmoid.c @@ -0,0 +1,47 @@ +// Hard Sigmoid +// +// hard_sigmoid - Hard Sigmoid activation +// +// y[t] = x[t] <= -3 ? 0 : x[t] >= 3 ? 1 : x[t]/6 + 0.5 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_hard_sigmoid_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_hard_sigmoid_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hard_sigmoid( + 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 +) +{ + TS_PROLOGUE( ts_hard_sigmoid_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi <= -3 ) + y[0][i] = 0; + else if ( xi >= 3 ) + y[0][i] = 1; + else + y[0][i] = xi / 6 + 0.5; + } + return 0; +} + + + diff --git a/lib/hard_swish.c b/lib/hard_swish.c new file mode 100644 index 0000000..458ba8d --- /dev/null +++ b/lib/hard_swish.c @@ -0,0 +1,47 @@ +// Hard Swish +// +// hard_swish - Hard Swish activation +// +// y[t] = x[t] <= -3 ? 0 : x[t] >= 3 ? x[t] : x[t] * ( x[t] + 3 ) / 6 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_hard_swish_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_hard_swish_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hard_swish( + 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 +) +{ + TS_PROLOGUE( ts_hard_swish_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi <= -3 ) + y[0][i] = 0; + else if ( xi >= 3 ) + y[0][i] = xi; + else + y[0][i] = xi * ( xi + 3 ) / 6; + } + return 0; +} + + + diff --git a/lib/hard_tanh.c b/lib/hard_tanh.c new file mode 100644 index 0000000..36301d3 --- /dev/null +++ b/lib/hard_tanh.c @@ -0,0 +1,48 @@ +// Hard Tanh +// +// hard_tanh - Hard Tanh activation +// +// y[t] = clamp( x[t], -1, 1 ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_hard_tanh_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_hard_tanh_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hard_tanh( + 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 +) +{ + TS_PROLOGUE( ts_hard_tanh_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi < -1 ) + y[0][i] = -1; + else if ( xi > 1 ) + y[0][i] = 1; + else + y[0][i] = xi; + } + return 0; +} + + + diff --git a/lib/harmonic.c b/lib/harmonic.c new file mode 100644 index 0000000..2c69ee8 --- /dev/null +++ b/lib/harmonic.c @@ -0,0 +1,50 @@ +// Harmonic +// +// harmonic +// +// Harmonic mean +// +// H = n / Σ(1/x_i) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_harmonic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_harmonic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_harmonic( + 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 +) +{ + TS_PROLOGUE( ts_harmonic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] < 0) { y[0][i] = NAN; continue; } + uint_t n_val = (uint_t)x[0][i]; + if (n_val > 10000000) n_val = 10000000; + fp_t sum = 0.0; + for( uint_t k = 1; k <= n_val; ++k ) + { + sum += 1.0 / (fp_t)k; + } + y[0][i] = sum; + } + return 0; +} + + + diff --git a/lib/hermite.c b/lib/hermite.c new file mode 100644 index 0000000..0418c6b --- /dev/null +++ b/lib/hermite.c @@ -0,0 +1,64 @@ +// Hermite +// +// hermite +// +// Hermite polynomial +// +// H_n(x) via recurrence relation +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_hermite_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_hermite_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hermite( + 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 +) +{ + TS_PROLOGUE( ts_hermite_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if (x[1][i] < 0 || x[1][i] > 10000) { y[0][i] = 0; continue; } + uint_t n_val = (uint_t)x[1][i]; + if( n_val == 0 ) + { + y[0][i] = 1.0; + } + else if( n_val == 1 ) + { + y[0][i] = 2.0 * xv; + } + else + { + fp_t h0 = 1.0; + fp_t h1 = 2.0 * xv; + for( uint_t k = 2; k <= n_val; ++k ) + { + fp_t h2 = 2.0 * xv * h1 - 2.0 * (fp_t)(k - 1) * h0; + h0 = h1; + h1 = h2; + } + y[0][i] = h1; + } + } + return 0; +} + + + diff --git a/lib/high_low_bands.c b/lib/high_low_bands.c new file mode 100644 index 0000000..544796f --- /dev/null +++ b/lib/high_low_bands.c @@ -0,0 +1,71 @@ +// High Low Bands +// +// high_low_bands +// +// High-Low Bands +// +// Upper: highest high, Lower: lowest low over period +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_high_low_bands_params_t; + +ts_parameter_t const ts_high_low_bands_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_high_low_bands_params_t) }; + +uint_t ts_high_low_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? 2 * ((ts_high_low_bands_params_t *)c)->k - 2 : 0 ); +} + +int ts_high_low_bands( + 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 +) +{ + TS_PROLOGUE( ts_high_low_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_high_low_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = 2 * k - 2; i < n; ++i ) + { + y[0][i] = 0; + y[1][i] = 0; + for( uint_t j = i - 2 * k + 2; j <= i - k + 1; ++j ) + { + uint_t start = (j >= k - 1) ? j - k + 1 : 0; + + fp_t hh = x[1][start]; + for( uint_t m = start + 1; m <= j; ++m ) + { + if( x[1][m] > hh ) + hh = x[1][m]; + } + y[0][i] += hh; + + fp_t ll = x[2][start]; + for( uint_t m = start + 1; m <= j; ++m ) + { + if( x[2][m] < ll ) + ll = x[2][m]; + } + y[1][i] += ll; + } + y[0][i] /= k; + y[1][i] /= k; + } + return 0; +} + + + diff --git a/lib/high_minus_low.c b/lib/high_minus_low.c new file mode 100644 index 0000000..b38fcbb --- /dev/null +++ b/lib/high_minus_low.c @@ -0,0 +1,42 @@ +// High Minus Low +// +// high_minus_low +// +// High minus Low +// +// H - L +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_high_minus_low_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_high_minus_low_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_high_minus_low( + 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 +) +{ + TS_PROLOGUE( ts_high_minus_low_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[1][i] - x[2][i]; + } + return 0; +} + + + diff --git a/lib/highest_high.c b/lib/highest_high.c new file mode 100644 index 0000000..d3ac753 --- /dev/null +++ b/lib/highest_high.c @@ -0,0 +1,67 @@ +// Highest High +// +// highest_high +// +// Highest high over rolling window +// +// HH = max(H[t-k+1..t]) +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_highest_high_params_t; + +ts_parameter_t const ts_highest_high_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_highest_high_params_t) }; + +uint_t ts_highest_high_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_highest_high_params_t *)c)->k > 0 ? ((ts_highest_high_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_highest_high( + 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 +) +{ + TS_PROLOGUE( ts_highest_high_p ); + if ( !c ) return -1; + uint_t k = ((ts_highest_high_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t max_val = ts_h( x, 0 ); + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = ts_h( x, j ); + if( val > max_val ) + max_val = val; + } + y[0][k - 1] = max_val; + + for( uint_t i = k; i < n; ++i ) + { + fp_t new_val = ts_h( x, i ); + fp_t old_val = ts_h( x, i - k ); + if ( new_val > max_val ) max_val = new_val; + if ( old_val == max_val ) + { + max_val = ts_h( x, i - k + 1 ); + for( uint_t j = i - k + 2; j <= i; ++j ) + { + fp_t val = ts_h( x, j ); + if( val > max_val ) max_val = val; + } + } + y[0][i] = max_val; + } + return 0; +} + diff --git a/lib/historical_volatility.c b/lib/historical_volatility.c new file mode 100644 index 0000000..cde0d91 --- /dev/null +++ b/lib/historical_volatility.c @@ -0,0 +1,62 @@ +// Historical Volatility +// +// historical_volatility +// +// Historical volatility of log returns +// +// stddev(log(C[t]/C[t-1])) over period +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_historical_volatility_params_t; + +ts_parameter_t const ts_historical_volatility_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_historical_volatility_params_t) }; + +uint_t ts_historical_volatility_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_historical_volatility_params_t *)c)->k > 0 ? ((ts_historical_volatility_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_historical_volatility( + 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 +) +{ + TS_PROLOGUE( ts_historical_volatility_p ); + if ( !c ) return -1; + uint_t k = ((ts_historical_volatility_params_t *)c)->k; + if (k == 0 || n < k + 1) return -1; + + for ( uint_t d = k - 1; d < n - 1; ++d ) + { + fp_t sum = 0; + fp_t sum2 = 0; + for ( uint_t j = d - k + 1; j <= d; ++j ) + { + fp_t lr = 0; + if ( x[0][j] > 0 && x[0][j + 1] > 0 ) + lr = log( x[0][j + 1] / x[0][j] ); + sum += lr; + sum2 += lr * lr; + } + fp_t mean = sum / ( fp_t )k; + fp_t variance = sum2 / ( fp_t )k - mean * mean; + y[0][d] = variance > 0 ? sqrt( variance ) * sqrt( 252.0 ) : 0; + } + + y[0][n - 1] = 0; + return 0; +} + + + diff --git a/lib/hull_ma.c b/lib/hull_ma.c new file mode 100644 index 0000000..e412a6f --- /dev/null +++ b/lib/hull_ma.c @@ -0,0 +1,89 @@ +// Hull Ma +// +// hull_ma +// +// Hull Moving Average +// +// HMA = WMA(2*WMA(n/2) - WMA(n), √n) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_hull_ma_params_t; + +ts_parameter_t const ts_hull_ma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_hull_ma_params_t) }; + +uint_t ts_hull_ma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_hull_ma_params_t *)c)->k + (uint_t)sqrt((fp_t)((ts_hull_ma_params_t *)c)->k) - 2 : 0 ); +} + +int ts_hull_ma( + 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 +) +{ + TS_PROLOGUE( ts_hull_ma_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_hull_ma_params_t *)c)->k; + if (k < 2) return -1; + + uint_t half_k = k / 2; + uint_t sqrt_k = (uint_t)sqrt((fp_t)k); + if (sqrt_k < 1) sqrt_k = 1; + + fp_t *wma_half = calloc( n, sizeof( fp_t ) ); + fp_t *wma_full = calloc( n, sizeof( fp_t ) ); + if ( !wma_half || !wma_full ) { free( wma_half ); free( wma_full ); return -1; } + + /* WMA(n/2) */ + fp_t wt_half = (fp_t)half_k * ((fp_t)half_k + 1.0) / 2.0; + for ( uint_t i = half_k - 1; i < n; ++i ) + { + fp_t s = 0; + for ( uint_t j = 0; j < half_k; ++j ) + s += x[0][i - j] * (fp_t)(half_k - j); + wma_half[i] = s / wt_half; + } + + /* WMA(n) */ + fp_t wt_full = (fp_t)k * ((fp_t)k + 1.0) / 2.0; + for ( uint_t i = k - 1; i < n; ++i ) + { + fp_t s = 0; + for ( uint_t j = 0; j < k; ++j ) + s += x[0][i - j] * (fp_t)(k - j); + wma_full[i] = s / wt_full; + } + + /* HMA = WMA(sqrt(n), 2*WMA(n/2) - WMA(n)) */ + fp_t wt_sqrt = (fp_t)sqrt_k * ((fp_t)sqrt_k + 1.0) / 2.0; + uint_t offset = k + sqrt_k - 2; + for (uint_t j = 0; j < n && j < offset; ++j) y[0][j] = NAN; + for ( uint_t i = offset; i < n; ++i ) + { + fp_t s = 0; + for ( uint_t j = 0; j < sqrt_k; ++j ) + { + fp_t diff = 2.0 * wma_half[i - j] - wma_full[i - j]; + s += diff * (fp_t)(sqrt_k - j); + } + y[0][i] = s / wt_sqrt; + } + + free( wma_half ); + free( wma_full ); + return 0; +} + diff --git a/lib/hurst_exponent.c b/lib/hurst_exponent.c new file mode 100644 index 0000000..aa58036 --- /dev/null +++ b/lib/hurst_exponent.c @@ -0,0 +1,122 @@ +// Hurst Exponent +// +// hurst_exponent +// +// Hurst exponent via R/S analysis +// +// H = slope of linear regression of log(R/S) vs log(n) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_hurst_exponent_params_t; + +ts_parameter_t const ts_hurst_exponent_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_hurst_exponent_params_t) }; + +uint_t ts_hurst_exponent_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hurst_exponent( + 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 +) +{ + TS_PROLOGUE( ts_hurst_exponent_p ); + if ( !c ) return -1; + uint_t k = ((ts_hurst_exponent_params_t *)c)->k; + if (k == 0 || k > n) return -1; + + for ( uint_t i = 0; i < n; ++i ) + { + if (i + 1 < k) { + y[0][i] = 0; + continue; + } + + uint_t data_len = i + 1; + uint_t num_lags = 0; + fp_t sum_log_s = 0; + fp_t sum_log_rs = 0; + fp_t sum_log_s2 = 0; + fp_t sum_log_s_log_rs = 0; + + for ( uint_t s = 2; s <= k && s <= data_len / 2; ++s ) + { + uint_t ns = data_len / s; + if (ns == 0) continue; + + fp_t total_log_rs = 0; + uint_t valid_segs = 0; + + for ( uint_t seg = 0; seg < ns; ++seg ) + { + uint_t start = seg * s; + uint_t end = start + s; + if (end > data_len) break; + + fp_t mean = 0; + for ( uint_t p = start; p < end; ++p ) + mean += x[0][p]; + mean /= (fp_t)s; + + fp_t cumsum = 0; + fp_t max_dev = 0, min_dev = 0; + for ( uint_t p = start; p < end; ++p ) + { + cumsum += x[0][p] - mean; + if (cumsum > max_dev) max_dev = cumsum; + if (cumsum < min_dev) min_dev = cumsum; + } + + fp_t r = max_dev - min_dev; + fp_t std = 0; + for ( uint_t p = start; p < end; ++p ) + { + fp_t d = x[0][p] - mean; + std += d * d; + } + std = sqrt(std / (fp_t)s); + + if (std > 0 && r > 0) { + total_log_rs += log(r / std); + valid_segs++; + } + } + + if (valid_segs > 0) { + fp_t log_s = log((fp_t)s); + fp_t mean_log_rs = total_log_rs / (fp_t)valid_segs; + sum_log_s += log_s; + sum_log_rs += mean_log_rs; + sum_log_s2 += log_s * log_s; + sum_log_s_log_rs += log_s * mean_log_rs; + num_lags++; + } + } + + if (num_lags >= 2) { + fp_t denom = (fp_t)num_lags * sum_log_s2 - sum_log_s * sum_log_s; + if (fabs(denom) > 1e-300) { + fp_t h = ((fp_t)num_lags * sum_log_s_log_rs - sum_log_s * sum_log_rs) / denom; + y[0][i] = h; + } else { + y[0][i] = 0; + } + } else { + y[0][i] = 0; + } + } + return 0; +} + diff --git a/lib/hypot.c b/lib/hypot.c new file mode 100644 index 0000000..42fafa7 --- /dev/null +++ b/lib/hypot.c @@ -0,0 +1,42 @@ +// Hypot +// +// hypot +// +// Hypotenuse +// +// √(x² + y²) +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_hypot_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_hypot_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_hypot( + 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 +) +{ + TS_PROLOGUE( ts_hypot_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = hypot( x[0][i], x[1][i] ); + } + return 0; +} + + + diff --git a/lib/ichimoku.c b/lib/ichimoku.c new file mode 100644 index 0000000..76f85e6 --- /dev/null +++ b/lib/ichimoku.c @@ -0,0 +1,113 @@ +// Ichimoku +// +// ichimoku +// +// Ichimoku Cloud +// +// Tenkan, Kijun, Senkou Span A/B, Chikou +// +// Inputs: 4 +// Outputs: 5 +// Constants: 3 (tenkan, kijun, senkou) + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t tenkan; + uint_t kijun; + uint_t senkou; +} ts_ichimoku_params_t; + +ts_parameter_t const ts_ichimoku_p = { .n_inputs = 4, .n_outputs = 5, .n_constants = 3, .params_size = sizeof(ts_ichimoku_params_t) }; + +uint_t ts_ichimoku_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 52 ); +} + +int ts_ichimoku( + 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 +) +{ + TS_PROLOGUE( ts_ichimoku_p ); + uint_t tenkan = 9; + uint_t kijun = 26; + uint_t senkou = 52; + + if ( c ) + { + ts_ichimoku_params_t *p = (ts_ichimoku_params_t *)c; + if ( p->tenkan > 0 ) tenkan = p->tenkan; + if ( p->kijun > 0 ) kijun = p->kijun; + if ( p->senkou > 0 ) senkou = p->senkou; + } + + if ( tenkan == 0 || kijun == 0 || senkou == 0 ) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = 0; + y[1][i] = 0; + y[2][i] = 0; + y[3][i] = 0; + y[4][i] = 0; + } + + for( uint_t i = tenkan - 1; i < n; ++i ) + { + fp_t hh = ts_h( x, i - tenkan + 1 ); + fp_t ll = ts_l( x, i - tenkan + 1 ); + for( uint_t j = i - tenkan + 1; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + y[0][i] = ( hh + ll ) / 2.0; + } + + for( uint_t i = kijun - 1; i < n; ++i ) + { + fp_t hh = ts_h( x, i - kijun + 1 ); + fp_t ll = ts_l( x, i - kijun + 1 ); + for( uint_t j = i - kijun + 1; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + y[1][i] = ( hh + ll ) / 2.0; + } + + for( uint_t i = kijun - 1; i < n; ++i ) + { + uint_t idx = i + kijun; + if( idx < n ) + y[2][idx] = ( y[0][i] + y[1][i] ) / 2.0; + } + + for( uint_t i = senkou - 1; i < n; ++i ) + { + fp_t hh = ts_h( x, i - senkou + 1 ); + fp_t ll = ts_l( x, i - senkou + 1 ); + for( uint_t j = i - senkou + 1; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + uint_t idx = i + kijun; + if( idx < n ) + y[3][idx] = ( hh + ll ) / 2.0; + } + + for( uint_t i = kijun; i < n; ++i ) + { + y[4][i] = ts_c( x, i - kijun ); + } + return 0; +} + diff --git a/lib/ilogb.c b/lib/ilogb.c new file mode 100644 index 0000000..82cda70 --- /dev/null +++ b/lib/ilogb.c @@ -0,0 +1,42 @@ +// Ilogb +// +// ilogb +// +// Integer logarithm base +// +// exponent of the floating-point representation +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_ilogb_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_ilogb_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_ilogb( + 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 +) +{ + TS_PROLOGUE( ts_ilogb_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = (fp_t)ilogb( x[0][i] ); + } + return 0; +} + + + diff --git a/lib/intraday_momentum_index.c b/lib/intraday_momentum_index.c new file mode 100644 index 0000000..79209bd --- /dev/null +++ b/lib/intraday_momentum_index.c @@ -0,0 +1,57 @@ +// Intraday Momentum Index +// +// intraday_momentum_index +// +// Intraday Momentum Index +// +// SMA of up moves / total moves +// +// Inputs: 4 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_intraday_momentum_index_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_intraday_momentum_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 3 ); +} + +int ts_intraday_momentum_index( + 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 +) +{ + TS_PROLOGUE( ts_intraday_momentum_index_p ); + (void)c; + uint_t k = 3; + + for (uint_t i = 0; i < k; ++i) y[0][i] = 0; + + for( uint_t i = k; i < n; ++i ) + { + fp_t sum_up = 0; + fp_t sum_down = 0; + + for( uint_t j = 0; j < k; ++j ) + { + fp_t diff = ts_o( x, i - k + 1 + j ) - ts_c( x, i - k + j ); + if( diff > 0 ) + sum_up += diff; + else if( diff < 0 ) + sum_down += (-diff); + } + + y[0][i] = (sum_up + sum_down) == 0 ? 0 : sum_up / (sum_up + sum_down); + } + return 0; +} + + + diff --git a/lib/is_prime.c b/lib/is_prime.c new file mode 100644 index 0000000..119c9a4 --- /dev/null +++ b/lib/is_prime.c @@ -0,0 +1,69 @@ +// Is Prime +// +// is_prime +// +// Prime number test +// +// 1 if prime, 0 otherwise +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_is_prime_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_is_prime_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_is_prime( + 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 +) +{ + TS_PROLOGUE( ts_is_prime_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + if (v > 1e15 || v < -1e15) { y[0][i] = 0.0; continue; } + long n_val = (long)v; + if( n_val < 2 ) + { + y[0][i] = 0.0; + continue; + } + if( n_val == 2 ) + { + y[0][i] = 1.0; + continue; + } + if( n_val % 2 == 0 ) + { + y[0][i] = 0.0; + continue; + } + int prime = 1; + for( long d = 3; d <= n_val / d; d += 2 ) + { + if( n_val % d == 0 ) + { + prime = 0; + break; + } + } + y[0][i] = prime ? 1.0 : 0.0; + } + return 0; +} + + + diff --git a/lib/jaccard_similarity.c b/lib/jaccard_similarity.c new file mode 100644 index 0000000..b2ffee6 --- /dev/null +++ b/lib/jaccard_similarity.c @@ -0,0 +1,45 @@ +// Jaccard Similarity +// +// jaccard_similarity +// +// Jaccard similarity coefficient +// +// +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_jaccard_similarity_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_jaccard_similarity_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_jaccard_similarity( + 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 +) +{ + TS_PROLOGUE( ts_jaccard_similarity_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t const a = x[0][i]; + fp_t const b = x[1][i]; + fp_t const m = fmax( a, b ); + y[0][i] = m == 0.0 ? 0.0 : ( a + b ) / ( 2.0 * m ); + } + return 0; +} + + + diff --git a/lib/kalman_filter.c b/lib/kalman_filter.c new file mode 100644 index 0000000..f7adf6f --- /dev/null +++ b/lib/kalman_filter.c @@ -0,0 +1,63 @@ +// Kalman Filter +// +// kalman_filter +// +// Kalman Filter +// +// Optimal state estimation via predict-update cycle +// +// Inputs: 1 +// Outputs: 1 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t Q; + fp_t R; +} ts_kalman_filter_params_t; + +ts_parameter_t const ts_kalman_filter_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_kalman_filter_params_t) }; + +uint_t ts_kalman_filter_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_kalman_filter( + 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 +) +{ + TS_PROLOGUE( ts_kalman_filter_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + fp_t Q = ((ts_kalman_filter_params_t *)c)->Q; + fp_t R = ((ts_kalman_filter_params_t *)c)->R; + + if (Q < 0 || R < 0) return -1; + if (isnan(Q) || isnan(R)) return -1; + if (n == 0) return -1; + fp_t x_est = x[0][0]; + /* NOTE: P=1.0 is a reasonable default for normalized data. + * For price data in the range of 100-3000, consider initializing + * P to the variance of the first few samples. */ + fp_t P = 1.0; + + y[0][0] = x_est; + for ( uint_t i = 1; i < n; ++i ) + { + P = P + Q; + fp_t denom = P + R; + fp_t K = denom != 0 ? P / denom : 0; + x_est = x_est + K * (x[0][i] - x_est); + P = (1 - K) * P; + y[0][i] = x_est; + } + return 0; +} + diff --git a/lib/kaufman_adaptive_ma.c b/lib/kaufman_adaptive_ma.c new file mode 100644 index 0000000..f56c6a1 --- /dev/null +++ b/lib/kaufman_adaptive_ma.c @@ -0,0 +1,61 @@ +// Kaufman Adaptive Ma +// +// kaufman_adaptive_ma +// +// Kaufman Adaptive Moving Average +// +// AMA based on efficiency ratio +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_kaufman_adaptive_ma_params_t; + +ts_parameter_t const ts_kaufman_adaptive_ma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_kaufman_adaptive_ma_params_t) }; + +uint_t ts_kaufman_adaptive_ma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_kaufman_adaptive_ma_params_t *)c)->k : 0 ); +} + +int ts_kaufman_adaptive_ma( + 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 +) +{ + TS_PROLOGUE( ts_kaufman_adaptive_ma_p ); + if ( !c ) return -1; + uint_t k = ((ts_kaufman_adaptive_ma_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t ama = x[0][0]; + fp_t sm = 2.0 / ((fp_t)k + 1.0); + fp_t sn = 2.0 / (sqrt((fp_t)k) + 1.0); + + for (uint_t i = k; i < n; ++i) + { + fp_t price_change = fabs(x[0][i] - x[0][i - k]); + fp_t noise = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + noise += fabs(x[0][j] - x[0][j - 1]); + + fp_t er = noise != 0 ? price_change / noise : 0; + fp_t er_sq = er * er; + fp_t sc = er_sq * (sn - sm) + sm; + sc = sc * sc; + + ama = ama + sc * (x[0][i] - ama); + y[0][i] = ama; + } + return 0; +} + diff --git a/lib/keltner_channel.c b/lib/keltner_channel.c new file mode 100644 index 0000000..259be03 --- /dev/null +++ b/lib/keltner_channel.c @@ -0,0 +1,78 @@ +// Keltner Channel +// +// keltner_channel +// +// Keltner Channel +// +// Middle: EMA, Upper/Lower: EMA ± mult*ATR +// +// Inputs: 4 +// Outputs: 3 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_keltner_channel_params_t; + +ts_parameter_t const ts_keltner_channel_p = { .n_inputs = 4, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_keltner_channel_params_t) }; + +uint_t ts_keltner_channel_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_keltner_channel_params_t *)c)->k : 0 ); +} + +int ts_keltner_channel( + 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 +) +{ + TS_PROLOGUE( ts_keltner_channel_p ); + if ( !c ) return -1; + uint_t k = ((ts_keltner_channel_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + fp_t a = 2.0 / ((fp_t)k + 1.0); + + fp_t ema_tp = ( ts_h( x, 0 ) + ts_l( x, 0 ) + ts_c( x, 0 ) ) / 3.0; + + fp_t tr = ts_h( x, 1 ) - ts_l( x, 1 ); + fp_t tr2 = fabs( ts_h( x, 1 ) - ts_c( x, 0 ) ); + fp_t tr3 = fabs( ts_l( x, 1 ) - ts_c( x, 0 ) ); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + fp_t atr = tr; + + for (uint_t i = 0; i < k; ++i) { y[0][i] = 0; y[1][i] = 0; y[2][i] = 0; } + + for( uint_t i = 1; i < n; ++i ) + { + fp_t tp = ( ts_h( x, i ) + ts_l( x, i ) + ts_c( x, i ) ) / 3.0; + ema_tp = ema_tp + a * ( tp - ema_tp ); + + tr = ts_h( x, i ) - ts_l( x, i ); + tr2 = fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ); + tr3 = fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + + if( i >= 2 ) + atr = atr + (tr - atr) / k; + + if( i >= k ) + { + y[0][i] = ema_tp; + y[1][i] = ema_tp + 2.0 * atr; + y[2][i] = ema_tp - 2.0 * atr; + } + } + return 0; +} + + + diff --git a/lib/kvo.c b/lib/kvo.c new file mode 100644 index 0000000..229fad8 --- /dev/null +++ b/lib/kvo.c @@ -0,0 +1,105 @@ +// Kvo +// +// kvo +// +// Klinger Volume Oscillator +// +// Volume-weighted price direction oscillator +// +// Inputs: 5 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k1; + fp_t f0; +} ts_kvo_params_t; + +ts_parameter_t const ts_kvo_p = { .n_inputs = 5, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_kvo_params_t) }; + +uint_t ts_kvo_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_kvo_params_t *)c)->k1 > 10 ? ((ts_kvo_params_t *)c)->k1 : 10 : 0 ); +} + +int ts_kvo( + 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 +) +{ + TS_PROLOGUE( ts_kvo_p ); + if ( !c ) return -1; + uint_t k1 = ((ts_kvo_params_t *)c)->k1; + fp_t f0_raw = ((ts_kvo_params_t *)c)->f0; + if (isnan(f0_raw) || isinf(f0_raw) || f0_raw < 0) return -1; + uint_t k10 = (uint_t)f0_raw; + if (k1 == 0) return -1; + k10 = k1 > 10 ? k1 : 10; + + fp_t ema_short_dvm = 0; + fp_t ema_long_absdvm = 0; + fp_t ema_signal = 0; + fp_t kvo_val = 0; + + fp_t short_a = 2.0 / (k1 + 1); + fp_t long_a = 2.0 / (k10 + 1); + fp_t sig_a = 2.0 / (k1 + 1); + + uint_t offset = k10; + + for (uint_t i = 0; i < offset; ++i) { y[0][i] = 0; y[1][i] = 0; } + + for( uint_t i = offset; i < n; ++i ) + { + fp_t dvm_i = 0; + fp_t absdvm_i = 0; + + if( i >= 1 ) + { + fp_t dm = (ts_c(x, i) - ts_o(x, i)) - (ts_c(x, i-1) - ts_o(x, i-1)); + fp_t vol = ts_v(x, i); + if( dm > 0 ) + { + dvm_i = dm * vol; + absdvm_i = dm * vol; + } + else if( dm < 0 ) + { + dvm_i = dm * vol; + absdvm_i = (-dm) * vol; + } + } + + if( i == offset ) + { + ema_short_dvm = dvm_i; + ema_long_absdvm = absdvm_i; + } + else + { + ema_short_dvm = ema_short_dvm + short_a * (dvm_i - ema_short_dvm); + ema_long_absdvm = ema_long_absdvm + long_a * (absdvm_i - ema_long_absdvm); + } + + if( ema_long_absdvm != 0 ) + kvo_val = ema_short_dvm / ema_long_absdvm; + + if( i == offset ) + ema_signal = kvo_val; + else + ema_signal = ema_signal + sig_a * (kvo_val - ema_signal); + + y[0][i] = kvo_val; + y[1][i] = ema_signal; + } + return 0; +} + + + diff --git a/lib/lag.c b/lib/lag.c new file mode 100644 index 0000000..1e3f657 --- /dev/null +++ b/lib/lag.c @@ -0,0 +1,54 @@ +// Lag +// +// lag - lag +// +// DESCRIPTION +// +// Inputs: 1 +// Outputs: 1 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t insert; +} ts_lag_params_t; + +ts_parameter_t const ts_lag_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_lag_params_t) }; + + +uint_t ts_lag_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_lag_params_t *)c)->k : 0 ); +} + +int ts_lag( + 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 +) +{ + TS_PROLOGUE( ts_lag_p ); + if ( !c ) return -1; + uint_t k = ((ts_lag_params_t *)c)->k; + if (k == 0) return -1; + + fp_t insert = ((ts_lag_params_t *)c)->insert; + + if (n <= k) return -1; + + for (uint_t i = 0; i < k; ++i) + y[0][i] = insert; + + for (uint_t i = k; i < n; ++i) + y[0][i] = x[0][i - k]; + return 0; +} + + + diff --git a/lib/laguerre.c b/lib/laguerre.c new file mode 100644 index 0000000..b1e1284 --- /dev/null +++ b/lib/laguerre.c @@ -0,0 +1,62 @@ +// Laguerre +// +// laguerre +// +// Laguerre polynomial +// +// L_n(x) via recurrence relation +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_laguerre_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_laguerre_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_laguerre( + 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 +) +{ + TS_PROLOGUE( ts_laguerre_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if (x[1][i] < 0) { y[0][i] = 0; continue; } + uint_t n_val = (uint_t)x[1][i]; + if( n_val == 0 ) + { + y[0][i] = 1.0; + } + else if( n_val == 1 ) + { + y[0][i] = 1.0 - xv; + } + else + { + fp_t l0 = 1.0; + fp_t l1 = 1.0 - xv; + for( uint_t k = 2; k <= n_val; ++k ) + { + fp_t l2 = ((2.0 * (fp_t)k - 1.0 - xv) * l1 - (fp_t)(k - 1) * l0) / (fp_t)k; + l0 = l1; + l1 = l2; + } + y[0][i] = l1; + } + } + return 0; +} + diff --git a/lib/lambert_w.c b/lib/lambert_w.c new file mode 100644 index 0000000..da59199 --- /dev/null +++ b/lib/lambert_w.c @@ -0,0 +1,64 @@ +// Lambert W +// +// lambert_w +// +// Lambert W function +// +// W(x) such that W*e^W = x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_lambert_w_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_lambert_w_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_lambert_w( + 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 +) +{ + TS_PROLOGUE( ts_lambert_w_p ); + (void)c; + for (uint_t i = 0; i < n; ++i) + { + fp_t xv = x[0][i]; + if (xv < -1.0/exp(1.0)) + { + y[0][i] = NAN; + continue; + } + if (xv == 0.0) + { + y[0][i] = 0.0; + continue; + } + /* Halley iteration for W(x) */ + fp_t w = xv < 1.0 ? log(xv + 1.0) : log(xv); + for (uint_t j = 0; j < 10; ++j) + { + fp_t ew = exp(w); + fp_t f = w * ew - xv; + fp_t f1 = ew * (w + 1.0); + if (f1 == 0) break; + fp_t f2 = f1 * (w + 2.0); + w = w - f / f1 * (1.0 + f * f2 / (2.0 * f1 * f1)); + } + y[0][i] = w; + } + return 0; +} + + + diff --git a/lib/laplace_cdf.c b/lib/laplace_cdf.c new file mode 100644 index 0000000..7a70882 --- /dev/null +++ b/lib/laplace_cdf.c @@ -0,0 +1,43 @@ +// Laplace Cdf +// +// laplace_cdf +// +// Laplace cumulative distribution +// +// F(x) = 0.5 + 0.5*sign(x)*(1-e^(-|x|)) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_laplace_cdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_laplace_cdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_laplace_cdf( + 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 +) +{ + TS_PROLOGUE( ts_laplace_cdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi < 0 ? 0.5 * exp( xi ) : 1.0 - 0.5 * exp( -xi ); + } + return 0; +} + + + diff --git a/lib/laplace_pdf.c b/lib/laplace_pdf.c new file mode 100644 index 0000000..d741aa4 --- /dev/null +++ b/lib/laplace_pdf.c @@ -0,0 +1,42 @@ +// Laplace Pdf +// +// laplace_pdf +// +// Laplace probability density +// +// f(x) = 0.5 * e^(-|x|) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_laplace_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_laplace_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_laplace_pdf( + 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 +) +{ + TS_PROLOGUE( ts_laplace_pdf_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = exp( -fabs( x[0][i] ) ) / 2.0; + } + return 0; +} + + + diff --git a/lib/lcm.c b/lib/lcm.c new file mode 100644 index 0000000..c8b52ef --- /dev/null +++ b/lib/lcm.c @@ -0,0 +1,62 @@ +// Lcm +// +// lcm +// +// Least common multiple +// +// lcm(a,b) = |a*b| / gcd(a,b) +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_lcm_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_lcm_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_lcm( + 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 +) +{ + TS_PROLOGUE( ts_lcm_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t ax = fabs(x[0][i]); + fp_t bx = fabs(x[1][i]); + if (ax > 1e15 || bx > 1e15) { y[0][i] = 0.0; continue; } + long a = (long)ax; + long b = (long)bx; + if( !a || !b ) + { + y[0][i] = 0.0; + } + else + { + long ta = a; + long tb = b; + while( tb ) + { + long t = tb; + tb = ta % tb; + ta = t; + } + y[0][i] = (fp_t)(a / ta) * (fp_t)b; + } + } + return 0; +} + + + diff --git a/lib/leaky_relu.c b/lib/leaky_relu.c new file mode 100644 index 0000000..51651bc --- /dev/null +++ b/lib/leaky_relu.c @@ -0,0 +1,42 @@ +// Leaky ReLU +// +// leaky_relu - Leaky Rectified Linear Unit +// +// y[t] = x[t] > 0 ? x[t] : 0.01 * x[t] +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_leaky_relu_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_leaky_relu_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_leaky_relu( + 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 +) +{ + TS_PROLOGUE( ts_leaky_relu_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi > 0 ? xi : 0.01 * xi; + } + return 0; +} + + + diff --git a/lib/legendre.c b/lib/legendre.c new file mode 100644 index 0000000..6ba4644 --- /dev/null +++ b/lib/legendre.c @@ -0,0 +1,64 @@ +// Legendre +// +// legendre +// +// Legendre polynomial +// +// P_n(x) via recurrence relation +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_legendre_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_legendre_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_legendre( + 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 +) +{ + TS_PROLOGUE( ts_legendre_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if (x[1][i] < 0) { y[0][i] = 0; continue; } + uint_t n_val = (uint_t)x[1][i]; + if( n_val == 0 ) + { + y[0][i] = 1.0; + } + else if( n_val == 1 ) + { + y[0][i] = xv; + } + else + { + fp_t p0 = 1.0; + fp_t p1 = xv; + for( uint_t k = 2; k <= n_val; ++k ) + { + fp_t p2 = ((2.0 * (fp_t)k - 1.0) * xv * p1 - (fp_t)(k - 1) * p0) / (fp_t)k; + p0 = p1; + p1 = p2; + } + y[0][i] = p1; + } + } + return 0; +} + + + diff --git a/lib/lerp.c b/lib/lerp.c new file mode 100644 index 0000000..6b284d6 --- /dev/null +++ b/lib/lerp.c @@ -0,0 +1,42 @@ +// Lerp +// +// lerp +// +// Linear interpolation +// +// a + t*(b-a) +// +// Inputs: 3 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_lerp_p = { .n_inputs = 3, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_lerp_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_lerp( + 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 +) +{ + TS_PROLOGUE( ts_lerp_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] + x[1][i] * ( x[2][i] - x[0][i] ); + } + return 0; +} + + + diff --git a/lib/lgamma.c b/lib/lgamma.c new file mode 100644 index 0000000..02ac593 --- /dev/null +++ b/lib/lgamma.c @@ -0,0 +1,52 @@ +// Lgamma +// +// lgamma +// +// Natural log of gamma function +// +// ln(|Γ(x)|) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_lgamma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_lgamma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_lgamma( + 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 +) +{ + TS_PROLOGUE( ts_lgamma_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( xv <= 0.0 ) + { + fp_t intpart; + if (modf(xv, &intpart) != 0.0) + y[0][i] = lgamma(xv); + else + y[0][i] = NAN; + } + else + y[0][i] = lgamma( xv ); + } + return 0; +} + + + diff --git a/lib/linear_regression.c b/lib/linear_regression.c new file mode 100644 index 0000000..5d595bc --- /dev/null +++ b/lib/linear_regression.c @@ -0,0 +1,86 @@ +// Linear Regression +// +// linear_regression +// +// Linear regression line +// +// y = a + b*x via least squares +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_params_t; + +ts_parameter_t const ts_linear_regression_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_params_t) }; + +uint_t ts_linear_regression_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_linear_regression_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + // Precompute constant x sums (x values are always 0..k-1) + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + // Initial window + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + y[0][k - 1] = intercept + slope * (fp_t)(k - 1); + } + + // Sliding window: sum_xy_new = sum_xy_old - sum_y_new + k * y_new + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + y[0][i] = intercept + slope * (fp_t)(k - 1); + } + } + return 0; +} + diff --git a/lib/linear_regression_angle.c b/lib/linear_regression_angle.c new file mode 100644 index 0000000..22928bc --- /dev/null +++ b/lib/linear_regression_angle.c @@ -0,0 +1,80 @@ +// Linear Regression Angle +// +// linear_regression_angle +// +// Linear regression angle +// +// atan(slope) in degrees +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_angle_params_t; + +ts_parameter_t const ts_linear_regression_angle_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_angle_params_t) }; + +uint_t ts_linear_regression_angle_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_angle_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression_angle( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_angle_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_regression_angle_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = 0; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + y[0][k - 1] = atan(slope) * 180.0 / PI; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = 0; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + y[0][i] = atan(slope) * 180.0 / PI; + } + } + return 0; +} + diff --git a/lib/linear_regression_forecast.c b/lib/linear_regression_forecast.c new file mode 100644 index 0000000..02a78a8 --- /dev/null +++ b/lib/linear_regression_forecast.c @@ -0,0 +1,82 @@ +// Linear Regression Forecast +// +// linear_regression_forecast +// +// Linear regression forecast +// +// Forecast = intercept + slope * period +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_forecast_params_t; + +ts_parameter_t const ts_linear_regression_forecast_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_forecast_params_t) }; + +uint_t ts_linear_regression_forecast_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_forecast_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression_forecast( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_forecast_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_regression_forecast_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + y[0][k - 1] = intercept + slope * fk; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + y[0][i] = intercept + slope * fk; + } + } + return 0; +} + diff --git a/lib/linear_regression_intercept.c b/lib/linear_regression_intercept.c new file mode 100644 index 0000000..2b26478 --- /dev/null +++ b/lib/linear_regression_intercept.c @@ -0,0 +1,79 @@ +// Linear Regression Intercept +// +// linear_regression_intercept +// +// Linear regression intercept +// +// a = ȳ - b*x̄ +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_intercept_params_t; + +ts_parameter_t const ts_linear_regression_intercept_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_intercept_params_t) }; + +uint_t ts_linear_regression_intercept_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_intercept_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression_intercept( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_intercept_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_regression_intercept_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + y[0][k - 1] = (sum_y - slope * sum_x) / fk; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + y[0][i] = (sum_y - slope * sum_x) / fk; + } + } + return 0; +} + diff --git a/lib/linear_regression_r2.c b/lib/linear_regression_r2.c new file mode 100644 index 0000000..5cec662 --- /dev/null +++ b/lib/linear_regression_r2.c @@ -0,0 +1,88 @@ +// Linear Regression R2 +// +// linear_regression_r2 +// +// Linear regression R-squared +// +// R² = 1 - SS_res/SS_tot +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_r2_params_t; + +ts_parameter_t const ts_linear_regression_r2_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_r2_params_t) }; + +uint_t ts_linear_regression_r2_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_r2_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression_r2( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_r2_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_regression_r2_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t fk = (fp_t)k; + fp_t denom_x = fk * sum_x2 - sum_x * sum_x; + + fp_t sum_y = 0, sum_xy = 0, sum_y2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + sum_y2 += yv * yv; + } + fp_t denom_y = fk * sum_y2 - sum_y * sum_y; + fp_t num = fk * sum_xy - sum_x * sum_y; + if (denom_x == 0 || denom_y == 0) { + y[0][k - 1] = 0; + } else { + fp_t prod = denom_x * denom_y; + fp_t r = prod > 0 ? num / sqrt(prod) : 0; + y[0][k - 1] = r * r; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + sum_y2 = sum_y2 - y_old * y_old + y_new * y_new; + denom_y = fk * sum_y2 - sum_y * sum_y; + num = fk * sum_xy - sum_x * sum_y; + if (denom_x == 0 || denom_y == 0) { + y[0][i] = 0; + } else { + fp_t prod = denom_x * denom_y; + fp_t r = prod > 0 ? num / sqrt(prod) : 0; + y[0][i] = r * r; + } + } + return 0; +} + diff --git a/lib/linear_regression_slope.c b/lib/linear_regression_slope.c new file mode 100644 index 0000000..d20ca8e --- /dev/null +++ b/lib/linear_regression_slope.c @@ -0,0 +1,77 @@ +// Linear Regression Slope +// +// linear_regression_slope +// +// Linear regression slope +// +// b = Σ(x-x̄)(y-ȳ) / Σ(x-x̄)² +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_regression_slope_params_t; + +ts_parameter_t const ts_linear_regression_slope_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_regression_slope_params_t) }; + +uint_t ts_linear_regression_slope_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_regression_slope_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_regression_slope( + 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 +) +{ + TS_PROLOGUE( ts_linear_regression_slope_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_regression_slope_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = 0; + } else { + y[0][k - 1] = (fk * sum_xy - sum_x * sum_y) / denom; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = 0; + } else { + y[0][i] = (fk * sum_xy - sum_x * sum_y) / denom; + } + } + return 0; +} + diff --git a/lib/linear_weighted_ma.c b/lib/linear_weighted_ma.c new file mode 100644 index 0000000..70f660e --- /dev/null +++ b/lib/linear_weighted_ma.c @@ -0,0 +1,56 @@ +// Linear Weighted Ma +// +// linear_weighted_ma +// +// Linear Weighted Moving Average +// +// LWMA = Σ(i*x_i) / Σ(i) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_linear_weighted_ma_params_t; + +ts_parameter_t const ts_linear_weighted_ma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_linear_weighted_ma_params_t) }; + +uint_t ts_linear_weighted_ma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_linear_weighted_ma_params_t *)c)->k - 1 : 0 ); +} + +int ts_linear_weighted_ma( + 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 +) +{ + TS_PROLOGUE( ts_linear_weighted_ma_p ); + if ( !c ) return -1; + uint_t k = ((ts_linear_weighted_ma_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t w_sum = 0; + for( uint_t j = 1; j <= k; ++j ) + { + w_sum += (fp_t)j; + } + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + sum += (fp_t)(j + 1) * x[0][i - k + 1 + j]; + } + y[0][i] = sum / w_sum; + } + return 0; +} + diff --git a/lib/log1p.c b/lib/log1p.c new file mode 100644 index 0000000..dc49223 --- /dev/null +++ b/lib/log1p.c @@ -0,0 +1,41 @@ +// Log1p +// +// log1p +// +// Log(1+x) +// +// ln(1+x), accurate for small x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +ts_parameter_t const ts_log1p_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_log1p_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_log1p( + 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 +) +{ + TS_PROLOGUE( ts_log1p_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( xv < -1.0 ) { y[0][i] = 0; continue; } + y[0][i] = log1p( xv ); + } + return 0; +} + diff --git a/lib/log1pm1.c b/lib/log1pm1.c new file mode 100644 index 0000000..ea9281a --- /dev/null +++ b/lib/log1pm1.c @@ -0,0 +1,41 @@ +// Log1pm1 +// +// log1pm1 +// +// Log(1+x) - 1 +// +// ln(1+x) - 1 +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +ts_parameter_t const ts_log1pm1_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_log1pm1_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_log1pm1( + 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 +) +{ + TS_PROLOGUE( ts_log1pm1_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( xv < -1.0 ) { y[0][i] = 0; continue; } + y[0][i] = log1p( xv ) - 1.0; + } + return 0; +} + diff --git a/lib/log_return.c b/lib/log_return.c new file mode 100644 index 0000000..2aa8334 --- /dev/null +++ b/lib/log_return.c @@ -0,0 +1,50 @@ +// Log Return +// +// log_return +// +// Logarithmic return +// +// ln(C[t] / C[t-1]) +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_log_return_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_log_return_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 3 ); +} + +int ts_log_return( + 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 +) +{ + TS_PROLOGUE( ts_log_return_p ); + (void)c; + if (n < 3) return -1; + for (uint_t i = 0; i < 3; ++i) y[0][i] = 0; + + for( uint_t i = 3; i < n; ++i ) + { + if ( x[0][i - 3] <= 0 || x[0][i] <= 0 ) + { + y[0][i] = NAN; + } + else + { + y[0][i] = log( x[0][i] / x[0][i - 3] ); + } + } + return 0; +} + diff --git a/lib/logarithm.c b/lib/logarithm.c new file mode 100755 index 0000000..7451976 --- /dev/null +++ b/lib/logarithm.c @@ -0,0 +1,50 @@ +// Logarithm +// +// logarithm - log +// +// f( x ) = log_b( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 1 +// +// c[0] = b + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t b; +} ts_logarithm_params_t; + +ts_parameter_t const ts_logarithm_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_logarithm_params_t) }; + + +uint_t ts_logarithm_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_logarithm( + 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 +) +{ + TS_PROLOGUE( ts_logarithm_p ); + if ( !c ) return -1; + fp_t b = ((ts_logarithm_params_t *)c)->b; + if (b == 0 || b == 1.0) return -1; + + b = _ts_reciprocal( log( b ) ); + + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] > 0 ? log( x[0][i] ) * b : 0; + } + return 0; +} + diff --git a/lib/logarithm_binary.c b/lib/logarithm_binary.c new file mode 100644 index 0000000..1c6900d --- /dev/null +++ b/lib/logarithm_binary.c @@ -0,0 +1,44 @@ +// Binary Logarithm +// +// logarithm_binary - logb, lb +// +// f( x ) = log_2( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_logarithm_binary_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_logarithm_binary_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_logarithm_binary( + 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 +) +{ + TS_PROLOGUE( ts_logarithm_binary_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] <= 0) { + y[0][i] = 0; + } else { + y[0][i] = log2( x[0][i] ); + } + } + return 0; +} + diff --git a/lib/logarithm_decimal.c b/lib/logarithm_decimal.c new file mode 100644 index 0000000..39afb47 --- /dev/null +++ b/lib/logarithm_decimal.c @@ -0,0 +1,44 @@ +// Decimal Logarithm +// +// logarithm_decimal - logd, ld +// +// f( x ) = log_10( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_logarithm_decimal_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_logarithm_decimal_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_logarithm_decimal( + 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 +) +{ + TS_PROLOGUE( ts_logarithm_decimal_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] <= 0) { + y[0][i] = 0; + } else { + y[0][i] = log10( x[0][i] ); + } + } + return 0; +} + diff --git a/lib/logarithm_natural.c b/lib/logarithm_natural.c new file mode 100644 index 0000000..b76d67d --- /dev/null +++ b/lib/logarithm_natural.c @@ -0,0 +1,45 @@ +// Natural Logarithm +// +// logarithm_natural - logn, ln +// +// f( x ) = log_e( x ) +// = ln( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_logarithm_natural_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_logarithm_natural_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_logarithm_natural( + 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 +) +{ + TS_PROLOGUE( ts_logarithm_natural_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if (x[0][i] <= 0) { + y[0][i] = 0; + } else { + y[0][i] = log( x[0][i] ); + } + } + return 0; +} + diff --git a/lib/logb.c b/lib/logb.c new file mode 100644 index 0000000..7f1b66e --- /dev/null +++ b/lib/logb.c @@ -0,0 +1,42 @@ +// Logb +// +// logb +// +// Logarithm base of floating point +// +// exponent of the floating-point representation +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_logb_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_logb_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_logb( + 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 +) +{ + TS_PROLOGUE( ts_logb_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( xv == 0.0 ) { y[0][i] = 0; continue; } + y[0][i] = (fp_t)logb( xv ); + } + return 0; +} + diff --git a/lib/lowest_low.c b/lib/lowest_low.c new file mode 100644 index 0000000..d8ac284 --- /dev/null +++ b/lib/lowest_low.c @@ -0,0 +1,66 @@ +// Lowest Low +// +// lowest_low +// +// Lowest low over rolling window +// +// LL = min(L[t-k+1..t]) +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_lowest_low_params_t; + +ts_parameter_t const ts_lowest_low_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_lowest_low_params_t) }; + +uint_t ts_lowest_low_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_lowest_low_params_t *)c)->k - 1 : 0 ); +} + +int ts_lowest_low( + 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 +) +{ + TS_PROLOGUE( ts_lowest_low_p ); + if ( !c ) return -1; + uint_t k = ((ts_lowest_low_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t min_val = ts_l( x, 0 ); + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = ts_l( x, j ); + if( val < min_val ) + min_val = val; + } + y[0][k - 1] = min_val; + + for( uint_t i = k; i < n; ++i ) + { + fp_t new_val = ts_l( x, i ); + fp_t old_val = ts_l( x, i - k ); + if ( new_val < min_val ) min_val = new_val; + if ( old_val == min_val ) + { + min_val = ts_l( x, i - k + 1 ); + for( uint_t j = i - k + 2; j <= i; ++j ) + { + fp_t val = ts_l( x, j ); + if( val < min_val ) min_val = val; + } + } + y[0][i] = min_val; + } + return 0; +} + diff --git a/lib/macd.c b/lib/macd.c new file mode 100644 index 0000000..362f251 --- /dev/null +++ b/lib/macd.c @@ -0,0 +1,87 @@ +// Macd +// +// macd +// +// Moving Average Convergence Divergence +// +// MACD = EMA(fast) - EMA(slow), Signal = EMA(MACD) +// +// Inputs: 1 +// Outputs: 3 +// Constants: 3 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t fast_k; + fp_t slow_k; + fp_t signal_k; +} ts_macd_params_t; + +ts_parameter_t const ts_macd_p = { .n_inputs = 1, .n_outputs = 3, .n_constants = 3, .params_size = sizeof(ts_macd_params_t) }; + +uint_t ts_macd_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)(((ts_macd_params_t *)c)->slow_k + ((ts_macd_params_t *)c)->signal_k - 2) : 0 ); +} + +int ts_macd( + 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 +) +{ + TS_PROLOGUE( ts_macd_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + fp_t fast_k = ((ts_macd_params_t *)c)->fast_k; + fp_t slow_k = ((ts_macd_params_t *)c)->slow_k; + fp_t signal_k = ((ts_macd_params_t *)c)->signal_k; + + if (fast_k < 1 || slow_k < 1 || signal_k < 1) return -1; + if (isnan(fast_k) || isnan(slow_k) || isnan(signal_k)) return -1; + if (n == 0) return -1; + + fp_t fast_a = 2.0 / (fast_k + 1.0); + fp_t slow_a = 2.0 / (slow_k + 1.0); + fp_t signal_a = 2.0 / (signal_k + 1.0); + + fp_t fast_ema = x[0][0]; + fp_t slow_ema = x[0][0]; + + uint_t total_offset = (uint_t)(slow_k + signal_k - 2); + if (n <= total_offset) return -1; + + // Compute EMAs up to total_offset + for( uint_t i = 1; i <= total_offset; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + } + + fp_t macd_line = fast_ema - slow_ema; + fp_t signal_ema = macd_line; + + y[0][total_offset] = macd_line; + y[1][total_offset] = signal_ema; + y[2][total_offset] = 0; + + for( uint_t i = total_offset + 1; i < n; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + + macd_line = fast_ema - slow_ema; + signal_ema = signal_ema + signal_a * ( macd_line - signal_ema ); + + y[0][i] = macd_line; + y[1][i] = signal_ema; + y[2][i] = macd_line - signal_ema; + } + + return 0; +} + diff --git a/lib/manhattan_distance.c b/lib/manhattan_distance.c new file mode 100644 index 0000000..caa5ee4 --- /dev/null +++ b/lib/manhattan_distance.c @@ -0,0 +1,40 @@ +// Manhattan Distance +// +// manhattan_distance +// +// Manhattan distance (L1 norm) +// +// +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_manhattan_distance_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_manhattan_distance_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_manhattan_distance( + 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 +) +{ + TS_PROLOGUE( ts_manhattan_distance_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fabs( x[0][i] ) + fabs( x[1][i] ); + } + return 0; +} + diff --git a/lib/manhattan_norm.c b/lib/manhattan_norm.c new file mode 100644 index 0000000..10ca769 --- /dev/null +++ b/lib/manhattan_norm.c @@ -0,0 +1,40 @@ +// Manhattan Norm +// +// manhattan_norm +// +// Manhattan norm (L1) +// +// +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +ts_parameter_t const ts_manhattan_norm_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_manhattan_norm_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_manhattan_norm( + 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 +) +{ + TS_PROLOGUE( ts_manhattan_norm_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = fabs( x[0][i] ) + fabs( x[1][i] ); + } + return 0; +} + diff --git a/lib/market_facilitation_index.c b/lib/market_facilitation_index.c new file mode 100644 index 0000000..09c3feb --- /dev/null +++ b/lib/market_facilitation_index.c @@ -0,0 +1,39 @@ +// Market Facilitation Index +// +// market_facilitation_index +// +// Market Facilitation Index +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_market_facilitation_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_market_facilitation_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_market_facilitation_index( + 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 +) +{ + TS_PROLOGUE( ts_market_facilitation_index_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = (x[4][i] != 0) ? (x[1][i] - x[2][i]) / x[4][i] : 0; + } + return 0; +} + diff --git a/lib/mass_index.c b/lib/mass_index.c new file mode 100644 index 0000000..1985fab --- /dev/null +++ b/lib/mass_index.c @@ -0,0 +1,91 @@ +// Mass Index +// +// mass_index +// +// Mass Index +// +// SMA of EMA(H/L) ratio, reversal when > threshold +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include <stdlib.h> +#include "ts.h" + + +ts_parameter_t const ts_mass_index_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_mass_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 26 ); +} + +int ts_mass_index( + 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 +) +{ + TS_PROLOGUE( ts_mass_index_p ); + (void)c; + uint_t ema_period = 9; + uint_t sum_period = 26; + + if (n < sum_period) return -1; + + fp_t *hl = calloc( n, sizeof( fp_t ) ); + fp_t *ema1 = calloc( n, sizeof( fp_t ) ); + fp_t *ema2 = calloc( n, sizeof( fp_t ) ); + fp_t *ratio = calloc( n, sizeof( fp_t ) ); + if ( !hl || !ema1 || !ema2 || !ratio ) + { + free( hl ); free( ema1 ); free( ema2 ); free( ratio ); + return -1; + } + + fp_t a1 = 2.0 / ( ( fp_t )ema_period + 1.0 ); + + for ( uint_t i = 0; i < n; ++i ) + { + hl[i] = ts_h( x, i ) - ts_l( x, i ); + } + + ema1[0] = hl[0]; + for ( uint_t i = 1; i < n; ++i ) + { + ema1[i] = ema1[i - 1] + a1 * ( hl[i] - ema1[i - 1] ); + } + + ema2[0] = ema1[0]; + for ( uint_t i = 1; i < n; ++i ) + { + ema2[i] = ema2[i - 1] + a1 * ( ema1[i] - ema2[i - 1] ); + } + + for ( uint_t i = 0; i < n; ++i ) + { + ratio[i] = ema2[i] != 0 ? ema1[i] / ema2[i] : 0; + } + + fp_t sum = 0; + for ( uint_t i = 0; i < sum_period; ++i ) + { + sum += ratio[i]; + } + + for ( uint_t j = 0; j < sum_period - 1; ++j ) y[0][j] = 0; + y[0][sum_period - 1] = sum; + + for ( uint_t i = sum_period; i < n; ++i ) + { + sum = sum - ratio[i - sum_period] + ratio[i]; + y[0][i] = sum; + } + + free( hl ); free( ema1 ); free( ema2 ); free( ratio ); + return 0; +} + diff --git a/lib/maximum.c b/lib/maximum.c new file mode 100755 index 0000000..f4d124a --- /dev/null +++ b/lib/maximum.c @@ -0,0 +1,56 @@ +// Maximum +// +// maximum - max +// +// Finds the maximum value in the past k values +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_maximum_params_t; + +ts_parameter_t const ts_maximum_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_maximum_params_t) }; + + +uint_t ts_maximum_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_maximum_params_t *)c)->k - 1 : 0 ); +} + +int ts_maximum( + 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 +) +{ + TS_PROLOGUE( ts_maximum_p ); + if ( !c ) return -1; + uint_t k = ((ts_maximum_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k - 1; + uint_t xj = 0; + uint_t yi = k - 1; + + for( ; xi < n; xi++, xj++, yi++ ) + { + y[0][yi] = x[0][xj]; + + for( uint_t i = xj; i <= xi; i++ ) + { + if( x[0][i] > y[0][yi] ) + { + y[0][yi] = x[0][i]; + } + } + } + return 0; +} + diff --git a/lib/median_price.c b/lib/median_price.c new file mode 100644 index 0000000..7df202f --- /dev/null +++ b/lib/median_price.c @@ -0,0 +1,39 @@ +// Median Price +// +// median_price +// +// Median Price +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_median_price_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_median_price_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_median_price( + 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 +) +{ + TS_PROLOGUE( ts_median_price_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ( x[1][i] + x[2][i] ) / 2; + } + return 0; +} + diff --git a/lib/mesa_sine_wave.c b/lib/mesa_sine_wave.c new file mode 100644 index 0000000..8ed5fa6 --- /dev/null +++ b/lib/mesa_sine_wave.c @@ -0,0 +1,112 @@ +// Mesa Sine Wave +// +// mesa_sine_wave +// +// Mesa Sine Wave +// +// Adaptive sine wave following price trends +// +// Inputs: 1 +// Outputs: 2 +// Constants: 1 + + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_mesa_sine_wave_params_t; + +ts_parameter_t const ts_mesa_sine_wave_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_mesa_sine_wave_params_t) }; + +uint_t ts_mesa_sine_wave_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_mesa_sine_wave_params_t *)c)->k : 0 ); +} + +int ts_mesa_sine_wave( + 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 +) +{ + TS_PROLOGUE( ts_mesa_sine_wave_p ); + if ( !c ) return -1; + uint_t k = ((ts_mesa_sine_wave_params_t *)c)->k; + if (k < 2 || n < k) return -1; + + fp_t *sma = calloc(n, sizeof(fp_t)); + if (!sma) return -1; + + for (uint_t i = 0; i < n; ++i) + { + if (i < k - 1) + { + fp_t sum = 0; + for (uint_t j = 0; j <= i; ++j) + sum += x[0][j]; + sma[i] = sum / (i + 1); + } + else + { + fp_t sum = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + sum += x[0][j]; + sma[i] = sum / k; + } + } + + fp_t ema_roc = 0; + uint_t ema_period = 5; + fp_t ema_a = 2.0 / (ema_period + 1); + fp_t phase = 0; + int_t first = 1; + + for (uint_t j = 0; j < n && j < k; ++j) { y[0][j] = 0; y[1][j] = 0; } + for (uint_t i = k; i < n; ++i) + { + fp_t roc = sma[i] - sma[i - 2]; + + if (first) + { + ema_roc = roc; + first = 0; + } + else + { + ema_roc = ema_roc + ema_a * (roc - ema_roc); + } + + fp_t smoothed = ema_roc; + if (smoothed > 1.0) smoothed = 1.0; + if (smoothed < 0.01) smoothed = 0.01; + + fp_t wperiod = 2 * PI / smoothed; + if (wperiod < 5) wperiod = 5; + if (wperiod > 12) wperiod = 12; + + if (smoothed >= 1.0) + { + phase = 0; + } + else if (smoothed <= 0.01) + { + phase = 2 * PI; + } + else + { + phase += 2 * PI / wperiod; + } + while (phase > 2 * PI) phase -= 2 * PI; + + y[0][i] = cos(phase); + y[1][i] = cos(phase + PI / 2); + } + + free(sma); + return 0; +} + diff --git a/lib/mfi.c b/lib/mfi.c new file mode 100644 index 0000000..c93803c --- /dev/null +++ b/lib/mfi.c @@ -0,0 +1,84 @@ +// Mfi +// +// mfi +// +// Money Flow Index +// +// 100 - 100/(1 + MF_ratio), volume-weighted RSI +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_mfi_params_t; + +ts_parameter_t const ts_mfi_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_mfi_params_t) }; + +uint_t ts_mfi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_mfi_params_t *)c)->k : 0 ); +} + +int ts_mfi( + 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 +) +{ + TS_PROLOGUE( ts_mfi_p ); + if ( !c ) return -1; + uint_t k = ((ts_mfi_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + fp_t *tp = calloc( n, sizeof( fp_t ) ); + if ( !tp ) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + tp[i] = ( ts_h( x, i ) + ts_l( x, i ) + ts_c( x, i ) ) / 3.0; + } + + fp_t pos_sum = 0; + fp_t neg_sum = 0; + + for (uint_t i = 0; i < k; ++i) y[0][i] = 0; + + for( uint_t i = 1; i <= k; ++i ) + { + fp_t raw_mf = tp[i] * ts_v( x, i ); + if( tp[i] > tp[i - 1] ) + pos_sum += raw_mf; + else + neg_sum += raw_mf; + } + + y[0][k] = ( neg_sum == 0 ) ? 100.0 : 100.0 - 100.0 / ( 1.0 + pos_sum / neg_sum ); + + for( uint_t i = k + 1; i < n; ++i ) + { + fp_t cur_raw_mf = tp[i] * ts_v( x, i ); + if( tp[i] > tp[i - 1] ) + pos_sum += cur_raw_mf; + else + neg_sum += cur_raw_mf; + + fp_t old_raw_mf = tp[i - k] * ts_v( x, i - k ); + if( tp[i - k] > tp[i - k - 1] ) + pos_sum -= old_raw_mf; + else + neg_sum -= old_raw_mf; + + y[0][i] = ( neg_sum == 0 ) ? 100.0 : 100.0 - 100.0 / ( 1.0 + pos_sum / neg_sum ); + } + + free( tp ); + return 0; +} + diff --git a/lib/midpoint.c b/lib/midpoint.c new file mode 100644 index 0000000..c28cdff --- /dev/null +++ b/lib/midpoint.c @@ -0,0 +1,53 @@ +// Midpoint +// +// midpoint +// +// Midpoint over rolling window +// +// (highest_high + lowest_low) / 2 +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_midpoint_params_t; + +ts_parameter_t const ts_midpoint_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_midpoint_params_t) }; + +uint_t ts_midpoint_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_midpoint_params_t *)c)->k - 1 : 0 ); +} + +int ts_midpoint( + 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 +) +{ + TS_PROLOGUE( ts_midpoint_p ); + if ( !c ) return -1; + uint_t k = ((ts_midpoint_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t max_val = x[0][i]; + fp_t min_val = x[0][i]; + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = x[0][i - j]; + if( val > max_val ) max_val = val; + if( val < min_val ) min_val = val; + } + y[0][i] = ( max_val + min_val ) / 2; + } + return 0; +} + diff --git a/lib/midprice.c b/lib/midprice.c new file mode 100644 index 0000000..d26a58e --- /dev/null +++ b/lib/midprice.c @@ -0,0 +1,53 @@ +// Midprice +// +// midprice +// +// Midprice +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_midprice_params_t; + +ts_parameter_t const ts_midprice_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_midprice_params_t) }; + +uint_t ts_midprice_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_midprice_params_t *)c)->k - 1 : 0 ); +} + +int ts_midprice( + 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 +) +{ + TS_PROLOGUE( ts_midprice_p ); + if ( !c ) return -1; + uint_t k = ((ts_midprice_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t max_val = x[0][i]; + fp_t min_val = x[0][i]; + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = x[0][i - j]; + if( val > max_val ) max_val = val; + if( val < min_val ) min_val = val; + } + y[0][i] = ( max_val + min_val ) / 2; + } + return 0; +} + diff --git a/lib/minimum.c b/lib/minimum.c new file mode 100755 index 0000000..d2ba15f --- /dev/null +++ b/lib/minimum.c @@ -0,0 +1,56 @@ +// Minimum +// +// minimum - min +// +// Finds the minimum value in the past k data points +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_minimum_params_t; + +ts_parameter_t const ts_minimum_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_minimum_params_t) }; + + +uint_t ts_minimum_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_minimum_params_t *)c)->k - 1 : 0 ); +} + +int ts_minimum( + 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 +) +{ + TS_PROLOGUE( ts_minimum_p ); + if ( !c ) return -1; + uint_t k = ((ts_minimum_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k - 1; + uint_t xj = 0; + uint_t yi = k - 1; + + for( ; xi < n; xi++, xj++, yi++ ) + { + y[0][yi] = x[0][xj]; + + for( uint_t i = xj; i <= xi; i++ ) + { + if( x[0][i] < y[0][yi] ) + { + y[0][yi] = x[0][i]; + } + } + } + return 0; +} + diff --git a/lib/minmax.c b/lib/minmax.c new file mode 100644 index 0000000..4bee598 --- /dev/null +++ b/lib/minmax.c @@ -0,0 +1,53 @@ +// Minmax +// +// minmax +// +// Min and Max over rolling window +// +// min(x[t-k+1..t]), max(x[t-k+1..t]) +// +// Inputs: 1 +// Outputs: 2 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_minmax_params_t; + +ts_parameter_t const ts_minmax_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_minmax_params_t) }; + +uint_t ts_minmax_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_minmax_params_t *)c)->k - 1 : 0 ); +} + +int ts_minmax( + 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 +) +{ + TS_PROLOGUE( ts_minmax_p ); + if ( !c ) return -1; + uint_t k = ((ts_minmax_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t min_val = x[0][i - k + 1]; + fp_t max_val = x[0][i - k + 1]; + for( uint_t j = i - k + 2; j <= i; ++j ) + { + if( x[0][j] < min_val ) min_val = x[0][j]; + if( x[0][j] > max_val ) max_val = x[0][j]; + } + y[0][i] = min_val; + y[1][i] = max_val; + } + return 0; +} + diff --git a/lib/missing_bar_count.c b/lib/missing_bar_count.c new file mode 100644 index 0000000..e6f548e --- /dev/null +++ b/lib/missing_bar_count.c @@ -0,0 +1,51 @@ +// Missing Bar Count +// +// missing_bar_count +// +// Missing bar counter +// +// Counts consecutive missing data bars +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t threshold; +} ts_missing_bar_count_params_t; + +ts_parameter_t const ts_missing_bar_count_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_missing_bar_count_params_t) }; + +uint_t ts_missing_bar_count_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_missing_bar_count( + 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 +) +{ + TS_PROLOGUE( ts_missing_bar_count_p ); + if ( !c ) return -1; + if (n == 0) return -1; + fp_t threshold = ((ts_missing_bar_count_params_t *)c)->threshold; + + y[0][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + if( fabs( x[0][i] - x[0][i - 1] ) < threshold ) + y[0][i] = y[0][i - 1] + 1; + else + y[0][i] = 0; + } + return 0; +} + diff --git a/lib/momentum.c b/lib/momentum.c new file mode 100644 index 0000000..f0d89fd --- /dev/null +++ b/lib/momentum.c @@ -0,0 +1,46 @@ +// Momentum +// +// momentum +// +// Momentum +// +// C[t] - C[t-k] +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_momentum_params_t; + +ts_parameter_t const ts_momentum_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_momentum_params_t) }; + +uint_t ts_momentum_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_momentum_params_t *)c)->k : 0 ); +} + +int ts_momentum( + 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 +) +{ + TS_PROLOGUE( ts_momentum_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_momentum_params_t *)c)->k; + + for( uint_t i = k; i < n; ++i ) + { + y[0][i] = x[0][i] - x[0][i - k]; + } + + return 0; +} + diff --git a/lib/money_flow_multiplier.c b/lib/money_flow_multiplier.c new file mode 100644 index 0000000..f00707c --- /dev/null +++ b/lib/money_flow_multiplier.c @@ -0,0 +1,42 @@ +// Money Flow Multiplier +// +// money_flow_multiplier +// +// Money Flow Multiplier +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_money_flow_multiplier_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +// ( ( c - l ) - ( h - c ) ) / ( h - l ) +uint_t ts_money_flow_multiplier_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_money_flow_multiplier( + 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 +) +{ + TS_PROLOGUE( ts_money_flow_multiplier_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t denom = x[1][i] - x[2][i]; + y[0][i] = denom != 0 ? ( ( x[3][i] - x[2][i] ) - ( x[1][i] - x[3][i] ) ) / denom : 0; + } + return 0; +} + diff --git a/lib/moving_linear_regression.c b/lib/moving_linear_regression.c new file mode 100644 index 0000000..ceb34d5 --- /dev/null +++ b/lib/moving_linear_regression.c @@ -0,0 +1,83 @@ +// Moving Linear Regression +// +// moving_linear_regression +// +// Moving Linear Regression +// +// Rolling least-squares regression line +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_moving_linear_regression_params_t; + +ts_parameter_t const ts_moving_linear_regression_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_moving_linear_regression_params_t) }; + +uint_t ts_moving_linear_regression_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_moving_linear_regression_params_t *)c)->k - 1 : 0 ); +} + +int ts_moving_linear_regression( + 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 +) +{ + TS_PROLOGUE( ts_moving_linear_regression_p ); + if ( !c ) return -1; + uint_t k = ((ts_moving_linear_regression_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t denom = (fp_t)k * sum_x2 - sum_x * sum_x; + fp_t fk = (fp_t)k; + + fp_t sum_y = 0, sum_xy = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + } + if (denom == 0) { + y[0][k - 1] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + fp_t x_last = (fp_t)(k - 1); + y[0][k - 1] = intercept + slope * x_last; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + if (denom == 0) { + y[0][i] = sum_y / fk; + } else { + fp_t slope = (fk * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / fk; + fp_t x_last = (fp_t)(k - 1); + y[0][i] = intercept + slope * x_last; + } + } + return 0; +} + diff --git a/lib/moving_linear_regression_r2.c b/lib/moving_linear_regression_r2.c new file mode 100644 index 0000000..3015a25 --- /dev/null +++ b/lib/moving_linear_regression_r2.c @@ -0,0 +1,88 @@ +// Moving Linear Regression R2 +// +// moving_linear_regression_r2 +// +// Moving Linear Regression R² +// +// Rolling coefficient of determination +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_moving_linear_regression_r2_params_t; + +ts_parameter_t const ts_moving_linear_regression_r2_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_moving_linear_regression_r2_params_t) }; + +uint_t ts_moving_linear_regression_r2_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_moving_linear_regression_r2_params_t *)c)->k - 1 : 0 ); +} + +int ts_moving_linear_regression_r2( + 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 +) +{ + TS_PROLOGUE( ts_moving_linear_regression_r2_p ); + if ( !c ) return -1; + uint_t k = ((ts_moving_linear_regression_r2_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum_x = 0, sum_x2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + sum_x += xv; + sum_x2 += xv * xv; + } + fp_t fk = (fp_t)k; + fp_t denom_x = fk * sum_x2 - sum_x * sum_x; + + fp_t sum_y = 0, sum_xy = 0, sum_y2 = 0; + for ( uint_t j = 0; j < k; ++j ) + { + fp_t yv = x[0][j]; + sum_y += yv; + sum_xy += (fp_t)j * yv; + sum_y2 += yv * yv; + } + fp_t denom_y = fk * sum_y2 - sum_y * sum_y; + fp_t num = fk * sum_xy - sum_x * sum_y; + if (denom_x == 0 || denom_y == 0) { + y[0][k - 1] = 0; + } else { + fp_t prod = denom_x * denom_y; + fp_t r = prod > 0 ? num / sqrt(prod) : 0; + y[0][k - 1] = r * r; + } + + for ( uint_t i = k; i < n; ++i ) + { + fp_t y_new = x[0][i]; + fp_t y_old = x[0][i - k]; + fp_t sum_y_old = sum_y; + sum_y = sum_y - y_old + y_new; + sum_xy = sum_xy - sum_y_old + y_old + (fk - 1) * y_new; + sum_y2 = sum_y2 - y_old * y_old + y_new * y_new; + denom_y = fk * sum_y2 - sum_y * sum_y; + num = fk * sum_xy - sum_x * sum_y; + if (denom_x == 0 || denom_y == 0) { + y[0][i] = 0; + } else { + fp_t prod = denom_x * denom_y; + fp_t r = prod > 0 ? num / sqrt(prod) : 0; + y[0][i] = r * r; + } + } + return 0; +} + diff --git a/lib/multiplication.c b/lib/multiplication.c new file mode 100644 index 0000000..74a7985 --- /dev/null +++ b/lib/multiplication.c @@ -0,0 +1,39 @@ +// Multiplication +// +// multiplication - mul +// +// f( x_0, x_1 ) = x_0 * x_1 +// +// X_n - 2 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_multiplication_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_multiplication_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_multiplication( + 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 +) +{ + TS_PROLOGUE( ts_multiplication_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] * x[1][i]; + } + return 0; +} + diff --git a/lib/nan_fn.c b/lib/nan_fn.c new file mode 100644 index 0000000..cd93069 --- /dev/null +++ b/lib/nan_fn.c @@ -0,0 +1,41 @@ +// NaN Function +// +// nan_fn - Returns NaN for all inputs +// +// y[t] = NaN +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_nan_fn_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_nan_fn_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_nan_fn( + 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 +) +{ + TS_PROLOGUE( ts_nan_fn_p ); + (void)c; + ( void ) x; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = NAN; + } + return 0; +} + diff --git a/lib/natr.c b/lib/natr.c new file mode 100644 index 0000000..ce58bb1 --- /dev/null +++ b/lib/natr.c @@ -0,0 +1,57 @@ +// Natr +// +// natr +// +// Normalized ATR +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_natr_params_t; + +ts_parameter_t const ts_natr_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_natr_params_t) }; + + +uint_t ts_natr_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_natr_params_t *)c)->k : 0 ); +} + +int ts_natr( + 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 +) +{ + TS_PROLOGUE( ts_natr_p ); + if ( !c ) return -1; + uint_t k = ((ts_natr_params_t *)c)->k; + if (k == 0) return -1; + if (n < k + 1) return -1; + + for( uint_t i = k; i < n; ++i ) + { + fp_t tr_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + fp_t tr = fmax( ts_h( x, idx ) - ts_l( x, idx ), fmax( fabs( ts_h( x, idx ) - ts_c( x, idx - 1 ) ), fabs( ts_l( x, idx ) - ts_c( x, idx - 1 ) ) ) ); + tr_sum += tr; + } + fp_t atr = tr_sum / (fp_t)k; + y[0][i] = ts_c( x, i ) != 0 ? atr / ts_c( x, i ) * 100.0 : 0; + } + return 0; +} + diff --git a/lib/nearbyint.c b/lib/nearbyint.c new file mode 100644 index 0000000..de32777 --- /dev/null +++ b/lib/nearbyint.c @@ -0,0 +1,40 @@ +// Nearbyint +// +// nearbyint +// +// Nearby integer +// +// Rounded to nearest integer per current rounding mode +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_nearbyint_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_nearbyint_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_nearbyint( + 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 +) +{ + TS_PROLOGUE( ts_nearbyint_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = nearbyint( x[0][i] ); + } + return 0; +} + diff --git a/lib/nextafter.c b/lib/nextafter.c new file mode 100644 index 0000000..8c766e8 --- /dev/null +++ b/lib/nextafter.c @@ -0,0 +1,40 @@ +// Nextafter +// +// nextafter +// +// Next representable float +// +// Next float after x toward y +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_nextafter_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_nextafter_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_nextafter( + 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 +) +{ + TS_PROLOGUE( ts_nextafter_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = nextafter( x[0][i], x[1][i] ); + } + return 0; +} + diff --git a/lib/nexttoward.c b/lib/nexttoward.c new file mode 100644 index 0000000..740dd91 --- /dev/null +++ b/lib/nexttoward.c @@ -0,0 +1,40 @@ +// Nexttoward +// +// nexttoward +// +// Next representable double +// +// Next double after x toward y +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_nexttoward_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_nexttoward_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_nexttoward( + 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 +) +{ + TS_PROLOGUE( ts_nexttoward_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = nexttoward( x[0][i], (long double)x[1][i] ); + } + return 0; +} + diff --git a/lib/none.c b/lib/none.c new file mode 100644 index 0000000..c9449d5 --- /dev/null +++ b/lib/none.c @@ -0,0 +1,36 @@ +// None (Identity) +// +// none - identity function +// +// Passes input through unchanged: y = x +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include <string.h> +#include "ts.h" + + + +ts_parameter_t const ts_none_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_none_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_none( + 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 +) +{ + TS_PROLOGUE( ts_none_p ); + (void)c; + memcpy( y[0], x[0], sizeof( fp_t ) * n ); + return 0; +} + diff --git a/lib/nvi.c b/lib/nvi.c new file mode 100644 index 0000000..f4d3397 --- /dev/null +++ b/lib/nvi.c @@ -0,0 +1,51 @@ +// Nvi +// +// nvi +// +// Negative Volume Index +// +// Accumulates price change on declining volume +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_nvi_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_nvi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_nvi( + 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 +) +{ + TS_PROLOGUE( ts_nvi_p ); + (void)c; + if (n == 0) return -1; + + fp_t nvi = 1000; + + y[0][0] = nvi; + + for( uint_t i = 1; i < n; ++i ) + { + if( x[4][i] < x[4][i - 1] && x[3][i - 1] != 0 ) + { + fp_t r = ( x[3][i] - x[3][i - 1] ) / x[3][i - 1]; + nvi = nvi * ( 1 + r ); + } + y[0][i] = nvi; + } + + return 0; +} + diff --git a/lib/obv.c b/lib/obv.c new file mode 100644 index 0000000..3765ba3 --- /dev/null +++ b/lib/obv.c @@ -0,0 +1,54 @@ +// Obv +// +// obv +// +// On-Balance Volume +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_obv_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_obv_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_obv( + 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 +) +{ + TS_PROLOGUE( ts_obv_p ); + (void)c; + TS_CHECK_INPUT(x, x_n, n); + if (n == 0) return -1; + + fp_t obv = 0; + y[0][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + if( x[3][i] > x[3][i - 1] ) + { + obv += x[4][i]; + } + else if( x[3][i] < x[3][i - 1] ) + { + obv -= x[4][i]; + } + y[0][i] = obv; + } + + return 0; +} + diff --git a/lib/on_balance_volume_rate.c b/lib/on_balance_volume_rate.c new file mode 100644 index 0000000..7183c23 --- /dev/null +++ b/lib/on_balance_volume_rate.c @@ -0,0 +1,74 @@ +// On Balance Volume Rate +// +// on_balance_volume_rate +// +// OBV Rate of Change +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + fp_t f0; +} ts_on_balance_volume_rate_params_t; + +ts_parameter_t const ts_on_balance_volume_rate_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_on_balance_volume_rate_params_t) }; + +uint_t ts_on_balance_volume_rate_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)((ts_on_balance_volume_rate_params_t *)c)->f0 + 1 : 0 ); +} + +int ts_on_balance_volume_rate( + 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 +) +{ + TS_PROLOGUE( ts_on_balance_volume_rate_p ); + if ( !c ) return -1; + uint_t k = (uint_t)((ts_on_balance_volume_rate_params_t *)c)->f0; + if (k == 0 || k >= n) return -1; + + fp_t obv = 0; + + fp_t *obv_history = NULL; + if( n > 0 ) + { + obv_history = calloc( n, sizeof( fp_t ) ); + if( !obv_history ) return -1; + } + + for( uint_t i = 1; i < n; ++i ) + { + if( x[3][i] > x[3][i - 1] ) + { + obv += x[4][i]; + } + else if( x[3][i] < x[3][i - 1] ) + { + obv -= x[4][i]; + } + obv_history[i] = obv; + } + + for (uint_t i = 0; i <= k; ++i) y[0][i] = 0; + + for( uint_t i = k + 1; i < n; ++i ) + { + fp_t obv_prev = obv_history[i - k]; + y[0][i] = obv_prev != 0 ? ( obv_history[i] - obv_prev ) / obv_prev * 100 : 0; + } + + free( obv_history ); + + return 0; +} + diff --git a/lib/open_interest.c b/lib/open_interest.c new file mode 100644 index 0000000..a3d23aa --- /dev/null +++ b/lib/open_interest.c @@ -0,0 +1,40 @@ +// Open Interest +// +// open_interest +// +// Open Interest change +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_open_interest_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_open_interest_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_open_interest( + 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 +) +{ + TS_PROLOGUE( ts_open_interest_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i]; + } + + return 0; +} + diff --git a/lib/open_minus_close.c b/lib/open_minus_close.c new file mode 100644 index 0000000..2aaca32 --- /dev/null +++ b/lib/open_minus_close.c @@ -0,0 +1,39 @@ +// Open Minus Close +// +// open_minus_close +// +// Open minus Close +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_open_minus_close_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_open_minus_close_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_open_minus_close( + 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 +) +{ + TS_PROLOGUE( ts_open_minus_close_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] - x[3][i]; + } + return 0; +} + diff --git a/lib/oscillator_cross.c b/lib/oscillator_cross.c new file mode 100644 index 0000000..0c6c5f8 --- /dev/null +++ b/lib/oscillator_cross.c @@ -0,0 +1,70 @@ +// Oscillator Cross +// +// oscillator_cross +// +// Oscillator crossover detector +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 2 + + +#include "ts.h" + +typedef struct { + fp_t fast; + fp_t slow; +} ts_oscillator_cross_params_t; + +ts_parameter_t const ts_oscillator_cross_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_oscillator_cross_params_t) }; + +uint_t ts_oscillator_cross_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)((ts_oscillator_cross_params_t *)c)->slow : 0 ); +} + +int ts_oscillator_cross( + 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 +) +{ + TS_PROLOGUE( ts_oscillator_cross_p ); + if ( !c ) return -1; + fp_t const fast = ((ts_oscillator_cross_params_t *)c)->fast; + fp_t const slow = ((ts_oscillator_cross_params_t *)c)->slow; + if (fast == 0 || slow == 0) return -1; + if (n == 0) return -1; + fp_t const fast_a = 1.0 / fast; + fp_t const slow_a = 1.0 / slow; + fp_t fast_e = x[0][0]; + fp_t slow_e = x[0][0]; + uint_t const offset = (uint_t)slow; + if (offset >= n) return -1; + + for( uint_t i = 0; i < offset; ++i ) + y[0][i] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t pf = fast_e; + fp_t ps = slow_e; + fast_e = fast_e + fast_a * ( x[0][i] - fast_e ); + slow_e = slow_e + slow_a * ( x[0][i] - slow_e ); + + if( i >= offset ) + { + if( fast_e > slow_e && pf <= ps ) + y[0][i] = 1; + else if( fast_e < slow_e && pf >= ps ) + y[0][i] = -1; + else + y[0][i] = 0; + } + } + return 0; +} + diff --git a/lib/osma.c b/lib/osma.c new file mode 100644 index 0000000..a5a2756 --- /dev/null +++ b/lib/osma.c @@ -0,0 +1,78 @@ +// Osma +// +// osma +// +// Oscillator Moving Average +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 3 + + +#include "ts.h" + +typedef struct { + fp_t fast_k; + fp_t slow_k; + fp_t signal_k; +} ts_osma_params_t; + +ts_parameter_t const ts_osma_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 3, .params_size = sizeof(ts_osma_params_t) }; + +uint_t ts_osma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)((ts_osma_params_t *)c)->slow_k : 0 ); +} + +int ts_osma( + 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 +) +{ + TS_PROLOGUE( ts_osma_p ); + if ( !c ) return -1; + fp_t fast_k = ((ts_osma_params_t *)c)->fast_k; + fp_t slow_k = ((ts_osma_params_t *)c)->slow_k; + fp_t signal_k = ((ts_osma_params_t *)c)->signal_k; + + if (fast_k == 0 || slow_k == 0 || signal_k == 0) return -1; + if (n == 0) return -1; + + fp_t fast_a = 1.0 / fast_k; + fp_t slow_a = 1.0 / slow_k; + fp_t signal_a = 1.0 / signal_k; + + fp_t fast_ema = x[0][0]; + fp_t slow_ema = x[0][0]; + + uint_t slow_offset = (uint_t)(slow_k - 1); + if (n <= slow_offset + 1) return -1; + + for( uint_t i = 1; i <= slow_offset; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + } + + fp_t macd_line = fast_ema - slow_ema; + fp_t signal_ema = macd_line; + + for( uint_t i = slow_offset + 1; i < n; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + + macd_line = fast_ema - slow_ema; + signal_ema = signal_ema + signal_a * ( macd_line - signal_ema ); + + y[0][i] = macd_line - signal_ema; + } + + return 0; +} + + diff --git a/lib/parabolic_sar.c b/lib/parabolic_sar.c new file mode 100644 index 0000000..ff84834 --- /dev/null +++ b/lib/parabolic_sar.c @@ -0,0 +1,99 @@ +// Parabolic Sar +// +// parabolic_sar +// +// Parabolic SAR +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t step; + fp_t max_af; +} ts_parabolic_sar_params_t; + +ts_parameter_t const ts_parabolic_sar_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_parabolic_sar_params_t) }; + +uint_t ts_parabolic_sar_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_parabolic_sar( + 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 +) +{ + TS_PROLOGUE( ts_parabolic_sar_p ); + if ( !c ) return -1; + fp_t step = ((ts_parabolic_sar_params_t *)c)->step; + fp_t max_af = ((ts_parabolic_sar_params_t *)c)->max_af; + if (step <= 0 || max_af <= 0) return -1; + if (isnan(step) || isnan(max_af)) return -1; + if (step > max_af) return -1; + if (n == 0) return -1; + + int_t trend = 1; + fp_t sar = ts_l( x, 0 ); + fp_t ep = ts_h( x, 0 ); + fp_t af = step; + + for ( uint_t i = 0; i < n; ++i ) + { + y[0][i] = sar; + y[1][i] = (fp_t)(trend > 0); + + if (i == 0) continue; + + if (trend > 0) + { + sar = sar + af * (ep - sar); + if (ts_l( x, i ) < sar) + { + trend = -1; + sar = ep; + ep = ts_l( x, i ); + af = step; + } + else + { + if (ts_h( x, i ) > ep) + { + ep = ts_h( x, i ); + af = af + step; + if (af > max_af) af = max_af; + } + } + } + else + { + sar = sar + af * (ep - sar); + if (ts_h( x, i ) > sar) + { + trend = 1; + sar = ep; + ep = ts_h( x, i ); + af = step; + } + else + { + if (ts_l( x, i ) < ep) + { + ep = ts_l( x, i ); + af = af + step; + if (af > max_af) af = max_af; + } + } + } + } + return 0; +} diff --git a/lib/performance_index.c b/lib/performance_index.c new file mode 100644 index 0000000..c211379 --- /dev/null +++ b/lib/performance_index.c @@ -0,0 +1,39 @@ +// Performance Index +// +// performance_index +// +// Performance Index +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_performance_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_performance_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_performance_index( + 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 +) +{ + TS_PROLOGUE( ts_performance_index_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] != 0 ? ( x[3][i] - x[0][i] ) / x[0][i] * 100 : 0; + } + return 0; +} + diff --git a/lib/performance_ratio.c b/lib/performance_ratio.c new file mode 100644 index 0000000..ffd0ece --- /dev/null +++ b/lib/performance_ratio.c @@ -0,0 +1,50 @@ +// Performance Ratio +// +// performance_ratio +// +// Performance Ratio +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_performance_ratio_params_t; + +ts_parameter_t const ts_performance_ratio_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_performance_ratio_params_t) }; + +uint_t ts_performance_ratio_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_performance_ratio_params_t *)c)->k : 0 ); +} + +int ts_performance_ratio( + 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 +) +{ + TS_PROLOGUE( ts_performance_ratio_p ); + if ( !c ) return -1; + uint_t k = ((ts_performance_ratio_params_t *)c)->k; + if (k <= 1 || n == 0) return -1; + + fp_t factor = (fp_t)k / (k - 1); + + for( uint_t i = k; i < n; ++i ) + { + fp_t v = x[0][i - k + 1]; + fp_t inner = v * v - 1; + y[0][i] = inner >= 0 ? sqrt( factor * inner ) : 0; + } + return 0; +} + diff --git a/lib/pivot_points.c b/lib/pivot_points.c new file mode 100644 index 0000000..b86c44a --- /dev/null +++ b/lib/pivot_points.c @@ -0,0 +1,51 @@ +// Pivot Points +// +// pivot_points +// +// Pivot Points +// +// +// +// Inputs: 5 +// Outputs: 5 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_pivot_points_p = { .n_inputs = 4, .n_outputs = 5, .n_constants = 0, .params_size = 0 }; + +uint_t ts_pivot_points_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_pivot_points( + 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 +) +{ + TS_PROLOGUE( ts_pivot_points_p ); + (void)c; + if (n > 0) { y[0][0] = 0; y[1][0] = 0; y[2][0] = 0; y[3][0] = 0; y[4][0] = 0; } + for( uint_t i = 1; i < n; ++i ) + { + fp_t prev_h = ts_h( x, i - 1 ); + fp_t prev_l = ts_l( x, i - 1 ); + fp_t prev_c = ts_c( x, i - 1 ); + + fp_t p = ( prev_h + prev_l + prev_c ) / 3.0; + fp_t range = prev_h - prev_l; + + y[0][i] = p; + y[1][i] = 2.0 * p - prev_l; + y[2][i] = 2.0 * p - prev_h; + y[3][i] = p + range; + y[4][i] = p - range; + } + return 0; +} + diff --git a/lib/plot.c b/lib/plot.c new file mode 100644 index 0000000..f459b05 --- /dev/null +++ b/lib/plot.c @@ -0,0 +1,39 @@ +// Plot +// +// plot +// +// Plot indicator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_plot_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_plot_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_plot( + 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 +) +{ + TS_PROLOGUE( ts_plot_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i]; + } + return 0; +} + diff --git a/lib/poisson_pdf.c b/lib/poisson_pdf.c new file mode 100644 index 0000000..e1e2d59 --- /dev/null +++ b/lib/poisson_pdf.c @@ -0,0 +1,48 @@ +// Poisson Pdf +// +// poisson_pdf +// +// Poisson probability density +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t lambda; +} ts_poisson_pdf_params_t; + +ts_parameter_t const ts_poisson_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_poisson_pdf_params_t) }; + +uint_t ts_poisson_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_poisson_pdf( + 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 +) +{ + TS_PROLOGUE( ts_poisson_pdf_p ); + if ( !c ) return -1; + fp_t lambda = ((ts_poisson_pdf_params_t *)c)->lambda; + if (lambda < 0.0 || isnan(lambda) || isinf(lambda)) return -1; + for( uint_t i = 0; i < n; ++i ) + { + fp_t k_val = x[0][i]; + if( k_val < 0 ) { y[0][i] = 0; continue; } + uint_t k = (uint_t)k_val; + y[0][i] = pow( lambda, k ) * exp( -lambda ) / tgamma( k + 1.0 ); + } + return 0; +} + diff --git a/lib/polynomial.c b/lib/polynomial.c new file mode 100644 index 0000000..2914fe6 --- /dev/null +++ b/lib/polynomial.c @@ -0,0 +1,41 @@ +// Polynomial Power +// +// polynomial - pow(x, y) +// +// x raised to the power y +// +// Inputs: 2 (base, exponent) +// Outputs: 1 +// Constants: 0 + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_polynomial_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_polynomial_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_polynomial( + 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 +) +{ + TS_PROLOGUE( ts_polynomial_p ); + (void)c; + /* x[0] = base, x[1] = exponent */ + for (uint_t i = 0; i < n; ++i) + { + fp_t base = x[0][i]; + fp_t exponent = x[1][i]; + if( base < 0.0 && exponent != (fp_t)(long)exponent ) { y[0][i] = 0; continue; } + y[0][i] = pow(base, exponent); + } + return 0; +} + diff --git a/lib/power.c b/lib/power.c new file mode 100755 index 0000000..41bb644 --- /dev/null +++ b/lib/power.c @@ -0,0 +1,71 @@ +// Power +// +// power - pow +// +// f( x ) = x^r +// +// X_n - 1 +// Y_n - 1 +// C_n - 1 +// +// c[0] = r + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t r; +} ts_power_params_t; + +ts_parameter_t const ts_power_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_power_params_t) }; + + +uint_t ts_power_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_power( + 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 +) +{ + TS_PROLOGUE( ts_power_p ); + if ( !c ) return -1; + fp_t r = ((ts_power_params_t *)c)->r; + + for( uint_t i = 0; i < n; ++i ) + { + fp_t base = x[0][i]; + if ( base > 0 ) + { + y[0][i] = pow( base, r ); + } + else if ( base < 0 ) + { + fp_t intpart; + if ( modf( r, &intpart ) == 0 ) + { + fp_t result = pow( -base, r ); + long ip = (long)intpart; + y[0][i] = (ip >= 0 && (ip & 1) != 0) ? -result : result; + } + else + { + y[0][i] = 0; + } + } + else + { + if (r > 0.0) y[0][i] = 0.0; + else if (r < 0.0) y[0][i] = INFINITY; + else y[0][i] = 1.0; + } + } + return 0; +} + diff --git a/lib/ppo.c b/lib/ppo.c new file mode 100644 index 0000000..b2b3bbf --- /dev/null +++ b/lib/ppo.c @@ -0,0 +1,94 @@ +// Ppo +// +// ppo +// +// Percentage Price Oscillator +// +// +// +// Inputs: 5 +// Outputs: 2 +// Constants: 3 + + +#include "ts.h" + +typedef struct { + fp_t fast_k; + fp_t slow_k; + fp_t signal_k; +} ts_ppo_params_t; + +ts_parameter_t const ts_ppo_p = { .n_inputs = 5, .n_outputs = 2, .n_constants = 3, .params_size = sizeof(ts_ppo_params_t) }; + +uint_t ts_ppo_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? (uint_t)(((ts_ppo_params_t *)c)->slow_k + ((ts_ppo_params_t *)c)->signal_k - 2) : 0 ); +} + +int ts_ppo( + 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 +) +{ + TS_PROLOGUE( ts_ppo_p ); + if ( !c ) return -1; + fp_t fast_k = ((ts_ppo_params_t *)c)->fast_k; + fp_t slow_k = ((ts_ppo_params_t *)c)->slow_k; + fp_t signal_k = ((ts_ppo_params_t *)c)->signal_k; + + if (fast_k == 0 || slow_k == 0 || signal_k == 0) return -1; + if (n == 0) return -1; + + fp_t fast_a = 1.0 / fast_k; + fp_t slow_a = 1.0 / slow_k; + fp_t signal_a = 1.0 / signal_k; + + fp_t fast_ema = x[0][0]; + fp_t slow_ema = x[0][0]; + + uint_t slow_offset = (uint_t)(slow_k - 1); + uint_t signal_offset = (uint_t)(signal_k - 1); + uint_t total_offset = slow_offset + signal_offset; + + if (n <= total_offset) return -1; + + for( uint_t i = 1; i <= slow_offset; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + } + + fp_t ppo_val = slow_ema != 0 ? (fast_ema - slow_ema) / slow_ema * 100.0 : 0; + fp_t signal_ema = ppo_val; + + for( uint_t i = slow_offset + 1; i <= total_offset; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + + ppo_val = slow_ema != 0 ? (fast_ema - slow_ema) / slow_ema * 100.0 : 0; + signal_ema = signal_ema + signal_a * ( ppo_val - signal_ema ); + } + + y[0][total_offset] = ppo_val; + y[1][total_offset] = signal_ema; + + for( uint_t i = total_offset + 1; i < n; ++i ) + { + fast_ema = fast_ema + fast_a * ( x[0][i] - fast_ema ); + slow_ema = slow_ema + slow_a * ( x[0][i] - slow_ema ); + + ppo_val = slow_ema != 0 ? (fast_ema - slow_ema) / slow_ema * 100.0 : 0; + signal_ema = signal_ema + signal_a * ( ppo_val - signal_ema ); + + y[0][i] = ppo_val; + y[1][i] = signal_ema; + } + + return 0; +} + + diff --git a/lib/pretty_good_oscillator.c b/lib/pretty_good_oscillator.c new file mode 100644 index 0000000..65a05c2 --- /dev/null +++ b/lib/pretty_good_oscillator.c @@ -0,0 +1,64 @@ +// Pretty Good Oscillator +// +// pretty_good_oscillator +// +// Pretty Good Oscillator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_pretty_good_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_pretty_good_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_pretty_good_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_pretty_good_oscillator_p ); + (void)c; + if (n == 0) return -1; + fp_t ema14 = x[0][0]; + fp_t ema30 = x[0][0]; + fp_t ema90 = x[0][0]; + fp_t a14 = 15.0 / (19.0 * 14); + fp_t a30 = 15.0 / (19.0 * 30); + fp_t a90 = 15.0 / (19.0 * 90); + + for( uint_t i = 0; i < n; ++i ) + { + if( i > 0 ) + { + ema14 = ema14 + a14 * (x[0][i] - ema14); + ema30 = ema30 + a30 * (x[0][i] - ema30); + ema90 = ema90 + a90 * (x[0][i] - ema90); + } + + fp_t arr[3] = {ema14, ema30, ema90}; + for( uint_t a = 0; a < 2; ++a ) + for( uint_t b = a + 1; b < 3; ++b ) + if( arr[a] > arr[b] ) { fp_t t = arr[a]; arr[a] = arr[b]; arr[b] = t; } + + fp_t median = arr[1]; + + if( median == 0 ) + y[0][i] = 0; + else + y[0][i] = (x[0][i] / median - 1.0); + } + return 0; +} + diff --git a/lib/price_channel.c b/lib/price_channel.c new file mode 100644 index 0000000..9aba070 --- /dev/null +++ b/lib/price_channel.c @@ -0,0 +1,58 @@ +// Price Channel +// +// price_channel +// +// Price Channel +// +// +// +// Inputs: 1 +// Outputs: 3 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_price_channel_params_t; + +ts_parameter_t const ts_price_channel_p = { .n_inputs = 1, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_price_channel_params_t) }; + +uint_t ts_price_channel_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_price_channel_params_t *)c)->k - 1 : 0 ); +} + +int ts_price_channel( + 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 +) +{ + TS_PROLOGUE( ts_price_channel_p ); + if ( !c ) return -1; + uint_t k = ((ts_price_channel_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t hh = x[0][i - k + 1]; + fp_t ll = x[0][i - k + 1]; + fp_t sum = x[0][i - k + 1]; + + for( uint_t j = i - k + 2; j <= i; ++j ) + { + if( x[0][j] > hh ) hh = x[0][j]; + if( x[0][j] < ll ) ll = x[0][j]; + sum += x[0][j]; + } + + y[0][i] = hh; + y[1][i] = sum / (fp_t)k; + y[2][i] = ll; + } + return 0; +} + diff --git a/lib/price_momentum_oscillator.c b/lib/price_momentum_oscillator.c new file mode 100644 index 0000000..b03f350 --- /dev/null +++ b/lib/price_momentum_oscillator.c @@ -0,0 +1,45 @@ +// Price Momentum Oscillator +// +// price_momentum_oscillator +// +// Price Momentum Oscillator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_price_momentum_oscillator_params_t; + +ts_parameter_t const ts_price_momentum_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_price_momentum_oscillator_params_t) }; + +uint_t ts_price_momentum_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_price_momentum_oscillator_params_t *)c)->k : 0 ); +} + +int ts_price_momentum_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_price_momentum_oscillator_p ); + if ( !c ) return -1; + uint_t k = ((ts_price_momentum_oscillator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = k; i < n; ++i) + { + y[0][i] = x[0][i - k] != 0 ? ( x[0][i] / x[0][i - k] - 1 ) * 100 : 0; + } + return 0; +} + diff --git a/lib/price_rate_of_change.c b/lib/price_rate_of_change.c new file mode 100644 index 0000000..8f26548 --- /dev/null +++ b/lib/price_rate_of_change.c @@ -0,0 +1,45 @@ +// Price Rate Of Change +// +// price_rate_of_change +// +// Price Rate of Change +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_price_rate_of_change_params_t; + +ts_parameter_t const ts_price_rate_of_change_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_price_rate_of_change_params_t) }; + +uint_t ts_price_rate_of_change_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_price_rate_of_change_params_t *)c)->k : 0 ); +} + +int ts_price_rate_of_change( + 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 +) +{ + TS_PROLOGUE( ts_price_rate_of_change_p ); + if ( !c ) return -1; + uint_t k = ((ts_price_rate_of_change_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = k; i < n; ++i) + { + y[0][i] = x[0][i - k] != 0 ? (x[0][i] - x[0][i - k]) / x[0][i - k] * 100 : 0; + } + return 0; +} + diff --git a/lib/prime_number_bands.c b/lib/prime_number_bands.c new file mode 100644 index 0000000..ac94e7b --- /dev/null +++ b/lib/prime_number_bands.c @@ -0,0 +1,58 @@ +// Prime Number Bands +// +// prime_number_bands +// +// Prime Number Bands +// +// +// +// Inputs: 1 +// Outputs: 3 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_prime_number_bands_params_t; + +ts_parameter_t const ts_prime_number_bands_p = { .n_inputs = 1, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_prime_number_bands_params_t) }; + +uint_t ts_prime_number_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_prime_number_bands_params_t *)c)->k - 1 : 0 ); +} + +int ts_prime_number_bands( + 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 +) +{ + TS_PROLOGUE( ts_prime_number_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_prime_number_bands_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0, sum2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + sum += v; + sum2 += v * v; + } + fp_t mean = sum / k; + fp_t variance = sum2 / k - mean * mean; + fp_t stddev = variance >= 0 ? sqrt( variance ) : 0; + y[0][i] = mean; + y[1][i] = mean + 2.0 * stddev; + y[2][i] = mean - 2.0 * stddev; + } + return 0; +} + diff --git a/lib/prime_number_oscillator.c b/lib/prime_number_oscillator.c new file mode 100644 index 0000000..a86fa7b --- /dev/null +++ b/lib/prime_number_oscillator.c @@ -0,0 +1,82 @@ +// Prime Number Oscillator +// +// prime_number_oscillator +// +// Prime Number Oscillator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_prime_number_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_prime_number_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 23 ); +} + +int ts_prime_number_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_prime_number_oscillator_p ); + (void)c; + fp_t last_gap = 0; + fp_t prev_prime = 0; + bool first_prime = true; + + for( uint_t i = 0; i < n; ++i ) + { + long v = (long)x[0][i]; + bool is_p = false; + + if( v >= 2 ) + { + if( v == 2 ) + is_p = true; + else if( v % 2 != 0 ) + { + is_p = true; + for( long d = 3; d <= v / d; d += 2 ) + { + if( v % d == 0 ) + { + is_p = false; + break; + } + } + } + } + + if( is_p ) + { + if( first_prime ) + { + prev_prime = (fp_t)v; + first_prime = false; + y[0][i] = 0; + } + else + { + last_gap = (fp_t)v - prev_prime; + prev_prime = (fp_t)v; + y[0][i] = last_gap; + } + } + else + { + y[0][i] = last_gap; + } + } + return 0; +} + diff --git a/lib/prings_kst.c b/lib/prings_kst.c new file mode 100644 index 0000000..e33688e --- /dev/null +++ b/lib/prings_kst.c @@ -0,0 +1,79 @@ +// Prings Kst +// +// prings_kst +// +// Prings KST +// +// +// +// Inputs: 1 +// Outputs: 2 +// Constants: 0 + + +#include <stdlib.h> +#include "ts.h" + + +ts_parameter_t const ts_prings_kst_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 0, .params_size = 0 }; + +uint_t ts_prings_kst_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 8 ); +} + +int ts_prings_kst( + 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 +) +{ + TS_PROLOGUE( ts_prings_kst_p ); + (void)c; + if (n < 23) return -1; + + fp_t *raw_kst = calloc( n, sizeof( fp_t ) ); + if ( !raw_kst ) return -1; + + for (uint_t i = 0; i < 8; ++i) { y[0][i] = 0; y[1][i] = 0; } + + for( uint_t i = 8; i < n; ++i ) + { + if ( x[0][i - 2] == 0 || x[0][i - 3] == 0 || x[0][i - 5] == 0 || x[0][i - 8] == 0 ) + { + raw_kst[i] = 0; + continue; + } + fp_t r2 = ( x[0][i] - x[0][i - 2] ) / x[0][i - 2] * 100; + fp_t r3 = ( x[0][i] - x[0][i - 3] ) / x[0][i - 3] * 100; + fp_t r5 = ( x[0][i] - x[0][i - 5] ) / x[0][i - 5] * 100; + fp_t r8 = ( x[0][i] - x[0][i - 8] ) / x[0][i - 8] * 100; + raw_kst[i] = r2 + 2 * r3 + 3 * r5 + 4 * r8; + } + + fp_t alpha1 = 2.0 / 11.0; + y[0][8] = raw_kst[8]; + for( uint_t i = 9; i < n; ++i ) + { + y[0][i] = y[0][i - 1] + alpha1 * ( raw_kst[i] - y[0][i - 1] ); + } + + fp_t alpha2 = 2.0 / 16.0; + for( uint_t i = 8; i < 16; ++i ) + y[1][i] = 0; + { + fp_t seed = 0; + for( uint_t i = 8; i <= 16; ++i ) seed += y[0][i]; + seed /= 9.0; + y[1][16] = seed; + } + for( uint_t i = 17; i < n; ++i ) + { + y[1][i] = y[1][i - 1] + alpha2 * ( y[0][i] - y[1][i - 1] ); + } + + free( raw_kst ); + return 0; +} + diff --git a/lib/prings_special_k.c b/lib/prings_special_k.c new file mode 100644 index 0000000..91a40ba --- /dev/null +++ b/lib/prings_special_k.c @@ -0,0 +1,86 @@ +// Prings Special K +// +// prings_special_k +// +// Prings Special K +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 2 + + +#include "ts.h" + +typedef struct { + uint_t k1; + uint_t k2; +} ts_prings_special_k_params_t; + +ts_parameter_t const ts_prings_special_k_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_prings_special_k_params_t) }; + +uint_t ts_prings_special_k_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_prings_special_k_params_t *)c)->k1 > 0 && ((ts_prings_special_k_params_t *)c)->k2 > 0 ? ((ts_prings_special_k_params_t *)c)->k1 + ((ts_prings_special_k_params_t *)c)->k2 - 2 : 0 : 0 ); +} + +int ts_prings_special_k( + 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 +) +{ + TS_PROLOGUE( ts_prings_special_k_p ); + if ( !c ) return -1; + uint_t k1 = ((ts_prings_special_k_params_t *)c)->k1; + uint_t k2 = ((ts_prings_special_k_params_t *)c)->k2; + if (k1 == 0 || k2 == 0) return -1; + + fp_t ema = 0; + fp_t ema_a = 1.0 / (fp_t)k2; + + for (uint_t j = 0; j < n && j < k1 - 1; ++j) { y[0][j] = 0; y[1][j] = 0; } + for( uint_t i = k1 - 1; i < n; ++i ) + { + fp_t ll = ts_l( x, i ); + for( uint_t j = 1; j < k1; ++j ) + { + fp_t v = ts_l( x, i - j ); + if( v < ll ) ll = v; + } + + fp_t hh = ts_h( x, i ); + for( uint_t j = 1; j < k1; ++j ) + { + fp_t v = ts_h( x, i - j ); + if( v > hh ) hh = v; + } + + fp_t raw = ( hh != ll ) ? 100.0 * ( ts_c( x, i ) - ll ) / ( hh - ll ) : 0; + + if( i == k1 - 1 ) + { + ema = raw; + } + else + { + ema = ema + ema_a * ( raw - ema ); + } + + y[0][i] = ema; + + if( i >= k1 + k2 - 2 ) + { + fp_t d_sum = 0; + for( uint_t j = 0; j < k2; ++j ) + { + d_sum += y[0][i - j]; + } + y[1][i] = d_sum / (fp_t)k2; + } + } + return 0; +} + diff --git a/lib/psychological_line.c b/lib/psychological_line.c new file mode 100644 index 0000000..35fd912 --- /dev/null +++ b/lib/psychological_line.c @@ -0,0 +1,56 @@ +// Psychological Line +// +// psychological_line +// +// Psychological Line +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_psychological_line_params_t; + +ts_parameter_t const ts_psychological_line_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_psychological_line_params_t) }; + +uint_t ts_psychological_line_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_psychological_line_params_t *)c)->k - 1 : 0 ); +} + +int ts_psychological_line( + 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 +) +{ + TS_PROLOGUE( ts_psychological_line_p ); + if ( !c ) return -1; + uint_t k = ((ts_psychological_line_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t sum = 0; + + for( uint_t i = 0; i < k; ++i ) + { + sum += ( ts_c( x, i ) > ts_o( x, i ) ) ? 100.0 : 0.0; + } + + y[0][k - 1] = sum / (fp_t)k; + + for( uint_t i = k; i < n; ++i ) + { + fp_t s = ( ts_c( x, i ) > ts_o( x, i ) ) ? 100.0 : 0.0; + fp_t s_old = ( ts_c( x, i - k ) > ts_o( x, i - k ) ) ? 100.0 : 0.0; + y[0][i] = y[0][i - 1] + ( s - s_old ) / (fp_t)k; + } + return 0; +} + diff --git a/lib/pvi.c b/lib/pvi.c new file mode 100644 index 0000000..26265d5 --- /dev/null +++ b/lib/pvi.c @@ -0,0 +1,61 @@ +// Pvi +// +// pvi +// +// Positive Volume Index +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_pvi_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_pvi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_pvi( + 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 +) +{ + TS_PROLOGUE( ts_pvi_p ); + (void)c; + fp_t pvi = 1000; + + if (n == 0) return -1; + + y[0][0] = pvi; + + if( n > 1 ) + { + if( x[4][1] > x[4][0] && x[3][0] != 0 ) + { + fp_t r = ( x[3][1] - x[3][0] ) / x[3][0]; + pvi = pvi * ( 1 + r ); + } + y[0][1] = pvi; + } + + for( uint_t i = 2; i < n; ++i ) + { + if( x[4][i] > x[4][i - 1] && x[3][i - 1] != 0 ) + { + fp_t r = ( x[3][i] - x[3][i - 1] ) / x[3][i - 1]; + pvi = pvi * ( 1 + r ); + } + y[0][i] = pvi; + } + + return 0; +} + diff --git a/lib/pvt.c b/lib/pvt.c new file mode 100644 index 0000000..f98c288 --- /dev/null +++ b/lib/pvt.c @@ -0,0 +1,46 @@ +// Pvt +// +// pvt +// +// Price Volume Trend +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_pvt_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_pvt_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_pvt( + 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 +) +{ + TS_PROLOGUE( ts_pvt_p ); + (void)c; + if (n > 0) y[0][0] = 0; + + fp_t pvt = 0; + + for( uint_t i = 1; i < n; ++i ) + { + if( x[3][i - 1] != 0 ) + pvt += ( x[3][i] / x[3][i - 1] - 1 ) * x[4][i]; + y[0][i] = pvt; + } + + return 0; +} + diff --git a/lib/qstick.c b/lib/qstick.c new file mode 100644 index 0000000..d2f4361 --- /dev/null +++ b/lib/qstick.c @@ -0,0 +1,50 @@ +// Qstick +// +// qstick +// +// Q Stick +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_qstick_params_t; + +ts_parameter_t const ts_qstick_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_qstick_params_t) }; + +uint_t ts_qstick_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_qstick_params_t *)c)->k - 1 : 0 ); +} + +int ts_qstick( + 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 +) +{ + TS_PROLOGUE( ts_qstick_p ); + if ( !c ) return -1; + uint_t k = ((ts_qstick_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + sum += ts_c( x, i - k + 1 + j ) - ts_o( x, i - k + 1 + j ); + } + y[0][i] = sum / (fp_t)k; + } + return 0; +} + diff --git a/lib/radian_degree.c b/lib/radian_degree.c new file mode 100644 index 0000000..046762a --- /dev/null +++ b/lib/radian_degree.c @@ -0,0 +1,39 @@ +// Radian Degree +// +// radian_degree +// +// Radians to Degrees +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_radian_degree_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_radian_degree_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_radian_degree( + 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 +) +{ + TS_PROLOGUE( ts_radian_degree_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] * 180.0 / PI; + } + return 0; +} + diff --git a/lib/rainbow_ma.c b/lib/rainbow_ma.c new file mode 100644 index 0000000..3311ec9 --- /dev/null +++ b/lib/rainbow_ma.c @@ -0,0 +1,61 @@ +// Rainbow Ma +// +// rainbow_ma +// +// Rainbow Moving Average +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_rainbow_ma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_rainbow_ma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 19 ); +} + +int ts_rainbow_ma( + 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 +) +{ + TS_PROLOGUE( ts_rainbow_ma_p ); + (void)c; + uint_t periods[7] = {3, 5, 7, 9, 11, 13, 15}; + + for( uint_t j = 0; j < n && j < 19; ++j ) y[0][j] = 0; + for( uint_t i = 19; i < n; ++i ) + { + fp_t sma_values[7]; + + for( uint_t p = 0; p < 7; ++p ) + { + fp_t sum = 0; + for( uint_t j = 0; j < periods[p]; ++j ) + sum += x[0][i - j]; + sma_values[p] = sum / periods[p]; + } + + for( uint_t a = 0; a < 6; ++a ) + for( uint_t b = a + 1; b < 7; ++b ) + if( sma_values[a] > sma_values[b] ) + { + fp_t t = sma_values[a]; + sma_values[a] = sma_values[b]; + sma_values[b] = t; + } + + y[0][i] = sma_values[2]; + } + return 0; +} + diff --git a/lib/rainbow_oscillator.c b/lib/rainbow_oscillator.c new file mode 100644 index 0000000..1a56b55 --- /dev/null +++ b/lib/rainbow_oscillator.c @@ -0,0 +1,68 @@ +// Rainbow Oscillator +// +// rainbow_oscillator +// +// Rainbow Oscillator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_rainbow_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_rainbow_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 13 ); +} + +int ts_rainbow_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_rainbow_oscillator_p ); + (void)c; + uint_t const periods[] = { 2, 4, 6, 8, 10, 12, 14 }; + uint_t const num_periods = 7; + + for( uint_t j = 0; j < n && j < 13; ++j ) y[0][j] = 0; + for( uint_t i = 13; i < n; ++i ) + { + fp_t ma_values[7]; + + for( uint_t p = 0; p < num_periods; ++p ) + { + fp_t sum = 0; + for( uint_t j = 0; j < periods[p]; ++j ) + { + sum += x[0][i - j]; + } + ma_values[p] = sum / periods[p]; + } + + fp_t min_ma = ma_values[0]; + fp_t max_ma = ma_values[0]; + for( uint_t p = 1; p < num_periods; ++p ) + { + if( ma_values[p] < min_ma ) + min_ma = ma_values[p]; + if( ma_values[p] > max_ma ) + max_ma = ma_values[p]; + } + + if( max_ma == min_ma ) + y[0][i] = 0; + else + y[0][i] = ( x[0][i] - min_ma ) / ( max_ma - min_ma ) * 100.0; + } + return 0; +} + diff --git a/lib/random_walk_index.c b/lib/random_walk_index.c new file mode 100644 index 0000000..b4375e5 --- /dev/null +++ b/lib/random_walk_index.c @@ -0,0 +1,64 @@ +// Random Walk Index +// +// random_walk_index +// +// Random Walk Index +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_random_walk_index_params_t; + +ts_parameter_t const ts_random_walk_index_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_random_walk_index_params_t) }; + +uint_t ts_random_walk_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_random_walk_index_params_t *)c)->k : 0 ); +} + +int ts_random_walk_index( + 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 +) +{ + TS_PROLOGUE( ts_random_walk_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_random_walk_index_params_t *)c)->k; + if (k == 0 || n < k + 1) return -1; + + fp_t sum_abs = 0; + fp_t sum_net = 0; + + for ( uint_t i = 0; i < k; ++i ) + { + fp_t change = x[0][i + 1] - x[0][i]; + sum_abs += fabs( change ); + sum_net += change; + } + + y[0][k] = sum_net == 0 ? 0 : sum_abs / sum_net; + + for ( uint_t i = k + 1; i < n; ++i ) + { + fp_t change_new = x[0][i] - x[0][i - 1]; + fp_t change_old = x[0][i - k] - x[0][i - k - 1]; + + sum_abs = sum_abs - fabs( change_old ) + fabs( change_new ); + sum_net = sum_net - change_old + change_new; + + y[0][i] = sum_net == 0 ? 0 : sum_abs / sum_net; + } + return 0; +} + diff --git a/lib/rank.c b/lib/rank.c new file mode 100644 index 0000000..392b63f --- /dev/null +++ b/lib/rank.c @@ -0,0 +1,54 @@ +// Rank +// +// rank +// +// Percentile rank +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_rank_params_t; + +ts_parameter_t const ts_rank_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_rank_params_t) }; + +uint_t ts_rank_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_rank_params_t *)c)->k - 1 : 0 ); +} + +int ts_rank( + 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 +) +{ + TS_PROLOGUE( ts_rank_p ); + if ( !c ) return -1; + uint_t k = ((ts_rank_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + uint_t count = 0; + fp_t current = x[0][i]; + + for( uint_t j = i - k + 1; j <= i; ++j ) + { + if( x[0][j] <= current ) + count++; + } + + y[0][i] = (fp_t)count / (fp_t)k * 100.0; + } + return 0; +} + diff --git a/lib/ratio.c b/lib/ratio.c new file mode 100755 index 0000000..8568476 --- /dev/null +++ b/lib/ratio.c @@ -0,0 +1,49 @@ +// Ratio +// +// ratio - ratio +// +// Calculates the ratio between the current value and the value k samples ago +// +// y_i = x_i / x_i-k +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_ratio_params_t; + +ts_parameter_t const ts_ratio_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_ratio_params_t) }; + + +uint_t ts_ratio_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_ratio_params_t *)c)->k : 0 ); +} + +int ts_ratio( + 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 +) +{ + TS_PROLOGUE( ts_ratio_p ); + if ( !c ) return -1; + uint_t k = ((ts_ratio_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k; + + for( ; xi < n; xi++ ) + { + if( x[0][xi - k] == 0 ) { y[0][xi] = 0; } else { y[0][xi] = x[0][xi] / x[0][xi - k]; } + } + return 0; +} + diff --git a/lib/ravi.c b/lib/ravi.c new file mode 100644 index 0000000..fcb3095 --- /dev/null +++ b/lib/ravi.c @@ -0,0 +1,88 @@ +// Ravi +// +// ravi +// +// Rogers Average True Range +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t k2; +} ts_ravi_params_t; + +ts_parameter_t const ts_ravi_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_ravi_params_t) }; + +uint_t ts_ravi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_ravi_params_t *)c)->k + (uint_t)((ts_ravi_params_t *)c)->k2 - 2 : 0 ); +} + +int ts_ravi( + 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 +) +{ + TS_PROLOGUE( ts_ravi_p ); + if ( !c ) return -1; + uint_t k = ((ts_ravi_params_t *)c)->k; + fp_t k2 = ((ts_ravi_params_t *)c)->k2; + + if( k == 0 || k2 == 0 ) return -1; + + fp_t *num_buf = calloc( n, sizeof( fp_t ) ); + fp_t *den_buf = calloc( n, sizeof( fp_t ) ); + if( !num_buf || !den_buf ) { free( num_buf ); free( den_buf ); return -1; } + + for( uint_t i = 0; i < n; ++i ) + { + num_buf[i] = (x[3][i] - x[0][i]) + (x[3][i] - (i > 0 ? x[3][i - 1] : x[3][i])); + den_buf[i] = fabs(x[1][i] - x[2][i]) + fabs(x[3][i] - x[0][i]); + } + + fp_t num_sum = 0; + fp_t den_sum = 0; + + for( uint_t i = 0; i < k; ++i ) + { + num_sum += num_buf[i]; + den_sum += den_buf[i]; + } + + fp_t rvi = ( den_sum != 0 ) ? num_sum / den_sum : 0; + fp_t sig = rvi; + fp_t sig_a = 1.0 / k2; + uint_t offset = k + (uint_t)k2 - 2; + + for (uint_t i = 0; i < offset; ++i) { y[0][i] = 0; y[1][i] = 0; } + + for( uint_t i = k; i < n; ++i ) + { + num_sum = num_sum - num_buf[i - k] + num_buf[i]; + den_sum = den_sum - den_buf[i - k] + den_buf[i]; + rvi = ( den_sum != 0 ) ? num_sum / den_sum : 0; + sig = sig + sig_a * (rvi - sig); + + if( i >= offset ) + { + y[0][i] = rvi; + y[1][i] = sig; + } + } + + free( num_buf ); + free( den_buf ); + return 0; +} + diff --git a/lib/reciprocal.c b/lib/reciprocal.c new file mode 100644 index 0000000..7a42482 --- /dev/null +++ b/lib/reciprocal.c @@ -0,0 +1,39 @@ +// Reciprocal +// +// reciprocal - rec +// +// f( x ) = 1 / x +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_reciprocal_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_reciprocal_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_reciprocal( + 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 +) +{ + TS_PROLOGUE( ts_reciprocal_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = _ts_reciprocal( x[0][i] ); + } + return 0; +} + diff --git a/lib/regression_linear_least_squares.c b/lib/regression_linear_least_squares.c new file mode 100755 index 0000000..49123bb --- /dev/null +++ b/lib/regression_linear_least_squares.c @@ -0,0 +1,92 @@ +// Simple Least Squares Moving Linear Regression +// +// regression_linear_simple_least_squares - slrls +// +// ((k * sum_{i=0}^{n}[x_i * y_i]) - (sum_{i=0}^{n}[x_i] * sum_{i=0}^{n}[y_i])) +// β = ──────────────────────────────────────────────────────────────────────── +// ((k * sum_{i=0}^{n}[x_i^2]) - (sum_{i=0}^{n}[x_i^2])^2) +// +// α = E(y) - β * E(x) +// = μ_y - β * μ_x +// +// ( E( xy ) - E( μ_x ) * E( μ_y ) ) +// r_xy = ──────────────────────────────────────────────────────────────────────── +// ((E(x^2) - E(x)^2) - (E(y^2) - E(y)^2))^(1 / 2) +// +// y = α + βx +// +// Inputs: 2 +// Outputs: 3 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_regression_linear_least_squares_params_t; + +ts_parameter_t const ts_regression_linear_least_squares_p = { .n_inputs = 2, .n_outputs = 3, .n_constants = 1, .params_size = sizeof(ts_regression_linear_least_squares_params_t) }; + + +uint_t ts_regression_linear_least_squares_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_regression_linear_least_squares_params_t *)c)->k : 0 ); +} + +int ts_regression_linear_least_squares( + 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 +) +{ + TS_PROLOGUE( ts_regression_linear_least_squares_p ); + if ( !c ) return -1; + uint_t k = ((ts_regression_linear_least_squares_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k; + uint_t yi = k; + fp_t var_x, var_y, cov_xy; + + for( ; xi < n; ++xi, ++yi ) + { + // Two-pass variance/covariance for numerical stability + fp_t mean_x = 0, mean_y = 0; + for (uint_t j = xi - k + 1; j <= xi; j++) { + mean_x += x[0][j]; + mean_y += x[1][j]; + } + mean_x /= (fp_t)k; + mean_y /= (fp_t)k; + var_x = 0; var_y = 0; cov_xy = 0; + for (uint_t j = xi - k + 1; j <= xi; j++) { + fp_t dx = x[0][j] - mean_x; + fp_t dy = x[1][j] - mean_y; + var_x += dx * dx; + var_y += dy * dy; + cov_xy += dx * dy; + } + var_x /= (fp_t)k; + var_y /= (fp_t)k; + cov_xy /= (fp_t)k; + + if ( var_x != 0 ) + { + y[0][yi] = cov_xy / var_x; + y[1][yi] = mean_y - y[0][yi] * mean_x; + } + else + { + y[0][yi] = 0; + y[1][yi] = mean_y; + } + fp_t prod = var_x * var_y; + y[2][yi] = prod > 0 ? cov_xy / sqrt( prod ) : 0; + } + return 0; +} + diff --git a/lib/relative_strength.c b/lib/relative_strength.c new file mode 100644 index 0000000..634cd47 --- /dev/null +++ b/lib/relative_strength.c @@ -0,0 +1,41 @@ +// Relative Strength +// +// relative_strength +// +// Relative Strength +// +// +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_relative_strength_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_relative_strength_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_relative_strength( + 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 +) +{ + TS_PROLOGUE( ts_relative_strength_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[1][i] == 0.0 ? 0.0 : x[0][i] / x[1][i] * 100.0; + } + return 0; +} + diff --git a/lib/relative_volatility.c b/lib/relative_volatility.c new file mode 100644 index 0000000..9682a2d --- /dev/null +++ b/lib/relative_volatility.c @@ -0,0 +1,75 @@ +// Relative Volatility +// +// relative_volatility +// +// Relative Volatility Index +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_relative_volatility_params_t; + +ts_parameter_t const ts_relative_volatility_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_relative_volatility_params_t) }; + +uint_t ts_relative_volatility_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_relative_volatility_params_t *)c)->k - 1 : 0 ); +} + +int ts_relative_volatility( + 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 +) +{ + TS_PROLOGUE( ts_relative_volatility_p ); + if ( !c ) return -1; + uint_t k = ((ts_relative_volatility_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k - 1; + + fp_t sum = 0; + fp_t sum2 = 0; + + for( ; xi < k; ++xi ) + { + sum += x[0][xi]; + sum2 += _ts_square( x[0][xi] ); + } + + fp_t mean = sum / k; + fp_t mean2 = sum2 / k; + fp_t variance = mean2 - _ts_square( mean ); + fp_t sd = variance >= 0 ? sqrt( variance ) : 0; + + y[0][yi] = (fabs( mean ) > 0) ? sd / fabs( mean ) * 100 : 0; + + fp_t rk = _ts_reciprocal( k ); + + for( ; xi < n; ++xi, ++yi ) + { + sum = sum + ( x[0][xi] - x[0][xi - k] ); + sum2 = sum2 + ( _ts_square( x[0][xi] ) - _ts_square( x[0][xi - k] ) ); + + mean = sum * rk; + mean2 = sum2 * rk; + variance = mean2 - _ts_square( mean ); + sd = variance >= 0 ? sqrt( variance ) : 0; + + y[0][yi] = (fabs( mean ) > 0) ? sd / fabs( mean ) * 100 : 0; + } + return 0; +} + diff --git a/lib/relu.c b/lib/relu.c new file mode 100644 index 0000000..4e96edb --- /dev/null +++ b/lib/relu.c @@ -0,0 +1,40 @@ +// ReLU +// +// relu - Rectified Linear Unit +// +// y[t] = max( 0, x[t] ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_relu_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_relu_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_relu( + 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 +) +{ + TS_PROLOGUE( ts_relu_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi > 0 ? xi : 0; + } + return 0; +} + diff --git a/lib/remainder.c b/lib/remainder.c new file mode 100644 index 0000000..c3a6df7 --- /dev/null +++ b/lib/remainder.c @@ -0,0 +1,41 @@ +// Remainder +// +// remainder +// +// IEEE remainder +// +// +// +// Inputs: 2 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_remainder_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_remainder_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_remainder( + 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 +) +{ + TS_PROLOGUE( ts_remainder_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if( x[1][i] == 0.0 ) { y[0][i] = 0; continue; } + y[0][i] = remainder( x[0][i], x[1][i] ); + } + return 0; +} + diff --git a/lib/remquo_dummy.c b/lib/remquo_dummy.c new file mode 100644 index 0000000..3f9bd48 --- /dev/null +++ b/lib/remquo_dummy.c @@ -0,0 +1,40 @@ +// Remquo Dummy +// +// remquo_dummy +// +// Remainder with quotient +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_remquo_dummy_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_remquo_dummy_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_remquo_dummy( + 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 +) +{ + TS_PROLOGUE( ts_remquo_dummy_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = remainder( x[0][i], 1.0 ); + } + return 0; +} + diff --git a/lib/reversal.c b/lib/reversal.c new file mode 100644 index 0000000..ede74fe --- /dev/null +++ b/lib/reversal.c @@ -0,0 +1,52 @@ +// Reversal +// +// reversal +// +// Reversal indicator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_reversal_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_reversal_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 2 ); +} + +int ts_reversal( + 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 +) +{ + TS_PROLOGUE( ts_reversal_p ); + (void)c; + if (n > 0) y[0][0] = 0; + if (n > 1) y[0][1] = 0; + for( uint_t i = 2; i < n; ++i ) + { + if( x[0][i] < x[0][i - 1] && x[0][i - 1] > x[0][i - 2] ) + { + y[0][i] = -1; + } + else if( x[0][i] > x[0][i - 1] && x[0][i - 1] < x[0][i - 2] ) + { + y[0][i] = 1; + } + else + { + y[0][i] = 0; + } + } + return 0; +} + diff --git a/lib/rint.c b/lib/rint.c new file mode 100644 index 0000000..fb744fb --- /dev/null +++ b/lib/rint.c @@ -0,0 +1,40 @@ +// Rint +// +// rint +// +// Round to integer +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_rint_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_rint_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_rint( + 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 +) +{ + TS_PROLOGUE( ts_rint_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = rint( x[0][i] ); + } + return 0; +} + diff --git a/lib/roc.c b/lib/roc.c new file mode 100644 index 0000000..3b45ecd --- /dev/null +++ b/lib/roc.c @@ -0,0 +1,47 @@ +// Roc +// +// roc +// +// Rate of Change +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_roc_params_t; + +ts_parameter_t const ts_roc_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_roc_params_t) }; + +uint_t ts_roc_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_roc_params_t *)c)->k : 0 ); +} + +int ts_roc( + 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 +) +{ + TS_PROLOGUE( ts_roc_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_roc_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = ( x[0][i] - x[0][i - k] ) / x[0][i - k] * 100; + } + return 0; +} + diff --git a/lib/rocp.c b/lib/rocp.c new file mode 100644 index 0000000..6575d85 --- /dev/null +++ b/lib/rocp.c @@ -0,0 +1,46 @@ +// Rocp +// +// rocp +// +// Rate of Change Percentage +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_rocp_params_t; + +ts_parameter_t const ts_rocp_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_rocp_params_t) }; + +uint_t ts_rocp_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_rocp_params_t *)c)->k : 0 ); +} + +int ts_rocp( + 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 +) +{ + TS_PROLOGUE( ts_rocp_p ); + if ( !c ) return -1; + uint_t k = ((ts_rocp_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = ( x[0][i] - x[0][i - k] ) / x[0][i - k]; + } + return 0; +} + diff --git a/lib/rocr.c b/lib/rocr.c new file mode 100644 index 0000000..d11cb91 --- /dev/null +++ b/lib/rocr.c @@ -0,0 +1,46 @@ +// Rocr +// +// rocr +// +// Rate of Change Ratio +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_rocr_params_t; + +ts_parameter_t const ts_rocr_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_rocr_params_t) }; + +uint_t ts_rocr_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_rocr_params_t *)c)->k : 0 ); +} + +int ts_rocr( + 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 +) +{ + TS_PROLOGUE( ts_rocr_p ); + if ( !c ) return -1; + uint_t k = ((ts_rocr_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = x[0][i] / x[0][i - k]; + } + return 0; +} + diff --git a/lib/rocr100.c b/lib/rocr100.c new file mode 100644 index 0000000..b90413f --- /dev/null +++ b/lib/rocr100.c @@ -0,0 +1,45 @@ +// Rocr100 +// +// rocr100 +// +// Rate of Change Ratio * 100 +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_rocr100_params_t; + +ts_parameter_t const ts_rocr100_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_rocr100_params_t) }; + +uint_t ts_rocr100_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_rocr100_params_t *)c)->k : 0 ); +} + +int ts_rocr100( + 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 +) +{ + TS_PROLOGUE( ts_rocr100_p ); + if ( !c ) return -1; + uint_t k = ((ts_rocr100_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = x[0][i] / x[0][i - k] * 100; + } + return 0; +} + diff --git a/lib/root.c b/lib/root.c new file mode 100755 index 0000000..a5e7dca --- /dev/null +++ b/lib/root.c @@ -0,0 +1,48 @@ +// Nth Root +// +// n_root - nrt +// +// f( x ) = x^( 1 / n ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 1 +// +// c[0] = n + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t r; +} ts_root_params_t; + +ts_parameter_t const ts_root_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_root_params_t) }; + + +uint_t ts_root_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_root( + 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 +) +{ + TS_PROLOGUE( ts_root_p ); + if ( !c ) return -1; + fp_t r = ((ts_root_params_t *)c)->r; + if (r == 0) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = pow( x[0][i], _ts_reciprocal( r ) ); + } + return 0; +} + diff --git a/lib/root_cube.c b/lib/root_cube.c new file mode 100644 index 0000000..1cd79d5 --- /dev/null +++ b/lib/root_cube.c @@ -0,0 +1,41 @@ +// Cube Root +// +// cube_root - cbrt +// +// f( x ) = ∛x +// = x^( 1 / 3 ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_root_cube_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_root_cube_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_root_cube( + 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 +) +{ + TS_PROLOGUE( ts_root_cube_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = cbrt( x[0][i] ); + } + return 0; +} + diff --git a/lib/root_square.c b/lib/root_square.c new file mode 100644 index 0000000..3e8ef8e --- /dev/null +++ b/lib/root_square.c @@ -0,0 +1,43 @@ +// Square Root +// +// square_root - sqrt +// +// f( x ) = √x +// = x^( 1 / 2 ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 +// +// + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_root_square_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_root_square_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_root_square( + 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 +) +{ + TS_PROLOGUE( ts_root_square_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] >= 0 ? sqrt( x[0][i] ) : 0; + } + return 0; +} + diff --git a/lib/round_.c b/lib/round_.c new file mode 100644 index 0000000..f061b87 --- /dev/null +++ b/lib/round_.c @@ -0,0 +1,40 @@ +// Round +// +// round - round +// +// f( x ) = ⌊ x + 0.5 ⌋ +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_round__p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_round__offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_round_( + 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 +) +{ + TS_PROLOGUE( ts_round__p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = round( x[0][i] ); + } + return 0; +} + diff --git a/lib/rsi.c b/lib/rsi.c new file mode 100644 index 0000000..bf21c5e --- /dev/null +++ b/lib/rsi.c @@ -0,0 +1,91 @@ +// Rsi +// +// rsi +// +// Relative Strength Index +// +// RSI = 100 - (100 / (1 + RS)), where RS = avg_gain / avg_loss +// Uses Wilder's smoothing for avg_gain and avg_loss. +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_rsi_params_t; + +ts_parameter_t const ts_rsi_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_rsi_params_t) }; + +uint_t ts_rsi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_rsi_params_t *)c)->k : 0 ); +} + +int ts_rsi( + 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 +) +{ + TS_PROLOGUE( ts_rsi_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_rsi_params_t *)c)->k; + if (k == 0 || n <= k) return -1; + + for (uint_t i = 0; i < k; ++i) y[0][i] = NAN; + + fp_t avg_gain = 0; + fp_t avg_loss = 0; + + for( uint_t i = 1; i <= k; ++i ) + { + fp_t change = x[0][i] - x[0][i - 1]; + if( change > 0 ) + avg_gain += change; + else if( change < 0 ) + avg_loss += (-change); + } + + avg_gain /= k; + avg_loss /= k; + + { + if( avg_loss < TS_FILL_ZERO_THRESHOLD ) + y[0][k] = 100; + else + { + fp_t rs = avg_gain / avg_loss; + y[0][k] = 100 - ( 100 / ( 1 + rs ) ); + } + } + + for( uint_t i = k + 1; i < n; ++i ) + { + fp_t change = x[0][i] - x[0][i - 1]; + fp_t gain = change > 0 ? change : 0; + fp_t loss = change < 0 ? (-change) : 0; + + avg_gain = ( avg_gain * ( k - 1 ) + gain ) / k; + avg_loss = ( avg_loss * ( k - 1 ) + loss ) / k; + + if( avg_loss < TS_FILL_ZERO_THRESHOLD ) + y[0][i] = 100; + else + { + fp_t rs = avg_gain / avg_loss; + y[0][i] = 100 - ( 100 / ( 1 + rs ) ); + } + } + + return 0; +} + + diff --git a/lib/running_count.c b/lib/running_count.c new file mode 100644 index 0000000..fb8d3ad --- /dev/null +++ b/lib/running_count.c @@ -0,0 +1,43 @@ +// Running Count +// +// running_count +// +// Running count of non-zero values +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_running_count_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_running_count_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_running_count( + 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 +) +{ + TS_PROLOGUE( ts_running_count_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + if( i == 0 ) + y[0][i] = ( x[0][i] != 0 ) ? 1 : 0; + else + y[0][i] = y[0][i - 1] + ( ( x[0][i] != 0 ) ? 1 : 0 ); + } + + return 0; +} + diff --git a/lib/schaff_trend_cycle.c b/lib/schaff_trend_cycle.c new file mode 100644 index 0000000..f4584e6 --- /dev/null +++ b/lib/schaff_trend_cycle.c @@ -0,0 +1,151 @@ +// Schaff Trend Cycle +// +// schaff_trend_cycle +// +// Schaff Trend Cycle +// +// +// +// Inputs: 1 +// Outputs: 2 +// Constants: 1 + + +#include <stdlib.h> +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t period; +} ts_schaff_trend_cycle_params_t; + +ts_parameter_t const ts_schaff_trend_cycle_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_schaff_trend_cycle_params_t) }; + +uint_t ts_schaff_trend_cycle_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 52 ); +} + +int ts_schaff_trend_cycle( + 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 +) +{ + TS_PROLOGUE( ts_schaff_trend_cycle_p ); + if ( !c ) return -1; + uint_t period = ((ts_schaff_trend_cycle_params_t *)c)->period; + if (period == 0) period = 3; + if (n == 0) return -1; + if (period > n) { for (uint_t j = 0; j < n; ++j) { y[0][j] = 0; y[1][j] = 0; } return 0; } + + uint_t fast = 10; + uint_t slow = 21; + uint_t rsi_period = 13; + + fp_t ema_fast = x[0][0]; + fp_t ema_slow = x[0][0]; + fp_t a_fast = 2.0 / (fast + 1); + fp_t a_slow = 2.0 / (slow + 1); + + fp_t *macd_line = calloc(n, sizeof(fp_t)); + if (!macd_line) return -1; + + for( uint_t i = 1; i < n; ++i ) + { + ema_fast = ema_fast + a_fast * (x[0][i] - ema_fast); + ema_slow = ema_slow + a_slow * (x[0][i] - ema_slow); + macd_line[i] = ema_fast - ema_slow; + } + + fp_t *rsi1 = calloc(n, sizeof(fp_t)); + fp_t *rsi2 = calloc(n, sizeof(fp_t)); + if (!rsi1 || !rsi2) { free(macd_line); free(rsi1); free(rsi2); return -1; } + + { + fp_t avg_up = 0, avg_down = 0; + for( uint_t i = 1; i <= rsi_period; ++i ) + { + fp_t change = macd_line[i] - macd_line[i - 1]; + if( change > 0 ) avg_up += change; + else avg_down += (-change); + } + avg_up /= rsi_period; + avg_down /= rsi_period; + rsi1[rsi_period] = (avg_down == 0) ? 100 : 100 - 100 / (1 + avg_up / avg_down); + + for( uint_t i = rsi_period + 1; i < n; ++i ) + { + fp_t change = macd_line[i] - macd_line[i - 1]; + fp_t gain = change > 0 ? change : 0; + fp_t loss = change < 0 ? (-change) : 0; + avg_up = (avg_up * (rsi_period - 1) + gain) / rsi_period; + avg_down = (avg_down * (rsi_period - 1) + loss) / rsi_period; + rsi1[i] = (avg_down == 0) ? 100 : 100 - 100 / (1 + avg_up / avg_down); + } + } + + { + fp_t avg_up = 0, avg_down = 0; + for( uint_t i = rsi_period + 1; i <= rsi_period * 2; ++i ) + { + fp_t change = rsi1[i] - rsi1[i - 1]; + if( change > 0 ) avg_up += change; + else avg_down += (-change); + } + avg_up /= rsi_period; + avg_down /= rsi_period; + rsi2[rsi_period * 2] = (avg_down == 0) ? 100 : 100 - 100 / (1 + avg_up / avg_down); + + for( uint_t i = rsi_period * 2 + 1; i < n; ++i ) + { + fp_t change = rsi1[i] - rsi1[i - 1]; + fp_t gain = change > 0 ? change : 0; + fp_t loss = change < 0 ? (-change) : 0; + avg_up = (avg_up * (rsi_period - 1) + gain) / rsi_period; + avg_down = (avg_down * (rsi_period - 1) + loss) / rsi_period; + rsi2[i] = (avg_down == 0) ? 100 : 100 - 100 / (1 + avg_up / avg_down); + } + } + + uint_t offset = slow + rsi_period * 2; + fp_t sig_a = 1.0 / (fp_t)period; + fp_t signal_ema = 0; + uint_t active = 0; + uint_t count = 0; + + for (uint_t j = 0; j < n && j < offset; ++j) { y[0][j] = 0; y[1][j] = 0; } + for( uint_t i = offset; i < n; ++i ) + { + fp_t hist = macd_line[i] - macd_line[i - 1]; + fp_t hist_prev = (i > 1) ? macd_line[i - 1] - macd_line[i - 2] : 0; + int prev_above = hist_prev > 0.5; + int curr_above = hist > 0.5; + fp_t stc = 0; + + if( !prev_above && curr_above ) + { + active = 1; + count = 0; + } + if( active ) + { + stc = 100; + count++; + if( count >= period ) + active = 0; + } + + y[0][i] = stc; + + signal_ema = signal_ema + sig_a * (stc - signal_ema); + y[1][i] = signal_ema; + } + + free(macd_line); + free(rsi1); + free(rsi2); + return 0; +} + diff --git a/lib/secant.c b/lib/secant.c new file mode 100644 index 0000000..128e5a7 --- /dev/null +++ b/lib/secant.c @@ -0,0 +1,41 @@ +// Secant +// +// secant - sec +// +// f( x ) = sec( x ) = 1 / cos( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_secant_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_secant_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_secant( + 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 +) +{ + TS_PROLOGUE( ts_secant_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t cv = cos( x[0][i] ); + y[0][i] = ( fabs( cv ) > 1e-15 ) ? _ts_reciprocal( cv ) : ( cv > 0 ? (fp_t)INFINITY : (fp_t)-INFINITY ); + } + return 0; +} + diff --git a/lib/secant_hyperbolic.c b/lib/secant_hyperbolic.c new file mode 100644 index 0000000..6956840 --- /dev/null +++ b/lib/secant_hyperbolic.c @@ -0,0 +1,42 @@ +// Hyperbolic Secant +// +// secant_hyperbolic - sech +// +// f( x ) = sech( x ) +// = 1 / cosh( x ) +// = 2 / ( e^x + e^( -x ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_secant_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_secant_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_secant_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_secant_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = _ts_reciprocal( cosh( x[0][i] ) ); + } + return 0; +} + diff --git a/lib/secant_hyperbolic_inverse.c b/lib/secant_hyperbolic_inverse.c new file mode 100644 index 0000000..d530d71 --- /dev/null +++ b/lib/secant_hyperbolic_inverse.c @@ -0,0 +1,42 @@ +// Inverse Hyperbolic Secant +// +// secant_hyperbolic_inverse - asech +// +// f( x ) = asech( x ) +// = ln( ( 1 / x ) + √( ( 1 / x^2 ) - 1 ) ) 0 < x ≤ 1 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_secant_hyperbolic_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_secant_hyperbolic_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_secant_hyperbolic_inverse( + 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 +) +{ + TS_PROLOGUE( ts_secant_hyperbolic_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = ( xi > 0 && xi <= 1.0 ) ? acosh( _ts_reciprocal( xi ) ) : 0; + } + return 0; +} + diff --git a/lib/secant_inverse.c b/lib/secant_inverse.c new file mode 100644 index 0000000..fbbab80 --- /dev/null +++ b/lib/secant_inverse.c @@ -0,0 +1,42 @@ +// Inverse Secant +// +// secant_inverse - asec +// +// f( x ) = asec( x ) +// = acos( 1 / x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_secant_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_secant_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_secant_inverse( + 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 +) +{ + TS_PROLOGUE( ts_secant_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t r = _ts_reciprocal( x[0][i] ); + y[0][i] = ( r >= -1.0 && r <= 1.0 ) ? acos( r ) : 0; + } + return 0; +} + diff --git a/lib/session_volume.c b/lib/session_volume.c new file mode 100644 index 0000000..be9810f --- /dev/null +++ b/lib/session_volume.c @@ -0,0 +1,43 @@ +// Session Volume +// +// session_volume +// +// Session volume +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_session_volume_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_session_volume_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_session_volume( + 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 +) +{ + TS_PROLOGUE( ts_session_volume_p ); + (void)c; + fp_t cum = 0; + + for( uint_t i = 0; i < n; ++i ) + { + cum += x[0][i]; + y[0][i] = cum; + } + + return 0; +} + diff --git a/lib/shinohara_intensity_ratio.c b/lib/shinohara_intensity_ratio.c new file mode 100644 index 0000000..fcdd364 --- /dev/null +++ b/lib/shinohara_intensity_ratio.c @@ -0,0 +1,85 @@ +// Shinohara Intensity Ratio +// +// shinohara_intensity_ratio +// +// Shinohara Intensity Ratio +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <stdlib.h> +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_shinohara_intensity_ratio_params_t; + +ts_parameter_t const ts_shinohara_intensity_ratio_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_shinohara_intensity_ratio_params_t) }; + +uint_t ts_shinohara_intensity_ratio_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_shinohara_intensity_ratio_params_t *)c)->k > 0 ? ((ts_shinohara_intensity_ratio_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_shinohara_intensity_ratio( + 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 +) +{ + TS_PROLOGUE( ts_shinohara_intensity_ratio_p ); + if ( !c ) return -1; + uint_t k = ((ts_shinohara_intensity_ratio_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t *intensity = calloc( n, sizeof( fp_t ) ); + if ( !intensity ) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + fp_t h = ts_h(x, i); + fp_t l = ts_l(x, i); + fp_t cl = ts_c(x, i); + fp_t o = ts_o(x, i); + fp_t range = h - l; + + if( range == 0 ) + { + intensity[i] = 0; + } + else if( cl > o ) + { + intensity[i] = (h - cl) / range; + } + else + { + intensity[i] = (cl - l) / range; + } + } + + for (uint_t j = 0; j < n && j < k - 1; ++j) y[0][j] = 0; + for( uint_t i = k - 1; i < n; ++i ) + { + if( i == k - 1 ) + { + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + sum += intensity[j]; + y[0][i] = sum / k; + } + else + { + y[0][i] = y[0][i-1] + (intensity[i] - intensity[i - k]) / k; + } + } + + free( intensity ); + return 0; +} + diff --git a/lib/sigmoid.c b/lib/sigmoid.c new file mode 100644 index 0000000..05ea126 --- /dev/null +++ b/lib/sigmoid.c @@ -0,0 +1,63 @@ +// Sigmoid +// +// sigmoid - sig +// +// f(x) = ( L / ( 1 + e^( -( 4m * ( x - x_0 ) / L ) ) ) ) + b - ( L / 2 ) +// +// m = f'(0) +// b = f(0) +// ( b - ( L / 2 ) ) < y < ( b + ( L / 2 ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 3 +// +// c[0] = m +// c[1] = L +// c[2] = b + + +#include <math.h> +#include "ts.h" + + +typedef struct { + fp_t m; + fp_t L; + fp_t b; +} ts_sigmoid_params_t; + +ts_parameter_t const ts_sigmoid_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 3, .params_size = sizeof(ts_sigmoid_params_t) }; + + +uint_t ts_sigmoid_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sigmoid( + 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 +) +{ + TS_PROLOGUE( ts_sigmoid_p ); + if ( !c ) return -1; + fp_t m = ((ts_sigmoid_params_t *)c)->m; + fp_t L = ((ts_sigmoid_params_t *)c)->L; + fp_t b = ((ts_sigmoid_params_t *)c)->b; + + m = -( 4.0 * m ) / L; + b = b - ( 0.5 * L ); + + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = m * x[0][i]; + if( xv > 500.0 ) { y[0][i] = b; continue; } + if( xv < -500.0 ) { y[0][i] = L + b; continue; } + y[0][i] = ( L / ( 1.0 + exp( xv ) ) ) + b; + } + return 0; +} + diff --git a/lib/sign.c b/lib/sign.c new file mode 100644 index 0000000..d6b1aaf --- /dev/null +++ b/lib/sign.c @@ -0,0 +1,41 @@ +// Sign +// +// sign - sgn +// +// ⎧ -1 if x < 0 +// f( x ) = ⎨ 0 if x = 0 +// ⎩ 1 if x > 0 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_sign_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sign_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sign( + 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 +) +{ + TS_PROLOGUE( ts_sign_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ( x[0][i] > 0 ) - ( x[0][i] < 0 ); + } + return 0; +} + diff --git a/lib/sinc.c b/lib/sinc.c new file mode 100644 index 0000000..779c8da --- /dev/null +++ b/lib/sinc.c @@ -0,0 +1,47 @@ +// Sinc +// +// sinc - Normalized Sinc function +// +// y[t] = x[t] == 0 ? 1 : sin( pi * x[t] ) / ( pi * x[t] ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sinc_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sinc_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sinc( + 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 +) +{ + TS_PROLOGUE( ts_sinc_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi == 0 ) + y[0][i] = 1; + else + { + fp_t pix = PI * xi; + y[0][i] = sin( pix ) / pix; + } + } + return 0; +} + diff --git a/lib/sinc_unnormalized.c b/lib/sinc_unnormalized.c new file mode 100644 index 0000000..c68f9fa --- /dev/null +++ b/lib/sinc_unnormalized.c @@ -0,0 +1,44 @@ +// Sinc Unnormalized +// +// sinc_unnormalized - Unnormalized Sinc function +// +// y[t] = x[t] == 0 ? 1 : sin( x[t] ) / x[t] +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sinc_unnormalized_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sinc_unnormalized_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sinc_unnormalized( + 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 +) +{ + TS_PROLOGUE( ts_sinc_unnormalized_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi == 0 ) + y[0][i] = 1; + else + y[0][i] = sin( xi ) / xi; + } + return 0; +} + diff --git a/lib/sine.c b/lib/sine.c new file mode 100644 index 0000000..262d4f8 --- /dev/null +++ b/lib/sine.c @@ -0,0 +1,40 @@ +// Sine +// +// sine - sin +// +// f( x ) = sin( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sine_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sine_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sine( + 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 +) +{ + TS_PROLOGUE( ts_sine_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = sin( x[0][i] ); + } + return 0; +} + diff --git a/lib/sine_hyperbolic.c b/lib/sine_hyperbolic.c new file mode 100644 index 0000000..5662f19 --- /dev/null +++ b/lib/sine_hyperbolic.c @@ -0,0 +1,41 @@ +// Hyperbolic Sine +// +// sine_hyperbolic - sinh +// +// f( x ) = sinh( x ) +// = 0.5 * ( e^x - e^( -x ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sine_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sine_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sine_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_sine_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = sinh( x[0][i] ); + } + return 0; +} + diff --git a/lib/sine_hyperbolic_inverse.c b/lib/sine_hyperbolic_inverse.c new file mode 100644 index 0000000..57c1be5 --- /dev/null +++ b/lib/sine_hyperbolic_inverse.c @@ -0,0 +1,41 @@ +// Inverse Hyperbolic Sine +// +// sine_hyperbolic_inverse - asinh +// +// f( x ) = asinh( x ) +// = ln( x + √( x^2 + 1 ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sine_hyperbolic_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sine_hyperbolic_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sine_hyperbolic_inverse( + 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 +) +{ + TS_PROLOGUE( ts_sine_hyperbolic_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = asinh( x[0][i] ); + } + return 0; +} + diff --git a/lib/sine_inverse.c b/lib/sine_inverse.c new file mode 100644 index 0000000..5d96513 --- /dev/null +++ b/lib/sine_inverse.c @@ -0,0 +1,41 @@ +// Inverse Sine +// +// sine_inverse - asin +// +// f( x ) = asin( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_sine_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_sine_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_sine_inverse( + 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 +) +{ + TS_PROLOGUE( ts_sine_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t v = x[0][i]; + y[0][i] = ( v >= -1.0 && v <= 1.0 ) ? asin( v ) : 0; + } + return 0; +} + diff --git a/lib/slope.c b/lib/slope.c new file mode 100755 index 0000000..ca4fd22 --- /dev/null +++ b/lib/slope.c @@ -0,0 +1,51 @@ +// Slope +// +// slope - slope +// +// Calculates the slope between the current value and the value k samples ago +// +// y_i = ( x_i - x_i-k ) / k +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_slope_params_t; + +ts_parameter_t const ts_slope_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_slope_params_t) }; + + +uint_t ts_slope_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_slope_params_t *)c)->k : 0 ); +} + +int ts_slope( + 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 +) +{ + TS_PROLOGUE( ts_slope_p ); + if ( !c ) return -1; + uint_t k = ((ts_slope_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = k; + + fp_t rk = _ts_reciprocal( k ); + + for( ; xi < n; xi++ ) + { + y[0][xi] = ( x[0][xi] - x[0][xi - k] ) * rk; + } + return 0; +} + diff --git a/lib/smootherstep.c b/lib/smootherstep.c new file mode 100644 index 0000000..3053bcf --- /dev/null +++ b/lib/smootherstep.c @@ -0,0 +1,49 @@ +// Smootherstep +// +// smootherstep - Smootherstep interpolation +// +// y[t] = x[t] <= 0 ? 0 : x[t] >= 1 ? 1 : 6*x[t]^5 - 15*x[t]^4 + 10*x[t]^3 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_smootherstep_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_smootherstep_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_smootherstep( + 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 +) +{ + TS_PROLOGUE( ts_smootherstep_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi <= 0 ) + y[0][i] = 0; + else if ( xi >= 1 ) + y[0][i] = 1; + else + { + fp_t x2 = xi * xi; + fp_t x3 = x2 * xi; + y[0][i] = 6 * x3 * x2 - 15 * x2 * x2 + 10 * x3; + } + } + return 0; +} + diff --git a/lib/smoothstep.c b/lib/smoothstep.c new file mode 100644 index 0000000..224e68b --- /dev/null +++ b/lib/smoothstep.c @@ -0,0 +1,48 @@ +// Smoothstep +// +// smoothstep - Smoothstep interpolation +// +// y[t] = x[t] <= 0 ? 0 : x[t] >= 1 ? 1 : 3*x[t]^2 - 2*x[t]^3 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_smoothstep_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_smoothstep_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_smoothstep( + 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 +) +{ + TS_PROLOGUE( ts_smoothstep_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + if ( xi <= 0 ) + y[0][i] = 0; + else if ( xi >= 1 ) + y[0][i] = 1; + else + { + fp_t x2 = xi * xi; + y[0][i] = 3 * x2 - 2 * x2 * xi; + } + } + return 0; +} + diff --git a/lib/softplus.c b/lib/softplus.c new file mode 100644 index 0000000..b61f96b --- /dev/null +++ b/lib/softplus.c @@ -0,0 +1,41 @@ +// Softplus +// +// softplus - Softplus activation +// +// y[t] = log( 1 + exp( x[t] ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_softplus_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_softplus_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_softplus( + 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 +) +{ + TS_PROLOGUE( ts_softplus_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi > 20 ? xi : log( 1 + exp( xi ) ); + } + return 0; +} + diff --git a/lib/softsign.c b/lib/softsign.c new file mode 100644 index 0000000..978f275 --- /dev/null +++ b/lib/softsign.c @@ -0,0 +1,41 @@ +// Softsign +// +// softsign - Softsign activation +// +// y[t] = x[t] / ( 1 + |x[t]| ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_softsign_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_softsign_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_softsign( + 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 +) +{ + TS_PROLOGUE( ts_softsign_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi / ( 1 + fabs( xi ) ); + } + return 0; +} + diff --git a/lib/standard_error.c b/lib/standard_error.c new file mode 100644 index 0000000..9b0ac3b --- /dev/null +++ b/lib/standard_error.c @@ -0,0 +1,67 @@ +// Standard Error +// +// standard_error +// +// Standard Error of the Mean +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_standard_error_params_t; + +ts_parameter_t const ts_standard_error_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_standard_error_params_t) }; + +uint_t ts_standard_error_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_standard_error_params_t *)c)->k > 0 ? ((ts_standard_error_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_standard_error( + 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 +) +{ + TS_PROLOGUE( ts_standard_error_p ); + if ( !c ) return -1; + uint_t k = ((ts_standard_error_params_t *)c)->k; + if (k < 2) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0, sum_y2 = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t xv = (fp_t)j; + fp_t yv = x[0][i - k + 1 + j]; + sum_x += xv; + sum_y += yv; + sum_xy += xv * yv; + sum_x2 += xv * xv; + sum_y2 += yv * yv; + } + fp_t nk = (fp_t)k; + fp_t denom = nk * sum_x2 - sum_x * sum_x; + if( denom == 0 ) + { + y[0][i] = 0; + } + else + { + fp_t ss_res = sum_y2 - (sum_y * sum_y) / nk - ((nk * sum_xy - sum_x * sum_y) * (nk * sum_xy - sum_x * sum_y)) / (nk * denom); + y[0][i] = ss_res > 0 ? sqrt( ss_res / (nk - 2) ) : 0; + } + } + return 0; +} + diff --git a/lib/starc_bands.c b/lib/starc_bands.c new file mode 100644 index 0000000..0c545f8 --- /dev/null +++ b/lib/starc_bands.c @@ -0,0 +1,70 @@ +// Starc Bands +// +// starc_bands +// +// STARC Bands +// +// +// +// Inputs: 4 +// Outputs: 3 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + + +typedef struct { + uint_t k; + fp_t mult; +} ts_starc_bands_params_t; + +ts_parameter_t const ts_starc_bands_p = { .n_inputs = 4, .n_outputs = 3, .n_constants = 2, .params_size = sizeof(ts_starc_bands_params_t) }; + + +uint_t ts_starc_bands_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_starc_bands_params_t *)c)->k : 0 ); +} + +int ts_starc_bands( + 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 +) +{ + TS_PROLOGUE( ts_starc_bands_p ); + if ( !c ) return -1; + uint_t k = ((ts_starc_bands_params_t *)c)->k; + if (k == 0) return -1; + if (n < k + 1) return -1; + + fp_t mult = ((ts_starc_bands_params_t *)c)->mult; + + for( uint_t i = k; i < n; ++i ) + { + fp_t tr_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + fp_t tr = fmax( ts_h( x, idx ) - ts_l( x, idx ), fmax( fabs( ts_h( x, idx ) - ts_c( x, idx - 1 ) ), fabs( ts_l( x, idx ) - ts_c( x, idx - 1 ) ) ) ); + tr_sum += tr; + } + fp_t atr = tr_sum / (fp_t)k; + + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + sum += ts_c( x, i - k + 1 + j ); + } + fp_t mean = sum / k; + + y[0][i] = mean + mult * atr; + y[1][i] = mean; + y[2][i] = mean - mult * atr; + } + return 0; +} + diff --git a/lib/starc_lower.c b/lib/starc_lower.c new file mode 100644 index 0000000..97ac02f --- /dev/null +++ b/lib/starc_lower.c @@ -0,0 +1,74 @@ +// Starc Lower +// +// starc_lower +// +// STARC Lower Band +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t mult; +} ts_starc_lower_params_t; + +ts_parameter_t const ts_starc_lower_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_starc_lower_params_t) }; + +uint_t ts_starc_lower_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_starc_lower_params_t *)c)->k > 0 ? ((ts_starc_lower_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_starc_lower( + 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 +) +{ + TS_PROLOGUE( ts_starc_lower_p ); + if ( !c ) return -1; + uint_t k = ((ts_starc_lower_params_t *)c)->k; + if( k == 0 ) return -1; + fp_t mult = ((ts_starc_lower_params_t *)c)->mult; + + uint_t half_k = k / 2; + if (n <= half_k) return -1; + + uint_t last = k - 1; + for( uint_t i = k - 1; i < n - half_k; ++i ) + { + fp_t middle_sum = 0; + fp_t atr_sum = 0; + + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - half_k + j; + middle_sum += ts_c( x, idx ); + + fp_t tr = ts_h( x, idx ) - ts_l( x, idx ); + if( idx > 0 ) + { + fp_t h_pc = fabs( ts_h( x, idx ) - ts_c( x, idx - 1 ) ); + fp_t l_pc = fabs( ts_l( x, idx ) - ts_c( x, idx - 1 ) ); + if( h_pc > tr ) tr = h_pc; + if( l_pc > tr ) tr = l_pc; + } + atr_sum += tr; + } + + y[0][i] = middle_sum / k - mult * atr_sum / k; + last = i; + } + for( uint_t i = last + 1; i < n; ++i ) + y[0][i] = y[0][last]; + return 0; +} + diff --git a/lib/starc_upper.c b/lib/starc_upper.c new file mode 100644 index 0000000..bcccb95 --- /dev/null +++ b/lib/starc_upper.c @@ -0,0 +1,64 @@ +// Starc Upper +// +// starc_upper +// +// STARC Upper Band +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t mult; +} ts_starc_upper_params_t; + +ts_parameter_t const ts_starc_upper_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_starc_upper_params_t) }; + +uint_t ts_starc_upper_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_starc_upper_params_t *)c)->k : 0 ); +} + +int ts_starc_upper( + 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 +) +{ + TS_PROLOGUE( ts_starc_upper_p ); + if ( !c ) return -1; + uint_t k = ((ts_starc_upper_params_t *)c)->k; + fp_t mult = ((ts_starc_upper_params_t *)c)->mult; + if ( k == 0 || k >= n ) return -1; + + for ( uint_t i = k; i < n; ++i ) + { + fp_t tr_sum = 0; + for ( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + fp_t tr = fmax( ts_h( x, idx ) - ts_l( x, idx ), fmax( fabs( ts_h( x, idx ) - ts_c( x, idx - 1 ) ), fabs( ts_l( x, idx ) - ts_c( x, idx - 1 ) ) ) ); + tr_sum += tr; + } + fp_t atr = tr_sum / (fp_t)k; + + fp_t sum = 0; + for ( uint_t j = 0; j < k; ++j ) + { + sum += ts_c( x, i - k + 1 + j ); + } + fp_t mean = sum / k; + + y[0][i] = mean + mult * atr; + } + return 0; +} + diff --git a/lib/std_error.c b/lib/std_error.c new file mode 100644 index 0000000..f55d34e --- /dev/null +++ b/lib/std_error.c @@ -0,0 +1,57 @@ +// Std Error +// +// std_error +// +// Standard Error +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_std_error_params_t; + +ts_parameter_t const ts_std_error_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_std_error_params_t) }; + +uint_t ts_std_error_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_std_error_params_t *)c)->k > 0 ? ((ts_std_error_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_std_error( + 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 +) +{ + TS_PROLOGUE( ts_std_error_p ); + if ( !c ) return -1; + uint_t k = ((ts_std_error_params_t *)c)->k; + if ( k == 0 ) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t sum = 0; + for( uint_t j = 0; j < k; ++j ) + sum += x[0][i - k + 1 + j]; + + fp_t mean = sum / k; + + fp_t sum_sq = 0; + for( uint_t j = 0; j < k; ++j ) + sum_sq += _ts_square( x[0][i - k + 1 + j] ); + + fp_t variance = sum_sq / k - _ts_square( mean ); + y[0][i] = variance >= 0 ? sqrt( variance ) / sqrt( (fp_t)k ) : 0; + } + return 0; +} + diff --git a/lib/stoch_rsi.c b/lib/stoch_rsi.c new file mode 100644 index 0000000..81992a6 --- /dev/null +++ b/lib/stoch_rsi.c @@ -0,0 +1,96 @@ +// Stoch Rsi +// +// stoch_rsi +// +// Stochastic RSI +// +// +// +// Inputs: 1 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t rsi_period; + uint_t stoch_period; +} ts_stoch_rsi_params_t; + +ts_parameter_t const ts_stoch_rsi_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_stoch_rsi_params_t) }; + +uint_t ts_stoch_rsi_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_stoch_rsi_params_t *)c)->rsi_period + ((ts_stoch_rsi_params_t *)c)->stoch_period - 1 : 0 ); +} + +int ts_stoch_rsi( + 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 +) +{ + TS_PROLOGUE( ts_stoch_rsi_p ); + if ( !c ) return -1; + uint_t rsi_period = ((ts_stoch_rsi_params_t *)c)->rsi_period; + uint_t stoch_period = ((ts_stoch_rsi_params_t *)c)->stoch_period; + if (rsi_period == 0 || stoch_period == 0) return -1; + + fp_t *rsi_values = NULL; + if( n > 0 ) + { + rsi_values = calloc( n, sizeof( fp_t ) ); + if( !rsi_values ) return -1; + } + + for( uint_t i = rsi_period; i < n; ++i ) + { + fp_t sum_gain = 0; + fp_t sum_loss = 0; + + for( uint_t j = 0; j < rsi_period; ++j ) + { + fp_t change = x[0][i - rsi_period + 1 + j] - x[0][i - rsi_period + j]; + if( change > 0 ) + sum_gain += change; + else if( change < 0 ) + sum_loss += (-change); + } + + if( sum_loss == 0 ) + rsi_values[i] = 100.0; + else + { + fp_t rs = sum_gain / sum_loss; + rsi_values[i] = 100.0 - ( 100.0 / ( 1.0 + rs ) ); + } + } + + for (uint_t j = 0; j < n && j < rsi_period + stoch_period - 1; ++j) { y[0][j] = 0; y[1][j] = 0; } + for( uint_t i = rsi_period + stoch_period - 1; i < n; ++i ) + { + fp_t rsi_min = rsi_values[i - stoch_period + 1]; + fp_t rsi_max = rsi_values[i - stoch_period + 1]; + + for( uint_t j = 1; j < stoch_period; ++j ) + { + rsi_min = fmin( rsi_min, rsi_values[i - stoch_period + 1 + j] ); + rsi_max = fmax( rsi_max, rsi_values[i - stoch_period + 1 + j] ); + } + + fp_t denom = rsi_max - rsi_min; + fp_t stoch_k = denom != 0 ? ( rsi_values[i] - rsi_min ) / denom : 0; + fp_t stoch_d = stoch_k; + + y[0][i] = stoch_k; + y[1][i] = stoch_d; + } + + free( rsi_values ); + return 0; +} + diff --git a/lib/stochastic_fast.c b/lib/stochastic_fast.c new file mode 100644 index 0000000..eb203c8 --- /dev/null +++ b/lib/stochastic_fast.c @@ -0,0 +1,94 @@ +// Stochastic Fast +// +// stochastic_fast +// +// Stochastic Fast %K +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_stochastic_fast_params_t; + +ts_parameter_t const ts_stochastic_fast_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_stochastic_fast_params_t) }; + +uint_t ts_stochastic_fast_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_stochastic_fast_params_t *)c)->k > 0 ? 2 * ((ts_stochastic_fast_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_stochastic_fast( + 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 +) +{ + TS_PROLOGUE( ts_stochastic_fast_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_stochastic_fast_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + // Initial window max/min + fp_t hh = ts_h( x, 0 ); + fp_t ll = ts_l( x, 0 ); + for( uint_t j = 1; j < k; ++j ) + { + if( ts_h( x, j ) > hh ) + hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) + ll = ts_l( x, j ); + } + + { + fp_t k_val = ( hh - ll ) == 0 ? 0 : 100.0 * ( ts_c( x, k - 1 ) - ll ) / ( hh - ll ); + y[0][k - 1] = k_val; + } + + for( uint_t i = k; i < n; ++i ) + { + fp_t new_h = ts_h( x, i ); + fp_t new_l = ts_l( x, i ); + fp_t old_h = ts_h( x, i - k ); + fp_t old_l = ts_l( x, i - k ); + + if ( new_h > hh ) hh = new_h; + if ( new_l < ll ) ll = new_l; + if ( old_h == hh || old_l == ll ) + { + hh = ts_h( x, i - k + 1 ); + ll = ts_l( x, i - k + 1 ); + for( uint_t j = i - k + 2; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + } + + fp_t k_val = ( hh - ll ) == 0 ? 0 : 100.0 * ( ts_c( x, i ) - ll ) / ( hh - ll ); + y[0][i] = k_val; + + if (i >= k + 1) + { + fp_t d_sum = 0; + for( uint_t j = 0; j < 3; ++j ) + d_sum += y[0][i - j]; + y[1][i] = d_sum / 3.0; + } + else + { + y[1][i] = 0; + } + } + + return 0; +} + diff --git a/lib/stochastic_momentum_index.c b/lib/stochastic_momentum_index.c new file mode 100644 index 0000000..5491c30 --- /dev/null +++ b/lib/stochastic_momentum_index.c @@ -0,0 +1,130 @@ +// Stochastic Momentum Index +// +// stochastic_momentum_index +// +// Stochastic Momentum Index +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 3 + + +#include <stdlib.h> +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k1; + uint_t k2; + uint_t signal_period; +} ts_stochastic_momentum_index_params_t; + +ts_parameter_t const ts_stochastic_momentum_index_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 3, .params_size = sizeof(ts_stochastic_momentum_index_params_t) }; + +uint_t ts_stochastic_momentum_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_stochastic_momentum_index_params_t *)c)->k1 + ((ts_stochastic_momentum_index_params_t *)c)->k2 : 0 ); +} + +int ts_stochastic_momentum_index( + 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 +) +{ + TS_PROLOGUE( ts_stochastic_momentum_index_p ); + if ( !c ) return -1; + uint_t k1 = ((ts_stochastic_momentum_index_params_t *)c)->k1; + uint_t k2 = ((ts_stochastic_momentum_index_params_t *)c)->k2; + uint_t signal_period = ((ts_stochastic_momentum_index_params_t *)c)->signal_period; + if (k1 == 0 || k2 == 0) return -1; + + fp_t *midpoint = calloc(n, sizeof(fp_t)); + fp_t *deviation = calloc(n, sizeof(fp_t)); + if (!midpoint || !deviation) { free(midpoint); free(deviation); return -1; } + + for( uint_t i = 0; i < n; ++i ) + { + midpoint[i] = (ts_h(x, i) + ts_l(x, i)) / 2.0; + deviation[i] = ts_c(x, i) - midpoint[i]; + } + + fp_t *sma_dev = calloc(n, sizeof(fp_t)); + fp_t *sma_high = calloc(n, sizeof(fp_t)); + if (!sma_dev || !sma_high) { free(midpoint); free(deviation); free(sma_dev); free(sma_high); return -1; } + + uint_t start = k1 - 1; + + for( uint_t i = start; i < n; ++i ) + { + fp_t sum_dev = 0; + fp_t sum_high = 0; + + for( uint_t j = 0; j < k1; ++j ) + { + sum_dev += deviation[i - j]; + fp_t abs_dev = fabs(deviation[i - j]); + if( abs_dev > sum_high ) sum_high = abs_dev; + } + + sma_dev[i] = sum_dev / k1; + sma_high[i] = sum_high; + } + + uint_t start2 = start + k2 - 1; + + fp_t *smi = calloc(n, sizeof(fp_t)); + if (!smi) { free(midpoint); free(deviation); free(sma_dev); free(sma_high); free(smi); return -1; } + + for( uint_t i = start2; i < n; ++i ) + { + fp_t sum_sma_dev = 0; + fp_t sum_sma_high = 0; + + for( uint_t j = 0; j < k2; ++j ) + { + sum_sma_dev += sma_dev[i - j]; + sum_sma_high += sma_high[i - j]; + } + + sum_sma_dev /= k2; + sum_sma_high /= k2; + + if( sum_sma_high == 0 ) + smi[i] = 0; + else + smi[i] = (sum_sma_dev / sum_sma_high) * 100; + } + + fp_t sig_a = 2.0 / (signal_period + 1); + fp_t signal_ema = 0; + uint_t signal_init = 0; + + for (uint_t j = 0; j < n && j < start2; ++j) { y[0][j] = 0; y[1][j] = 0; } + for( uint_t i = start2; i < n; ++i ) + { + y[0][i] = smi[i]; + + if( !signal_init ) + { + signal_ema = smi[i]; + signal_init = 1; + } + else + { + signal_ema = signal_ema + sig_a * (smi[i] - signal_ema); + } + y[1][i] = signal_ema; + } + + free(midpoint); + free(deviation); + free(sma_dev); + free(sma_high); + free(smi); + return 0; +} + diff --git a/lib/stochastic_slow.c b/lib/stochastic_slow.c new file mode 100644 index 0000000..0f84e1a --- /dev/null +++ b/lib/stochastic_slow.c @@ -0,0 +1,91 @@ +// Stochastic Slow +// +// stochastic_slow +// +// Stochastic Slow %K, %D +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_stochastic_slow_params_t; + +ts_parameter_t const ts_stochastic_slow_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_stochastic_slow_params_t) }; + +uint_t ts_stochastic_slow_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_stochastic_slow_params_t *)c)->k > 0 ? 3 * ((ts_stochastic_slow_params_t *)c)->k - 3 : 0 : 0 ); +} + +int ts_stochastic_slow( + 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 +) +{ + TS_PROLOGUE( ts_stochastic_slow_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_stochastic_slow_params_t *)c)->k; + if (k == 0) return -1; + + fp_t *fast_k = calloc( k, sizeof( fp_t ) ); + if( !fast_k ) return -1; + + for( uint_t i = 0; i < n; ++i ) + { + if( i < k - 1 ) + { + fast_k[i % k] = 0; + continue; + } + + fp_t hh = ts_h( x, i ); + fp_t ll = ts_l( x, i ); + + for( uint_t j = 1; j < k; ++j ) + { + if( ts_h( x, i - j ) > hh ) + hh = ts_h( x, i - j ); + if( ts_l( x, i - j ) < ll ) + ll = ts_l( x, i - j ); + } + + fast_k[i % k] = ( hh - ll ) == 0 ? 0 : 100.0 * ( ts_c( x, i ) - ll ) / ( hh - ll ); + } + + for (uint_t j = 0; j < n && j < 3 * k - 3; ++j) { y[0][j] = 0; y[1][j] = 0; } + for( uint_t i = 3 * k - 3; i < n; ++i ) + { + fp_t slow_k_sum = 0; + for( uint_t j = 0; j < k; ++j ) + slow_k_sum += fast_k[(i - j) % k]; + fp_t slow_k = slow_k_sum / k; + + fp_t slow_d_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + fp_t sk_sum = 0; + for( uint_t jj = 0; jj < k; ++jj ) + sk_sum += fast_k[(i - j - jj) % k]; + slow_d_sum += sk_sum / k; + } + fp_t slow_d = slow_d_sum / k; + + y[0][i] = slow_k; + y[1][i] = slow_d; + } + + free( fast_k ); + return 0; +} + diff --git a/lib/student_t_pdf.c b/lib/student_t_pdf.c new file mode 100644 index 0000000..67c9f1d --- /dev/null +++ b/lib/student_t_pdf.c @@ -0,0 +1,51 @@ +// Student T Pdf +// +// student_t_pdf +// +// Student's t probability density +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t nu; +} ts_student_t_pdf_params_t; + +ts_parameter_t const ts_student_t_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_student_t_pdf_params_t) }; + +uint_t ts_student_t_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_student_t_pdf( + 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 +) +{ + TS_PROLOGUE( ts_student_t_pdf_p ); + if ( !c ) return -1; + fp_t nu = ((ts_student_t_pdf_params_t *)c)->nu; + if ( nu <= 0 ) + { + for( uint_t i = 0; i < n; ++i ) y[0][i] = 0; + return -1; + } + fp_t coeff = tgamma( ( nu + 1.0 ) / 2.0 ) / ( sqrt( nu * PI ) * tgamma( nu / 2.0 ) ); + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = coeff * pow( 1.0 + xi * xi / nu, -( nu + 1.0 ) / 2.0 ); + } + return 0; +} + diff --git a/lib/subtraction.c b/lib/subtraction.c new file mode 100644 index 0000000..01fd208 --- /dev/null +++ b/lib/subtraction.c @@ -0,0 +1,39 @@ +// Subtraction +// +// subtraction - sub +// +// f( x_0, x_1 ) = x_0 - x_1 +// +// X_n - 2 +// Y_n - 1 +// C_n - 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_subtraction_p = { .n_inputs = 2, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_subtraction_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_subtraction( + 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 +) +{ + TS_PROLOGUE( ts_subtraction_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = x[0][i] - x[1][i]; + } + return 0; +} + diff --git a/lib/summation.c b/lib/summation.c new file mode 100755 index 0000000..6d0020b --- /dev/null +++ b/lib/summation.c @@ -0,0 +1,57 @@ +// Summation +// +// summation - sum +// +// Calculates the sum of the past k values +// +// y_i = sum_{j=i-k+1}^{i}[x_j] +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_summation_params_t; + +ts_parameter_t const ts_summation_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_summation_params_t) }; + + +uint_t ts_summation_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_summation_params_t *)c)->k > 0 ? ((ts_summation_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_summation( + 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 +) +{ + TS_PROLOGUE( ts_summation_p ); + if ( !c ) return -1; + uint_t k = ((ts_summation_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + y[0][k - 1] = 0; + + for( ; xi < k; xi++ ) + { + y[0][k - 1] += x[0][xi]; + } + + for( ; xi < n; xi++, yi++ ) + { + y[0][yi] = x[0][xi] + y[0][yi - 1] - x[0][xi - k]; + } + return 0; +} + diff --git a/lib/supertrend.c b/lib/supertrend.c new file mode 100644 index 0000000..4487e96 --- /dev/null +++ b/lib/supertrend.c @@ -0,0 +1,118 @@ +// Supertrend +// +// supertrend +// +// SuperTrend Indicator +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t mult; +} ts_supertrend_params_t; + +ts_parameter_t const ts_supertrend_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_supertrend_params_t) }; + +uint_t ts_supertrend_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_supertrend_params_t *)c)->k : 0 ); +} + +int ts_supertrend( + 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 +) +{ + TS_PROLOGUE( ts_supertrend_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_supertrend_params_t *)c)->k; + fp_t mult = ((ts_supertrend_params_t *)c)->mult; + if (k == 0 || n <= k) return -1; + + fp_t tr = ts_h( x, 1 ) - ts_l( x, 1 ); + fp_t tr2 = fabs( ts_h( x, 1 ) - ts_c( x, 0 ) ); + fp_t tr3 = fabs( ts_l( x, 1 ) - ts_c( x, 0 ) ); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + fp_t atr = tr; + + for( uint_t i = 2; i <= k; ++i ) + { + tr = ts_h( x, i ) - ts_l( x, i ); + tr2 = fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ); + tr3 = fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + atr = atr + (tr - atr) / k; + } + + fp_t median = ( ts_h( x, k ) + ts_l( x, k ) ) / 2.0; + fp_t raw_upper = median + mult * atr; + fp_t raw_lower = median - mult * atr; + + fp_t final_upper = raw_upper; + fp_t final_lower = raw_lower; + int_t trend = 1; + + if( ts_c( x, k ) > raw_lower ) + { + trend = 1; + } + else + { + trend = -1; + } + + y[0][k] = trend == 1 ? final_lower : final_upper; + y[1][k] = trend; + + for( uint_t i = k + 1; i < n; ++i ) + { + tr = ts_h( x, i ) - ts_l( x, i ); + tr2 = fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ); + tr3 = fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + atr = atr + (tr - atr) / k; + + median = ( ts_h( x, i ) + ts_l( x, i ) ) / 2.0; + raw_upper = median + mult * atr; + raw_lower = median - mult * atr; + + if( raw_upper < final_upper || ts_c( x, i - 1 ) > final_upper ) + final_upper = raw_upper; + + if( raw_lower > final_lower || ts_c( x, i - 1 ) < final_lower ) + final_lower = raw_lower; + + int_t new_trend = trend; + + if( trend == 1 ) + { + if( ts_c( x, i ) < final_lower ) + new_trend = -1; + } + else + { + if( ts_c( x, i ) > final_upper ) + new_trend = 1; + } + + trend = new_trend; + y[0][i] = trend == 1 ? final_lower : final_upper; + y[1][i] = trend; + } + return 0; +} + diff --git a/lib/swing_index.c b/lib/swing_index.c new file mode 100644 index 0000000..2f7c563 --- /dev/null +++ b/lib/swing_index.c @@ -0,0 +1,52 @@ +// Swing Index +// +// swing_index +// +// Swing Index +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_swing_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_swing_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_swing_index( + 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 +) +{ + TS_PROLOGUE( ts_swing_index_p ); + (void)c; + if (n > 0) y[0][0] = 0; + for( uint_t i = 1; i < n; ++i ) + { + fp_t a1 = ts_h( x, i ) - ts_h( x, i - 1 ); + fp_t a2 = ts_l( x, i - 1 ) - ts_l( x, i ); + fp_t a = fmax( a1, a2 ); + fp_t b = ts_c( x, i ) - ts_c( x, i - 1 ); + fp_t ldpl = ts_l( x, i ) - ts_l( x, i - 1 ); + fp_t phmh = ts_h( x, i - 1 ) - ts_h( x, i ); + fp_t c_val = 0; + if( b < 0 && ldpl > phmh ) c_val = ldpl; + else if( b >= 0 && phmh > ldpl ) c_val = phmh; + fp_t hl = ts_h( x, i ) - ts_l( x, i ); + fp_t swing = ( hl != 0 ) ? ( a * 2.0 + b - c_val ) * ts_v( x, i ) / hl : 0; + y[0][i] = ( i > 1 ? y[0][i - 1] : 0 ) + swing; + } + return 0; +} + diff --git a/lib/swish.c b/lib/swish.c new file mode 100644 index 0000000..7d09787 --- /dev/null +++ b/lib/swish.c @@ -0,0 +1,41 @@ +// Swish +// +// swish - Swish (Silu) activation +// +// y[t] = x[t] / ( 1 + exp( -x[t] ) ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_swish_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_swish_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_swish( + 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 +) +{ + TS_PROLOGUE( ts_swish_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi / ( 1 + exp( -xi ) ); + } + return 0; +} + diff --git a/lib/synthetic_yield.c b/lib/synthetic_yield.c new file mode 100644 index 0000000..c2c8d62 --- /dev/null +++ b/lib/synthetic_yield.c @@ -0,0 +1,47 @@ +// Synthetic Yield +// +// synthetic_yield +// +// Synthetic Yield +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_synthetic_yield_params_t; + +ts_parameter_t const ts_synthetic_yield_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_synthetic_yield_params_t) }; + +uint_t ts_synthetic_yield_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_synthetic_yield_params_t *)c)->k : 0 ); +} + +int ts_synthetic_yield( + 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 +) +{ + TS_PROLOGUE( ts_synthetic_yield_p ); + if ( !c ) return -1; + uint_t k = ((ts_synthetic_yield_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + if( x[0][i - k] == 0 ) { y[0][i] = 0; continue; } + y[0][i] = ( x[0][i] - x[0][i - k] ) / x[0][i - k] * 252.0 / k * 100.0; + } + return 0; +} + diff --git a/lib/t3.c b/lib/t3.c new file mode 100644 index 0000000..4689d54 --- /dev/null +++ b/lib/t3.c @@ -0,0 +1,66 @@ +// T3 +// +// t3 +// +// T3 Moving Average +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + +ts_parameter_t const ts_t3_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_t3_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_t3( + 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 +) +{ + TS_PROLOGUE( ts_t3_p ); + (void)c; + if (n == 0) return -1; + + uint_t k = 5; + + fp_t b = 0.7; + + fp_t alpha = 2.0 / ( (fp_t)k + 1.0 ); + + fp_t c3 = b; + fp_t c3m1 = c3 - 1.0; + fp_t c3m2 = c3 - 2.0; + fp_t c3m3 = c3 - 3.0; + + fp_t w3 = c3; + fp_t w2 = -3.0 * c3m1; + fp_t w1 = 3.0 * c3m1 * c3m2; + fp_t w0 = -c3m1 * c3m2 * c3m3; + + fp_t ema1 = x[0][0]; + fp_t ema2 = x[0][0]; + fp_t ema3 = x[0][0]; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + ema1 = ema1 + alpha * ( x[0][i] - ema1 ); + ema2 = ema2 + alpha * ( ema1 - ema2 ); + ema3 = ema3 + alpha * ( ema2 - ema3 ); + + y[0][i] = w3 * ema3 + w2 * ema2 + w1 * ema1 + w0 * x[0][i]; + } + return 0; +} + diff --git a/lib/tangent.c b/lib/tangent.c new file mode 100644 index 0000000..407ea6b --- /dev/null +++ b/lib/tangent.c @@ -0,0 +1,40 @@ +// Tangent +// +// tangent - tan +// +// f( x ) = tan( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_tangent_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_tangent_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tangent( + 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 +) +{ + TS_PROLOGUE( ts_tangent_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = tan( x[0][i] ); + } + return 0; +} + diff --git a/lib/tangent_hyperbolic.c b/lib/tangent_hyperbolic.c new file mode 100644 index 0000000..7a76c82 --- /dev/null +++ b/lib/tangent_hyperbolic.c @@ -0,0 +1,41 @@ +// Hyperbolic Tangent +// +// tangent_hyperbolic - tanh +// +// f( x ) = tanh( x ) +// = sinh( x ) / cosh( x ) +// = ( e^2x - 1 ) / ( e^2x + 1 ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_tangent_hyperbolic_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_tangent_hyperbolic_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tangent_hyperbolic( + 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 +) +{ + TS_PROLOGUE( ts_tangent_hyperbolic_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = tanh( x[0][i] ); + } + return 0; +} + diff --git a/lib/tangent_hyperbolic_inverse.c b/lib/tangent_hyperbolic_inverse.c new file mode 100644 index 0000000..1c337ba --- /dev/null +++ b/lib/tangent_hyperbolic_inverse.c @@ -0,0 +1,43 @@ +// Inverse Hyperbolic Tangent +// +// tangent_hyperbolic_inverse - atanh +// +// f( x ) = atanh( x ) +// = 0.5 * ln( ( 1 + x ) / ( 1 - x ) ) |x| < 1 +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_tangent_hyperbolic_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_tangent_hyperbolic_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tangent_hyperbolic_inverse( + 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 +) +{ + TS_PROLOGUE( ts_tangent_hyperbolic_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( fabs(xv) >= 1.0 ) { y[0][i] = 0; continue; } + y[0][i] = atanh( xv ); + } + return 0; +} + diff --git a/lib/tangent_inverse.c b/lib/tangent_inverse.c new file mode 100644 index 0000000..69b67b7 --- /dev/null +++ b/lib/tangent_inverse.c @@ -0,0 +1,40 @@ +// Inverse Tangent +// +// tangent_inverse - atan +// +// f( x ) = atan( x ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_tangent_inverse_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_tangent_inverse_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tangent_inverse( + 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 +) +{ + TS_PROLOGUE( ts_tangent_inverse_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = atan( x[0][i] ); + } + return 0; +} + diff --git a/lib/tanhshrink.c b/lib/tanhshrink.c new file mode 100644 index 0000000..c48ab85 --- /dev/null +++ b/lib/tanhshrink.c @@ -0,0 +1,41 @@ +// Tanhshrink +// +// tanhshrink - Tanhshrink activation +// +// y[t] = x[t] - tanh( x[t] ) +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_tanhshrink_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_tanhshrink_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tanhshrink( + 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 +) +{ + TS_PROLOGUE( ts_tanhshrink_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi - tanh( xi ); + } + return 0; +} + diff --git a/lib/test.c b/lib/test.c new file mode 100755 index 0000000..56c47eb --- /dev/null +++ b/lib/test.c @@ -0,0 +1,77 @@ +#include <assert.h> +#include <math.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include "ts.h" + + +void _ts_assert_approximate( fp_t y0, fp_t y1, fp_t e ) +{ + assert( ( fabs( y0 - y1 ) <= e ) ); +} + + +bool _ts_is_approximate( fp_t y0, fp_t y1, fp_t e ) +{ + return ( fabs( y0 - y1 ) <= e ); +} + + +#define _ts_test_print( FUNCTION ) \ + \ + fputs( #FUNCTION, stdout ); \ + fputs( ": ", stdout ) + + +void ts_test_all( void ) +{ + ts_test_absolute_value(); + ts_test_addition(); + ts_test_ceiling(); + +} + +bool ts_test( + void ( *ts_function )( + fp_t * const *y, + fp_t const * const *x, + 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 ) +{ + uint_t offset = ts_offset( c ); + uint_t y_n = n - offset; + bool result = true; + + fp_t *y[m->y]; + + for( uint_t i = 0; i < m->y; i++ ) + { + fp_t *tmp = malloc( y_n * sizeof( fp_t ) ); + y[i] = tmp; + } + + ts_function( y, x, n, c ); + + for( uint_t i = 0; i < m->y; ++i ) + { + for( uint_t j = 0; j < y_n; ++j ) + { + // _ts_assert_approximate( y[i][j], e[i][j], TS_TEST_EPSILON ); + result &= _ts_is_approximate( y[i][j], e[i][j], TS_TEST_EPSILON ); + } + + free( y[i] ); + } + + return result; +}
\ No newline at end of file diff --git a/lib/tgamma.c b/lib/tgamma.c new file mode 100644 index 0000000..967072e --- /dev/null +++ b/lib/tgamma.c @@ -0,0 +1,42 @@ +// Tgamma +// +// tgamma +// +// Gamma function +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_tgamma_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_tgamma_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tgamma( + 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 +) +{ + TS_PROLOGUE( ts_tgamma_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xv = x[0][i]; + if( xv <= 0.0 ) { y[0][i] = 0; continue; } + y[0][i] = tgamma( xv ); + } + return 0; +} + diff --git a/lib/tick_volume.c b/lib/tick_volume.c new file mode 100644 index 0000000..9e28ec7 --- /dev/null +++ b/lib/tick_volume.c @@ -0,0 +1,41 @@ +// Tick Volume +// +// tick_volume +// +// Tick Volume +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_tick_volume_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_tick_volume_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_tick_volume( + 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 +) +{ + TS_PROLOGUE( ts_tick_volume_p ); + (void)c; + ( void ) x; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = 1; + } + + return 0; +} + diff --git a/lib/time_series_forecast.c b/lib/time_series_forecast.c new file mode 100644 index 0000000..bf8e7af --- /dev/null +++ b/lib/time_series_forecast.c @@ -0,0 +1,58 @@ +// Time Series Forecast +// +// time_series_forecast +// +// Time Series Forecast +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_time_series_forecast_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_time_series_forecast_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 4 ); +} + +int ts_time_series_forecast( + 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 +) +{ + TS_PROLOGUE( ts_time_series_forecast_p ); + (void)c; + for ( uint_t j = 0; j < n && j < 4; ++j ) y[0][j] = 0; + for ( uint_t i = 4; i < n; ++i ) + { + fp_t sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0; + for ( uint_t j = 0; j < 5; ++j ) + { + fp_t xv = (fp_t)j; + fp_t yv = x[0][i - 4 + j]; + sum_x += xv; + sum_y += yv; + sum_xy += xv * yv; + sum_x2 += xv * xv; + } + fp_t denom = 5.0 * sum_x2 - sum_x * sum_x; + if (denom == 0) { + y[0][i] = sum_y / 5.0; + } else { + fp_t slope = (5.0 * sum_xy - sum_x * sum_y) / denom; + fp_t intercept = (sum_y - slope * sum_x) / 5.0; + y[0][i] = intercept + slope * 5.0; + } + } + return 0; +} + diff --git a/lib/trade_volume_index.c b/lib/trade_volume_index.c new file mode 100644 index 0000000..2e1b50d --- /dev/null +++ b/lib/trade_volume_index.c @@ -0,0 +1,43 @@ +// Trade Volume Index +// +// trade_volume_index +// +// Trade Volume Index +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_trade_volume_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_trade_volume_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_trade_volume_index( + 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 + ) + { + TS_PROLOGUE( ts_trade_volume_index_p ); + (void)c; + if (n < 2) return -1; + for( uint_t i = 0; i + 1 < n; ++i ) + { + fp_t roc = x[3][i] != 0 ? ( x[3][i + 1] - x[3][i] ) / x[3][i] : 0; + y[0][i] = ( i == 0 ? 0 : y[0][i - 1] ) + roc * x[4][i + 1]; + } + y[0][n - 1] = y[0][n - 2]; + return 0; + } + diff --git a/lib/trend_intensity_index.c b/lib/trend_intensity_index.c new file mode 100644 index 0000000..451e440 --- /dev/null +++ b/lib/trend_intensity_index.c @@ -0,0 +1,62 @@ +// Trend Intensity Index +// +// trend_intensity_index +// +// Trend Intensity Index +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_trend_intensity_index_params_t; + +ts_parameter_t const ts_trend_intensity_index_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_trend_intensity_index_params_t) }; + +uint_t ts_trend_intensity_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_trend_intensity_index_params_t *)c)->k > 0 ? ((ts_trend_intensity_index_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_trend_intensity_index( + 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 +) +{ + TS_PROLOGUE( ts_trend_intensity_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_trend_intensity_index_params_t *)c)->k; + if ( k == 0 || n < k ) return -1; + + fp_t signed_sum = 0; + fp_t vol_sum = 0; + + for ( uint_t i = 1; i <= k; ++i ) + { + signed_sum += ( ts_c( x, i ) > ts_c( x, i - 1 ) ) ? ts_v( x, i ) : -ts_v( x, i ); + vol_sum += ts_v( x, i ); + } + + y[0][k - 1] = vol_sum != 0 ? signed_sum / vol_sum : 0; + + for ( uint_t i = k + 1; i < n; ++i ) + { + fp_t cur_signed = ( ts_c( x, i ) > ts_c( x, i - 1 ) ) ? ts_v( x, i ) : -ts_v( x, i ); + fp_t old_signed = ( ts_c( x, i - k ) > ts_c( x, i - k - 1 ) ) ? ts_v( x, i - k ) : -ts_v( x, i - k ); + + signed_sum = signed_sum - old_signed + cur_signed; + vol_sum = vol_sum - ts_v( x, i - k ) + ts_v( x, i ); + + y[0][i] = vol_sum != 0 ? signed_sum / vol_sum : 0; + } + return 0; +} + diff --git a/lib/trix.c b/lib/trix.c new file mode 100755 index 0000000..a9aac88 --- /dev/null +++ b/lib/trix.c @@ -0,0 +1,61 @@ +// Trix +// +// trix - trix +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + + +typedef struct { + fp_t a; +} ts_trix_params_t; + +ts_parameter_t const ts_trix_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_trix_params_t) }; + + +uint_t ts_trix_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_trix( + 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 +) +{ + TS_PROLOGUE( ts_trix_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t a = ((ts_trix_params_t *)c)->a; + if (a == 0) return -1; + + fp_t ema1 = x[0][0]; + fp_t ema2 = x[0][0]; + fp_t ema30 = x[0][0]; + fp_t ema31; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + ema1 = ema1 + a * ( x[0][i] - ema1 ); + ema2 = ema2 + a * ( ema1 - ema2 ); + ema31 = ema30 + a * ( ema2 - ema30 ); + + if (ema30 == 0) + y[0][i] = 0; + else + y[0][i] = ( ema31 - ema30 ) / ema30; + + ema30 = ema31; + } + return 0; +} + diff --git a/lib/true_range.c b/lib/true_range.c new file mode 100644 index 0000000..ff1740e --- /dev/null +++ b/lib/true_range.c @@ -0,0 +1,45 @@ +// True Range +// +// true_range +// +// True Range +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_true_range_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(uint_t) }; + + +uint_t ts_true_range_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 1 ); +} + +int ts_true_range( + 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 +) +{ + TS_PROLOGUE( ts_true_range_p ); + (void)c; + if (n > 0) y[0][0] = 0; + for( uint_t i = 1; i < n; ++i ) + { + y[0][i] = fmax( ( ts_h( x, i ) - ts_l( x, i ) ), + fmax( fabs( ts_h( x, i ) - ts_c( x, i - 1 ) ), + fabs( ts_l( x, i ) - ts_c( x, i - 1 ) ) ) ); + } + return 0; +} + diff --git a/lib/truncate.c b/lib/truncate.c new file mode 100644 index 0000000..0b83feb --- /dev/null +++ b/lib/truncate.c @@ -0,0 +1,40 @@ +// Truncate +// +// truncate - trunc +// +// f( x ) = [ x ] +// +// X_n - 1 +// Y_n - 1 +// C_n - 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_truncate_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_truncate_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_truncate( + 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 +) +{ + TS_PROLOGUE( ts_truncate_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = trunc( x[0][i] ); + } + return 0; +} + diff --git a/lib/ts.c b/lib/ts.c new file mode 100755 index 0000000..68e2f41 --- /dev/null +++ b/lib/ts.c @@ -0,0 +1,119 @@ +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include "ts.h" + + +fp_t * ts_fill( fp_t *x, uint_t n, fp_t x0, fp_t x1 ) +{ + fp_t step = ( x1 - x0 ) / ( n - 1 ); + + if( x0 == 0.0 && x1 == 0.0 ) + { + memset(x, 0, sizeof( fp_t ) * n ); + } + + else + { + for( uint_t i = 0; i < n; i++ ) + { + x[i] = x0 + ( i * step ); + } + } + + return x; +} + + +void ts_print( fp_t *x, uint_t n ) +{ + fputc('[', stdout); + + uint_t i; + for( i = 0; i < n - 1; i++ ) + { + fprintf(stdout, "%f,", x[i]); + } + + fprintf(stdout, "%f]", x[i]); +} + + +void ts_pretty_print( fp_t const * const *x, uint_t m, uint_t n ) +{ + if( m > 1 ) + { + for( uint_t i = 0; i < n; i++ ) + { + uint_t j = 0; + for( ; j < m; j++ ) + { + fprintf( stdout, "%f ", x[i][j] ); + } + + fputs( "\n", stdout ); + } + } + else + { + for( uint_t i = 0; i < n; i++ ) + { + fprintf( stdout, "%f\n", x[0][i] ); + } + } +} + + +fp_t _ts_square( fp_t x ) +{ + return x * x; +} + + +fp_t _ts_reciprocal( fp_t x ) +{ + return x != 0 ? 1.0 / x : INFINITY; +} + + +fp_t _ts_square_pyramidal_number( fp_t x ) +{ + return ( x * ( x + 1 ) * ( 2 * x + 1 ) ) / 6; +} + + +fp_t _ts_triangle_number( fp_t x ) +{ + return ( ( x ) * ( ( x ) + 1 ) ) * 0.5; +} + + +fp_t _ts_sum_to_covariance( fp_t sum_xy, fp_t sum_x, fp_t sum_y, uint_t k ) +{ + return ( sum_xy - ( sum_x * sum_y / k ) ) / k; +} + + +fp_t _ts_sum_to_variance( fp_t sum_x2, fp_t sum_x, uint_t k ) +{ + return _ts_sum_to_covariance( sum_x2, sum_x, sum_x, k ); +} + + +uint_t _ts_circle_increment( uint_t i, uint_t k ) +{ + return ( i + 1 ) % k; +} + + +// ts_constant_t * ts_constant_init( uint_t n, ... ) +// { +// va_list args; +// va_start( args, n ); + +// for( uint_t i = 0; i < n; i++ ) +// { + +// } +// }
\ No newline at end of file diff --git a/lib/twiggs_money_flow.c b/lib/twiggs_money_flow.c new file mode 100644 index 0000000..334f968 --- /dev/null +++ b/lib/twiggs_money_flow.c @@ -0,0 +1,67 @@ +// Twiggs Money Flow +// +// twiggs_money_flow +// +// Twiggs Money Flow +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_twiggs_money_flow_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_twiggs_money_flow_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 35 ); +} + +int ts_twiggs_money_flow( + 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 +) +{ + TS_PROLOGUE( ts_twiggs_money_flow_p ); + (void)c; + if (n < 2) return -1; + + uint_t period = n - 2; + + fp_t pos_sum = 0; + fp_t neg_sum = 0; + + for( uint_t j = 0; j < period; ++j ) y[0][j] = 0; + for( uint_t i = 1; i <= period; ++i ) + { + fp_t change = x[0][i] - x[0][i - 1]; + if( change >= 0 ) pos_sum += change; + else neg_sum += fabs( change ); + } + + fp_t total = pos_sum + neg_sum; + y[0][period] = ( total != 0 ) ? ( pos_sum - neg_sum ) / total * 100.0 : 0; + + for( uint_t j = 1; j < n - period; ++j ) + { + fp_t new_change = x[0][period + j] - x[0][period + j - 1]; + fp_t old_change = x[0][j] - x[0][j - 1]; + + if( new_change >= 0 ) pos_sum += new_change; + else neg_sum += fabs( new_change ); + if( old_change >= 0 ) pos_sum -= old_change; + else neg_sum -= fabs( old_change ); + + total = pos_sum + neg_sum; + y[0][period + j] = ( total != 0 ) ? ( pos_sum - neg_sum ) / total * 100.0 : 0; + } + return 0; +} + diff --git a/lib/typical_price.c b/lib/typical_price.c new file mode 100644 index 0000000..2d7f1c6 --- /dev/null +++ b/lib/typical_price.c @@ -0,0 +1,41 @@ +// Typical Price +// +// typical_price +// +// Typical Price +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + + +ts_parameter_t const ts_typical_price_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_typical_price_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_typical_price( + 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 +) +{ + TS_PROLOGUE( ts_typical_price_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ( x[1][i] + x[2][i] + x[3][i] ) / 3; + } + return 0; +} + diff --git a/lib/ulcer_index.c b/lib/ulcer_index.c new file mode 100644 index 0000000..ca70734 --- /dev/null +++ b/lib/ulcer_index.c @@ -0,0 +1,76 @@ +// Ulcer Index +// +// ulcer_index +// +// Ulcer Index +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_ulcer_index_params_t; + +ts_parameter_t const ts_ulcer_index_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_ulcer_index_params_t) }; + +uint_t ts_ulcer_index_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_ulcer_index_params_t *)c)->k > 0 ? ((ts_ulcer_index_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_ulcer_index( + 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 +) +{ + TS_PROLOGUE( ts_ulcer_index_p ); + if ( !c ) return -1; + uint_t k = ((ts_ulcer_index_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + /* Precompute cumulative highest-high for each index: O(n) */ + fp_t *cum_hhv = calloc( n, sizeof( fp_t ) ); + if ( !cum_hhv ) return -1; + { + fp_t hh = x[0][0]; + for ( uint_t i = 0; i < n; ++i ) + { + if ( x[0][i] > hh ) hh = x[0][i]; + cum_hhv[i] = hh; + } + } + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t hhv = x[0][i - k + 1]; + for( uint_t j = 1; j < k; ++j ) + { + fp_t v = x[0][i - k + 1 + j]; + if (v > hhv) hhv = v; + } + + fp_t d_sq_sum = 0; + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + fp_t ch = cum_hhv[idx]; + fp_t d = ch > 0 ? ( ch - x[0][idx] ) / ch * 100.0 : 0; + d_sq_sum += d * d; + } + y[0][i] = sqrt( d_sq_sum / (fp_t)k ); + } + + free( cum_hhv ); + return 0; +} + diff --git a/lib/ultimate_oscillator.c b/lib/ultimate_oscillator.c new file mode 100644 index 0000000..aec2ef4 --- /dev/null +++ b/lib/ultimate_oscillator.c @@ -0,0 +1,88 @@ +// Ultimate Oscillator +// +// ultimate_oscillator +// +// Ultimate Oscillator +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + +ts_parameter_t const ts_ultimate_oscillator_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_ultimate_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 54 ); +} + +int ts_ultimate_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_ultimate_oscillator_p ); + (void)c; + for( uint_t i = 54; i < n; ++i ) + { + fp_t bp7 = 0, tr7 = 0; + fp_t bp14 = 0, tr14 = 0; + fp_t bp28 = 0, tr28 = 0; + + for( uint_t j = 0; j < 7; ++j ) + { + uint_t idx = i - 6 + j; + fp_t ll = ts_l( x, idx ); + fp_t hh = ts_h( x, idx ); + for( uint_t k = 1; k < 7; ++k ) + { + ll = fmin( ll, ts_l( x, idx - k ) ); + hh = fmax( hh, ts_h( x, idx - k ) ); + } + bp7 += ts_c( x, idx ) - ll; + tr7 += hh - ll; + } + + for( uint_t j = 0; j < 14; ++j ) + { + uint_t idx = i - 13 + j; + fp_t ll = ts_l( x, idx ); + fp_t hh = ts_h( x, idx ); + for( uint_t k = 1; k < 14; ++k ) + { + ll = fmin( ll, ts_l( x, idx - k ) ); + hh = fmax( hh, ts_h( x, idx - k ) ); + } + bp14 += ts_c( x, idx ) - ll; + tr14 += hh - ll; + } + + for( uint_t j = 0; j < 28; ++j ) + { + uint_t idx = i - 27 + j; + fp_t ll = ts_l( x, idx ); + fp_t hh = ts_h( x, idx ); + for( uint_t k = 1; k < 28; ++k ) + { + ll = fmin( ll, ts_l( x, idx - k ) ); + hh = fmax( hh, ts_h( x, idx - k ) ); + } + bp28 += ts_c( x, idx ) - ll; + tr28 += hh - ll; + } + + fp_t denom = 7.0 * tr7 + 3.0 * tr14 + tr28; + fp_t numer = 7.0 * bp7 + 3.0 * bp14 + bp28; + y[0][i] = denom != 0 ? 100.0 * numer / denom : 50.0; + } + return 0; +} + diff --git a/lib/valuation_lines.c b/lib/valuation_lines.c new file mode 100644 index 0000000..b56a871 --- /dev/null +++ b/lib/valuation_lines.c @@ -0,0 +1,62 @@ +// Valuation Lines +// +// valuation_lines +// +// Valuation Lines +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_valuation_lines_params_t; + +ts_parameter_t const ts_valuation_lines_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_valuation_lines_params_t) }; + +uint_t ts_valuation_lines_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_valuation_lines_params_t *)c)->k > 0 ? ((ts_valuation_lines_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_valuation_lines( + 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 +) +{ + TS_PROLOGUE( ts_valuation_lines_p ); + if ( !c ) return -1; + uint_t k = ((ts_valuation_lines_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k - 1; i < n; ++i ) + { + fp_t min_val = ts_l( x, i ); + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = ts_l( x, i - j ); + if( val < min_val ) + min_val = val; + } + y[0][i] = min_val; + + fp_t max_val = ts_h( x, i ); + for( uint_t j = 1; j < k; ++j ) + { + fp_t val = ts_h( x, i - j ); + if( val > max_val ) + max_val = val; + } + y[1][i] = max_val; + } + return 0; +} + diff --git a/lib/variable_index_dynamic_average.c b/lib/variable_index_dynamic_average.c new file mode 100644 index 0000000..5f4ae4f --- /dev/null +++ b/lib/variable_index_dynamic_average.c @@ -0,0 +1,65 @@ +// Variable Index Dynamic Average +// +// variable_index_dynamic_average +// +// Variable Index Dynamic Average +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t period; +} ts_variable_index_dynamic_average_params_t; + +ts_parameter_t const ts_variable_index_dynamic_average_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_variable_index_dynamic_average_params_t) }; + +uint_t ts_variable_index_dynamic_average_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_variable_index_dynamic_average_params_t *)c)->period : 0 ); +} + +int ts_variable_index_dynamic_average( + 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 +) +{ + TS_PROLOGUE( ts_variable_index_dynamic_average_p ); + if ( !c ) return -1; + uint_t period = ((ts_variable_index_dynamic_average_params_t *)c)->period; + if (period < 2 || n <= period) return -1; + + fp_t vidya = x[0][period]; + y[0][period] = vidya; + + fp_t alpha_init = (fp_t)(period - 2) / (2.0 * (fp_t)(period - 1)); + fp_t alpha = alpha_init; + + for( uint_t i = period + 1; i < n; ++i ) + { + vidya = vidya + alpha * (x[0][i] - vidya); + y[0][i] = vidya; + + fp_t numerator = fabs( x[0][i] - x[0][i - period] ); + fp_t denominator = 0; + for( uint_t j = i - period + 1; j <= i; ++j ) + denominator += fabs( x[0][j] - x[0][j - 1] ); + fp_t er = (denominator == 0) ? 0 : numerator / denominator; + + fp_t alpha_new = er * er * 2.0 / (fp_t)(period + 1); + if( alpha_new < 0 ) alpha_new = 0; + if( alpha_new > 1 ) alpha_new = 1; + + alpha = (alpha + alpha_new) / 2.0; + } + return 0; +} + diff --git a/lib/variance.c b/lib/variance.c new file mode 100755 index 0000000..f307198 --- /dev/null +++ b/lib/variance.c @@ -0,0 +1,72 @@ +// Variance +// +// variance - smv +// +// Calculates the moving variance +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +typedef struct { + uint_t k; +} ts_variance_params_t; + +ts_parameter_t const ts_variance_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_variance_params_t) }; + + +uint_t ts_variance_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_variance_params_t *)c)->k > 0 ? ((ts_variance_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_variance( + 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 +) +{ + TS_PROLOGUE( ts_variance_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_variance_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t xi = 0; + uint_t yi = k; + + fp_t sum = 0; // Sum of terms + fp_t sum2 = 0; // Sum of squared terms + fp_t mean; // Mean + fp_t mean2; // Mean of squares + + for( ; xi < k; ++xi ) + { + sum += x[0][xi]; + sum2 += _ts_square( x[0][xi] ); + + mean = sum / ( xi + 1 ); + mean2 = sum2 / ( xi + 1 ); + } + + fp_t var = mean2 - _ts_square( mean ); + y[0][k - 1] = var >= 0 ? var : 0.0; + + fp_t rk = _ts_reciprocal( k ); + + for( ; xi < n; ++xi, ++yi ) + { + mean = mean + ( x[0][xi] - x[0][xi - k] ) * rk; + mean2 = mean2 + ( _ts_square( x[0][xi] ) - _ts_square( x[0][xi - k] ) ) * rk; + + var = mean2 - _ts_square( mean ); + y[0][yi] = var >= 0 ? var : 0.0; + } + return 0; +} + diff --git a/lib/variance_exponential.c b/lib/variance_exponential.c new file mode 100755 index 0000000..338a140 --- /dev/null +++ b/lib/variance_exponential.c @@ -0,0 +1,51 @@ +// Exponential Variance +// +// variance_exponential +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + +#include "ts.h" + + +typedef struct { + fp_t a; +} ts_variance_exponential_params_t; + +ts_parameter_t const ts_variance_exponential_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_variance_exponential_params_t) }; + + +uint_t ts_variance_exponential_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_variance_exponential( + 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 +) +{ + TS_PROLOGUE( ts_variance_exponential_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t a = ((ts_variance_exponential_params_t *)c)->a; + if (a == 0) return -1; + + y[0][0] = 0; + + fp_t delta; + fp_t ema = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + delta = x[0][i] - ema; + ema = ema + a * delta; + y[0][i] = ( 1 - a ) * ( y[0][i - 1] + a * _ts_square( delta ) ); + } + return 0; +} + diff --git a/lib/vertical_horizontal_filter.c b/lib/vertical_horizontal_filter.c new file mode 100644 index 0000000..f5c91cf --- /dev/null +++ b/lib/vertical_horizontal_filter.c @@ -0,0 +1,75 @@ +// Vertical Horizontal Filter +// +// vertical_horizontal_filter +// +// Vertical Horizontal Filter +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_vertical_horizontal_filter_params_t; + +ts_parameter_t const ts_vertical_horizontal_filter_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_vertical_horizontal_filter_params_t) }; + +uint_t ts_vertical_horizontal_filter_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_vertical_horizontal_filter_params_t *)c)->k : 0 ); +} + +int ts_vertical_horizontal_filter( + 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 +) +{ + TS_PROLOGUE( ts_vertical_horizontal_filter_p ); + if ( !c ) return -1; + uint_t k = ((ts_vertical_horizontal_filter_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t cum_den = 0; + + for ( uint_t j = 1; j <= k; ++j ) + { + cum_den += fabs( ts_h( x, j ) - ts_h( x, j - 1 ) ) + + fabs( ts_l( x, j ) - ts_l( x, j - 1 ) ); + } + + for ( uint_t i = k; i < n; ++i ) + { + if ( i > k ) + { + cum_den += fabs( ts_h( x, i ) - ts_h( x, i - 1 ) ) + + fabs( ts_l( x, i ) - ts_l( x, i - 1 ) ); + cum_den -= fabs( ts_h( x, i - k ) - ts_h( x, i - k - 1 ) ) + + fabs( ts_l( x, i - k ) - ts_l( x, i - k - 1 ) ); + } + + fp_t hh = ts_h( x, i - k + 1 ); + fp_t ll = ts_l( x, i - k + 1 ); + for ( uint_t j = i - k + 2; j <= i; ++j ) + { + if ( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if ( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + + fp_t range = hh - ll; + + if ( cum_den == 0 ) + y[0][i] = ( range == 0 ) ? 1 : 0; + else + y[0][i] = range / cum_den; + } + return 0; +} + diff --git a/lib/volatility_stop.c b/lib/volatility_stop.c new file mode 100644 index 0000000..bfb207b --- /dev/null +++ b/lib/volatility_stop.c @@ -0,0 +1,74 @@ +// Volatility Stop +// +// volatility_stop +// +// Volatility Stop +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 2 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; + fp_t mult; +} ts_volatility_stop_params_t; + +ts_parameter_t const ts_volatility_stop_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 2, .params_size = sizeof(ts_volatility_stop_params_t) }; + +uint_t ts_volatility_stop_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_volatility_stop_params_t *)c)->k : 0 ); +} + +int ts_volatility_stop( + 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 +) +{ + TS_PROLOGUE( ts_volatility_stop_p ); + if ( !c ) return -1; + uint_t k = ((ts_volatility_stop_params_t *)c)->k; + fp_t mult = ((ts_volatility_stop_params_t *)c)->mult; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + fp_t hh = ts_h(x, i - k); + fp_t ll = ts_l(x, i - k); + + for( uint_t j = i - k + 1; j <= i; ++j ) + { + if( ts_h(x, j) > hh ) hh = ts_h(x, j); + if( ts_l(x, j) < ll ) ll = ts_l(x, j); + } + + fp_t atr = 0; + for( uint_t j = i - k; j <= i; ++j ) + { + fp_t tr = ts_h(x, j) - ts_l(x, j); + if( j > 0 ) + { + fp_t prev_close = ts_c(x, j - 1); + fp_t tr2 = fabs(ts_h(x, j) - prev_close); + fp_t tr3 = fabs(ts_l(x, j) - prev_close); + if( tr2 > tr ) tr = tr2; + if( tr3 > tr ) tr = tr3; + } + atr += tr; + } + atr /= (k + 1); + + y[0][i] = hh - mult * atr; + y[1][i] = ll + mult * atr; + } + return 0; +} + diff --git a/lib/volatility_system.c b/lib/volatility_system.c new file mode 100644 index 0000000..dbb74d1 --- /dev/null +++ b/lib/volatility_system.c @@ -0,0 +1,55 @@ +// Volatility System +// +// volatility_system +// +// Volatility System +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_volatility_system_params_t; + +ts_parameter_t const ts_volatility_system_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_volatility_system_params_t) }; + +uint_t ts_volatility_system_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? 2 * ((ts_volatility_system_params_t *)c)->k : 0 ); +} + +int ts_volatility_system( + 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 +) +{ + TS_PROLOGUE( ts_volatility_system_p ); + if ( !c ) return -1; + uint_t k = ((ts_volatility_system_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + uint_t range_period = 2 * k; + if (n < range_period) return -1; + + fp_t ema = (ts_c(x, range_period - 2) + ts_c(x, range_period - 1)) / 2.0; + + for( uint_t i = range_period; i < n; ++i ) + { + if( i == range_period ) + ema = (ts_c(x, i-1) + ts_c(x, i)) / 2.0; + else + ema = ema * 0.5 + ts_c(x, i) * 0.5; + y[0][i] = ema; + } + return 0; +} + diff --git a/lib/volume_delta.c b/lib/volume_delta.c new file mode 100644 index 0000000..cabd94a --- /dev/null +++ b/lib/volume_delta.c @@ -0,0 +1,60 @@ +// Volume Delta +// +// volume_delta +// +// Volume Delta +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_volume_delta_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_volume_delta_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_volume_delta( + 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 +) +{ + TS_PROLOGUE( ts_volume_delta_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t hl = x[1][i] - x[2][i]; + if( hl == 0 ) + { + y[0][i] = 0; + } + else if( x[3][i] > x[0][i] ) + { + fp_t buy_vol = x[4][i] * ( x[3][i] - x[2][i] ) / hl; + fp_t sell_vol = x[4][i] - buy_vol; + y[0][i] = buy_vol - sell_vol; + } + else if( x[3][i] < x[0][i] ) + { + fp_t sell_vol = x[4][i] * ( x[1][i] - x[3][i] ) / hl; + fp_t buy_vol = x[4][i] - sell_vol; + y[0][i] = buy_vol - sell_vol; + } + else + { + y[0][i] = 0; + } + } + + return 0; +} + diff --git a/lib/volume_moving_average.c b/lib/volume_moving_average.c new file mode 100644 index 0000000..93eb592 --- /dev/null +++ b/lib/volume_moving_average.c @@ -0,0 +1,48 @@ +// Volume Moving Average +// +// volume_moving_average +// +// Volume Moving Average +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_volume_moving_average_params_t; + +ts_parameter_t const ts_volume_moving_average_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_volume_moving_average_params_t) }; + +uint_t ts_volume_moving_average_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_volume_moving_average_params_t *)c)->k > 0 ? ((ts_volume_moving_average_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_volume_moving_average( + 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 +) +{ + TS_PROLOGUE( ts_volume_moving_average_p ); + if ( !c ) return -1; + uint_t k = ((ts_volume_moving_average_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = k - 1; i < n; ++i) + { + fp_t sum = 0; + for (uint_t j = i - k + 1; j <= i; ++j) + sum += x[4][j]; + y[0][i] = sum / k; + } + return 0; +} + diff --git a/lib/volume_oscillator.c b/lib/volume_oscillator.c new file mode 100644 index 0000000..6aa59f0 --- /dev/null +++ b/lib/volume_oscillator.c @@ -0,0 +1,57 @@ +// Volume Oscillator +// +// volume_oscillator +// +// Volume Oscillator +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 2 + + +#include "ts.h" + +typedef struct { + fp_t fast_k; + fp_t slow_k; +} ts_volume_oscillator_params_t; + +ts_parameter_t const ts_volume_oscillator_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 2, .params_size = sizeof(ts_volume_oscillator_params_t) }; + +uint_t ts_volume_oscillator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_volume_oscillator_params_t *)c)->slow_k > 0 ? (uint_t)(((ts_volume_oscillator_params_t *)c)->slow_k - 1) : 0 : 0 ); +} + +int ts_volume_oscillator( + 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 +) +{ + TS_PROLOGUE( ts_volume_oscillator_p ); + if ( !c ) return -1; + fp_t fast_k = ((ts_volume_oscillator_params_t *)c)->fast_k; + fp_t slow_k = ((ts_volume_oscillator_params_t *)c)->slow_k; + if (fast_k == 0 || slow_k == 0) return -1; + if (n == 0) return -1; + + fp_t fast_a = 2.0 / (fast_k + 1); + fp_t slow_a = 2.0 / (slow_k + 1); + + fp_t fast_ema = x[0][0]; + fp_t slow_ema = x[0][0]; + y[0][0] = 0; + + for (uint_t i = 1; i < n; ++i) + { + fast_ema = fast_ema + fast_a * (x[0][i] - fast_ema); + slow_ema = slow_ema + slow_a * (x[0][i] - slow_ema); + y[0][i] = slow_ema != 0 ? (fast_ema - slow_ema) / slow_ema * 100 : 0; + } + return 0; +} + diff --git a/lib/volume_rate_of_change.c b/lib/volume_rate_of_change.c new file mode 100644 index 0000000..ae9c776 --- /dev/null +++ b/lib/volume_rate_of_change.c @@ -0,0 +1,46 @@ +// Volume Rate Of Change +// +// volume_rate_of_change +// +// Volume Rate of Change +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_volume_rate_of_change_params_t; + +ts_parameter_t const ts_volume_rate_of_change_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_volume_rate_of_change_params_t) }; + +uint_t ts_volume_rate_of_change_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_volume_rate_of_change_params_t *)c)->k : 0 ); +} + +int ts_volume_rate_of_change( + 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 +) +{ + TS_PROLOGUE( ts_volume_rate_of_change_p ); + if ( !c ) return -1; + uint_t k = ((ts_volume_rate_of_change_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for (uint_t i = k; i < n; ++i) + { + y[0][i] = x[4][i - k] != 0 ? (x[4][i] - x[4][i - k]) / x[4][i - k] * 100 : 0; + } + return 0; +} + diff --git a/lib/volume_underlay.c b/lib/volume_underlay.c new file mode 100644 index 0000000..030ba66 --- /dev/null +++ b/lib/volume_underlay.c @@ -0,0 +1,45 @@ +// Volume Underlay +// +// volume_underlay +// +// Volume Underlay +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + + +ts_parameter_t const ts_volume_underlay_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(uint_t) }; + +uint_t ts_volume_underlay_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_volume_underlay( + 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 +) +{ + TS_PROLOGUE( ts_volume_underlay_p ); + (void)c; + /* Volume Underlay: normalize volume to [0,1] */ + /* Find max volume */ + fp_t max_vol = 0; + for (uint_t i = 0; i < n; ++i) { + if (x[0][i] > max_vol) max_vol = x[0][i]; + } + + for (uint_t i = 0; i < n; ++i) { + y[0][i] = max_vol != 0 ? x[0][i] / max_vol : 0; + } + return 0; +} + diff --git a/lib/volume_weighted_moving_average.c b/lib/volume_weighted_moving_average.c new file mode 100644 index 0000000..d6cd6bd --- /dev/null +++ b/lib/volume_weighted_moving_average.c @@ -0,0 +1,58 @@ +// Volume Weighted Moving Average +// +// volume_weighted_moving_average +// +// Volume Weighted Moving Average +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_volume_weighted_moving_average_params_t; + +ts_parameter_t const ts_volume_weighted_moving_average_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_volume_weighted_moving_average_params_t) }; + +uint_t ts_volume_weighted_moving_average_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_volume_weighted_moving_average_params_t *)c)->k > 0 ? ((ts_volume_weighted_moving_average_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_volume_weighted_moving_average( + 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 +) +{ + TS_PROLOGUE( ts_volume_weighted_moving_average_p ); + if ( !c ) return -1; + uint_t k = ((ts_volume_weighted_moving_average_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + fp_t cv_sum = 0; + fp_t v_sum = 0; + + for( uint_t j = 0; j < k; ++j ) + { + cv_sum += ts_c(x, j) * ts_v(x, j); + v_sum += ts_v(x, j); + } + + y[0][k - 1] = (v_sum != 0) ? cv_sum / v_sum : 0; + + for( uint_t i = k; i < n; ++i ) + { + cv_sum += ts_c(x, i) * ts_v(x, i) - ts_c(x, i - k) * ts_v(x, i - k); + v_sum += ts_v(x, i) - ts_v(x, i - k); + y[0][i] = (v_sum != 0) ? cv_sum / v_sum : 0; + } + return 0; +} + diff --git a/lib/vortex_indicator.c b/lib/vortex_indicator.c new file mode 100644 index 0000000..39ecfbc --- /dev/null +++ b/lib/vortex_indicator.c @@ -0,0 +1,64 @@ +// Vortex Indicator +// +// vortex_indicator +// +// Vortex Indicator +// +// +// +// Inputs: 4 +// Outputs: 2 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + uint_t k; +} ts_vortex_indicator_params_t; + +ts_parameter_t const ts_vortex_indicator_p = { .n_inputs = 4, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_vortex_indicator_params_t) }; + +uint_t ts_vortex_indicator_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_vortex_indicator_params_t *)c)->k : 0 ); +} + +int ts_vortex_indicator( + 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 +) +{ + TS_PROLOGUE( ts_vortex_indicator_p ); + if ( !c ) return -1; + uint_t k = ((ts_vortex_indicator_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + for( uint_t i = k; i < n; ++i ) + { + fp_t sum_vm_plus = 0; + fp_t sum_vm_minus = 0; + fp_t sum_tr = 0; + + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + fp_t vm_plus = fabs( ts_h( x, idx ) - ts_l( x, idx - 1 ) ); + fp_t vm_minus = fabs( ts_l( x, idx ) - ts_h( x, idx - 1 ) ); + fp_t tr = fmax( ts_h( x, idx ) - ts_l( x, idx ), + fmax( fabs( ts_h( x, idx ) - ts_c( x, idx - 1 ) ), + fabs( ts_l( x, idx ) - ts_c( x, idx - 1 ) ) ) ); + sum_vm_plus += vm_plus; + sum_vm_minus += vm_minus; + sum_tr += tr; + } + + y[0][i] = sum_tr != 0 ? sum_vm_plus / sum_tr : 0; + y[1][i] = sum_tr != 0 ? sum_vm_minus / sum_tr : 0; + } + return 0; +} + diff --git a/lib/vwap.c b/lib/vwap.c new file mode 100644 index 0000000..50308c7 --- /dev/null +++ b/lib/vwap.c @@ -0,0 +1,44 @@ +// Vwap +// +// vwap +// +// Volume Weighted Average Price +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_vwap_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_vwap_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_vwap( + 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 +) +{ + TS_PROLOGUE( ts_vwap_p ); + (void)c; + fp_t cum_pv = 0; + fp_t cum_v = 0; + + for( uint_t i = 0; i < n; ++i ) + { + cum_pv += ts_c( x, i ) * ts_v( x, i ); + cum_v += ts_v( x, i ); + y[0][i] = cum_v != 0 ? cum_pv / cum_v : 0; + } + return 0; +} + diff --git a/lib/vwap_period.c b/lib/vwap_period.c new file mode 100644 index 0000000..b8af2bd --- /dev/null +++ b/lib/vwap_period.c @@ -0,0 +1,58 @@ +// Vwap Period +// +// vwap_period +// +// Period VWAP +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_vwap_period_params_t; + +ts_parameter_t const ts_vwap_period_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_vwap_period_params_t) }; + +uint_t ts_vwap_period_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_vwap_period_params_t *)c)->k > 0 ? ((ts_vwap_period_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_vwap_period( + 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 +) +{ + TS_PROLOGUE( ts_vwap_period_p ); + if ( !c ) return -1; + uint_t k = ((ts_vwap_period_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + /* Periodic VWAP: reset cumulative sums every k bars */ + fp_t sum_pv = 0; + fp_t sum_vol = 0; + + for (uint_t i = 0; i < n; ++i) + { + sum_pv += ts_c( x, i ) * ts_v( x, i ); + sum_vol += ts_v( x, i ); + + y[0][i] = sum_vol != 0 ? sum_pv / sum_vol : 0; + + if ((i + 1) % k == 0 || i == n - 1) + { + sum_pv = 0; + sum_vol = 0; + } + } + return 0; +} + diff --git a/lib/weibull_pdf.c b/lib/weibull_pdf.c new file mode 100644 index 0000000..894c94a --- /dev/null +++ b/lib/weibull_pdf.c @@ -0,0 +1,46 @@ +// Weibull Pdf +// +// weibull_pdf +// +// Weibull probability density +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include <math.h> +#include "ts.h" + +typedef struct { + fp_t k; +} ts_weibull_pdf_params_t; + +ts_parameter_t const ts_weibull_pdf_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_weibull_pdf_params_t) }; + +uint_t ts_weibull_pdf_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_weibull_pdf( + 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 +) +{ + TS_PROLOGUE( ts_weibull_pdf_p ); + if ( !c ) return -1; + fp_t k = ((ts_weibull_pdf_params_t *)c)->k; + if (k <= 0.0 || isnan(k) || isinf(k)) return -1; + for( uint_t i = 0; i < n; ++i ) + { + fp_t xi = x[0][i]; + y[0][i] = xi >= 0 ? k * pow( xi, k - 1.0 ) * exp( -pow( xi, k ) ) : 0.0; + } + return 0; +} + diff --git a/lib/weighted_close.c b/lib/weighted_close.c new file mode 100644 index 0000000..30b8585 --- /dev/null +++ b/lib/weighted_close.c @@ -0,0 +1,39 @@ +// Weighted Close +// +// weighted_close +// +// Weighted Close +// +// +// +// Inputs: 5 +// Outputs: 1 +// Constants: 0 + + +#include "ts.h" + + +ts_parameter_t const ts_weighted_close_p = { .n_inputs = 5, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + +uint_t ts_weighted_close_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_weighted_close( + 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 +) +{ + TS_PROLOGUE( ts_weighted_close_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + y[0][i] = ( x[1][i] + x[2][i] + 2 * x[3][i] ) / 4; + } + return 0; +} + diff --git a/lib/wilders_smoothing.c b/lib/wilders_smoothing.c new file mode 100644 index 0000000..7aca8ad --- /dev/null +++ b/lib/wilders_smoothing.c @@ -0,0 +1,52 @@ +// Wilders Smoothing +// +// wilders_smoothing +// +// Wilder's Smoothing +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + fp_t k; +} ts_wilders_smoothing_params_t; + +ts_parameter_t const ts_wilders_smoothing_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_wilders_smoothing_params_t) }; + +uint_t ts_wilders_smoothing_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_wilders_smoothing( + 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 +) +{ + TS_PROLOGUE( ts_wilders_smoothing_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t k = ((ts_wilders_smoothing_params_t *)c)->k; + if (k == 0) return -1; + fp_t a = 1.0 / k; + + y[0][0] = x[0][0]; + + for( uint_t i = 1; i < n; ++i ) + { + y[0][i] = y[0][i - 1] + a * ( x[0][i] - y[0][i - 1] ); + } + + return 0; +} + + diff --git a/lib/williams_r.c b/lib/williams_r.c new file mode 100644 index 0000000..f0fae3f --- /dev/null +++ b/lib/williams_r.c @@ -0,0 +1,86 @@ +// Williams R +// +// williams_r +// +// Williams %R +// +// +// +// Inputs: 4 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_williams_r_params_t; + +ts_parameter_t const ts_williams_r_p = { .n_inputs = 4, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_williams_r_params_t) }; + +uint_t ts_williams_r_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_williams_r_params_t *)c)->k > 0 ? ((ts_williams_r_params_t *)c)->k - 1 : 0 : 0 ); +} + +int ts_williams_r( + 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 +) +{ + TS_PROLOGUE( ts_williams_r_p ); + if ( !c ) return -1; + TS_CHECK_INPUT(x, x_n, n); + uint_t k = ((ts_williams_r_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + // Initial window max/min + fp_t hh = ts_h( x, 0 ); + fp_t ll = ts_l( x, 0 ); + for( uint_t j = 1; j < k; ++j ) + { + if( ts_h( x, j ) > hh ) + hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) + ll = ts_l( x, j ); + } + + { + if( hh == ll ) + y[0][k - 1] = 0; + else + y[0][k - 1] = (hh - ts_c( x, k - 1 )) / (hh - ll) * (-100.0); + } + + for( uint_t i = k; i < n; ++i ) + { + fp_t new_h = ts_h( x, i ); + fp_t new_l = ts_l( x, i ); + fp_t old_h = ts_h( x, i - k ); + fp_t old_l = ts_l( x, i - k ); + + if ( new_h > hh ) hh = new_h; + if ( new_l < ll ) ll = new_l; + if ( old_h == hh || old_l == ll ) + { + hh = ts_h( x, i - k + 1 ); + ll = ts_l( x, i - k + 1 ); + for( uint_t j = i - k + 2; j <= i; ++j ) + { + if( ts_h( x, j ) > hh ) hh = ts_h( x, j ); + if( ts_l( x, j ) < ll ) ll = ts_l( x, j ); + } + } + + if( hh == ll ) + y[0][i] = 0; + else + y[0][i] = (hh - ts_c( x, i )) / (hh - ll) * (-100.0); + } + + return 0; +} + diff --git a/lib/zero_lag_ema.c b/lib/zero_lag_ema.c new file mode 100644 index 0000000..c78adbd --- /dev/null +++ b/lib/zero_lag_ema.c @@ -0,0 +1,53 @@ +// Zero Lag Ema +// +// zero_lag_ema +// +// Zero Lag EMA +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 1 + + +#include "ts.h" + +typedef struct { + uint_t k; +} ts_zero_lag_ema_params_t; + +ts_parameter_t const ts_zero_lag_ema_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 1, .params_size = sizeof(ts_zero_lag_ema_params_t) }; + +uint_t ts_zero_lag_ema_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( c ? ((ts_zero_lag_ema_params_t *)c)->k : 0 ); +} + +int ts_zero_lag_ema( + 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 +) +{ + TS_PROLOGUE( ts_zero_lag_ema_p ); + if ( !c ) return -1; + uint_t k = ((ts_zero_lag_ema_params_t *)c)->k; + if (k == 0 || n < k) return -1; + + /* Zero-lag EMA: compute lag, then apply EMA to (2*x - x_lag) */ + fp_t alpha = 2.0 / (k + 1.0); + uint_t lag = (k - 1) / 2; + + for (uint_t i = lag; i < n; ++i) + { + fp_t adjusted = 2.0 * x[0][i] - x[0][i - lag]; + if (i == lag) + y[0][i] = adjusted; + else + y[0][i] = y[0][i - 1] + alpha * (adjusted - y[0][i - 1]); + } + return 0; +} + diff --git a/lib/zeta.c b/lib/zeta.c new file mode 100644 index 0000000..7b32e77 --- /dev/null +++ b/lib/zeta.c @@ -0,0 +1,52 @@ +// Zeta +// +// zeta +// +// Riemann Zeta Function +// +// +// +// Inputs: 1 +// Outputs: 1 +// Constants: 0 + + +#include <math.h> +#include "ts.h" + + + +ts_parameter_t const ts_zeta_p = { .n_inputs = 1, .n_outputs = 1, .n_constants = 0, .params_size = 0 }; + + +uint_t ts_zeta_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_zeta( + 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 +) +{ + TS_PROLOGUE( ts_zeta_p ); + (void)c; + for( uint_t i = 0; i < n; ++i ) + { + fp_t s = x[0][i]; + if( s <= 1.0 ) { y[0][i] = 0; continue; } + fp_t sum = 0.0; + fp_t term = 0.0; + for( uint_t k = 1; k <= 100000; ++k ) + { + term = 1.0 / pow( (fp_t)k, s ); + sum += term; + if( term < 1e-12 && k > 1000 ) break; + } + y[0][i] = sum; + } + return 0; +} + diff --git a/lib/zigzag.c b/lib/zigzag.c new file mode 100644 index 0000000..50549e2 --- /dev/null +++ b/lib/zigzag.c @@ -0,0 +1,99 @@ +// Zigzag +// +// zigzag +// +// ZigZag Filter +// +// +// +// Inputs: 1 +// Outputs: 2 +// Constants: 1 + + +#include "ts.h" + + +typedef struct { + fp_t threshold; +} ts_zigzag_params_t; + +ts_parameter_t const ts_zigzag_p = { .n_inputs = 1, .n_outputs = 2, .n_constants = 1, .params_size = sizeof(ts_zigzag_params_t) }; + + +uint_t ts_zigzag_offset( ts_constant_t const *c ) +{ + ( void )c; + return ( 0 ); +} + +int ts_zigzag( + 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 +) +{ + TS_PROLOGUE( ts_zigzag_p ); + if ( !c ) return -1; + if (n == 0) return -1; + + fp_t threshold = ((ts_zigzag_params_t *)c)->threshold; + if (threshold <= 0 || isnan(threshold) || isinf(threshold)) return -1; + + y[0][0] = x[0][0]; + y[1][0] = 0; + + for( uint_t i = 1; i < n; ++i ) + { + fp_t last_zz = y[0][i - 1]; + fp_t dir = y[1][i - 1]; + fp_t change = last_zz != 0 ? ( x[0][i] - last_zz ) / last_zz * 100.0 : 0; + + if( dir == 0 ) + { + if( change >= threshold ) + { + y[0][i] = x[0][i]; + y[1][i] = 1; + } + else if( change <= -threshold ) + { + y[0][i] = x[0][i]; + y[1][i] = -1; + } + else + { + y[0][i] = last_zz; + y[1][i] = 0; + } + } + else if( dir == 1 ) + { + if( change <= -threshold ) + { + y[0][i] = x[0][i]; + y[1][i] = -1; + } + else + { + y[0][i] = last_zz; + y[1][i] = 1; + } + } + else + { + if( change >= threshold ) + { + y[0][i] = x[0][i]; + y[1][i] = 1; + } + else + { + y[0][i] = last_zz; + y[1][i] = -1; + } + } + } + return 0; +} + @@ -0,0 +1,92 @@ +// main.c - Moving average calculator using ts library +// Computes a simple moving average over configurable period and displays results. + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define TS_IMPLEMENTATION +#include "ts.h" + +#define BUFFER_SIZE 100 +#define DISPLAY_N 20 + +static void usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [period]\n", prog); + fprintf(stderr, " period: moving average window (1-%d, default: 10)\n", BUFFER_SIZE); +} + +int main(int argc, char *argv[]) { + uint_t period = 10; + if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { + usage(argv[0]); + return 0; + } + if (argc > 1) { + char *endptr = NULL; + errno = 0; + long val = strtol(argv[1], &endptr, 10); + if (endptr == argv[1] || *endptr != '\0' || errno == ERANGE) { + fprintf(stderr, "Error: invalid period '%s'\n", argv[1]); + usage(argv[0]); + return 1; + } + if (val < 1 || val > BUFFER_SIZE) { + fprintf(stderr, "Error: period must be between 1 and %d\n", BUFFER_SIZE); + usage(argv[0]); + return 1; + } + period = (uint_t)val; + } + if (period == 0 || period > BUFFER_SIZE) { + fprintf(stderr, "Error: period must be between 1 and %d\n", BUFFER_SIZE); + usage(argv[0]); + return 1; + } + + fp_t input[BUFFER_SIZE]; + fp_t output[BUFFER_SIZE]; + for (int_t i = 0; i < (int_t)BUFFER_SIZE; i++) { + output[i] = 0; + } + + if (!ts_fill(input, BUFFER_SIZE, 0, (fp_t)BUFFER_SIZE)) { + fprintf(stderr, "ts_fill failed\n"); + return 1; + } + + fp_t * const y[1] = { output }; + fp_t const * const x[1] = { input }; + + ts_constant_t c; + memset(&c, 0, sizeof(c)); + *(uint_t *)c.data = period; + int ret = ts_average(y, 1, x, 1, BUFFER_SIZE, &c); + if (ret == -1) { + fprintf(stderr, "ts_average failed\n"); + return 1; + } + + /* Validate output: first valid value is at index period-1 */ + uint_t offset = period - 1; + if (offset >= BUFFER_SIZE) { + fprintf(stderr, "error: period exceeds buffer size\n"); + return 1; + } + + /* Clamp display range to valid output */ + uint_t display_end = DISPLAY_N < BUFFER_SIZE ? DISPLAY_N : BUFFER_SIZE; + if (offset >= display_end) { + fprintf(stderr, "warning: period (%" TS_PRIuint ") >= display count (%" TS_PRIuint "), showing available data\n", period, display_end); + display_end = BUFFER_SIZE; + } + + for (uint_t i = offset; i < display_end; i++) { + printf("input[%" TS_PRIuint "] = %f SMA[%" TS_PRIuint "] = %f\n", i, input[i], i, output[i]); + } + + return 0; +} diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 0000000..2eed6ce --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "ts-bindings" +version = "0.1.0" +description = "Python bindings for the ts (time series) C library" +authors = [{name = "Ethan Werner"}] +requires-python = ">=3.9" +license = {text = "MIT"} +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Office/Business :: Financial", + "Topic :: Scientific/Engineering :: Mathematics", +] +dependencies = [ + "numpy>=1.21,<3.0", +] + +[project.urls] +Homepage = "https://github.com/ethanwerner/ts" +Repository = "https://github.com/ethanwerner/ts" + +[tool.setuptools.packages.find] +include = ["ts*"] + +[tool.setuptools.package-data] +ts = ["py.typed", "libts.so"] diff --git a/python/ts/py.typed b/python/ts/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/python/ts/py.typed diff --git a/scripts/gen_all.py b/scripts/gen_all.py new file mode 100644 index 0000000..dd9d395 --- /dev/null +++ b/scripts/gen_all.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Generate ts_generated.h with all indicator declarations and test registry. + +Parses lib/*.c for ts_parameter_t declarations and emits: +- extern ts_parameter_t const ts_<name>_p +- uint_t ts_<name>_offset(...) +- int ts_<name>(...) +- bool ts_test_<name>(void) [inside #ifdef TS_IMPLEMENTATION] +""" + +import importlib.util +import os +import re +import sys + +_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), +) +if _spec and _spec.loader: + _ts_parser = importlib.util.module_from_spec(_spec) + _spec.loader.exec_module(_ts_parser) + parse_all = _ts_parser.parse_all +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +TEST_DIR = os.path.join(ROOT, "test") +OUT_H = os.path.join(ROOT, "ts_generated.h") +OUT_TEST = os.path.join(TEST_DIR, "main_generated.c") + + +def generate_header(funcs): + """Generate ts_generated.h.""" + lines = [] + lines.append("/* Auto-generated indicator declarations. Do not edit. */") + lines.append("") + lines.append("#ifndef TS_GENERATED_H") + lines.append("#define TS_GENERATED_H") + lines.append("") + + for fm in funcs: + lines.append("extern ts_parameter_t const ts_" + fm.name + "_p;") + lines.append("uint_t ts_" + fm.name + "_offset( ts_constant_t const * );") + lines.append( + "int ts_" + + fm.name + + "( 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 );" + ) + + lines.append("") + lines.append("#ifdef TS_IMPLEMENTATION") + lines.append("") + for fm in funcs: + lines.append("bool ts_test_" + fm.name + "( void );") + lines.append("") + lines.append("#endif /* TS_IMPLEMENTATION */") + lines.append("") + lines.append("#endif /* TS_GENERATED_H */") + lines.append("") + + return "\n".join(lines) + + +def generate_test_main(funcs): + """Generate test/main_generated.c with extern declarations and test array.""" + lines = [] + lines.append("/* Auto-generated test registry. Do not edit. */") + lines.append("") + lines.append("#define EXTERN_TEST(name) extern bool ts_test_##name(void)") + for fm in funcs: + lines.append("EXTERN_TEST(" + fm.name + ");") + lines.append("#undef EXTERN_TEST") + lines.append("") + lines.append("typedef struct {") + lines.append(" const char *name;") + lines.append(" bool (*func)(void);") + lines.append("} test_case_t;") + lines.append("") + lines.append("#define TEST_ENTRY(name) { #name, ts_test_##name }") + lines.append("") + lines.append("static test_case_t tests[] = {") + for fm in funcs: + lines.append(" TEST_ENTRY(" + fm.name + "),") + lines.append("};") + lines.append("") + lines.append("#define NUM_TESTS (sizeof(tests) / sizeof(tests[0]))") + lines.append("") + lines.append("#undef TEST_ENTRY") + lines.append("") + + return "\n".join(lines) + + +def _is_valid_c_identifier(name): + return bool(re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", name)) + + +def main(): + funcs = parse_all(detect_x_access=False) + + if not funcs: + print("Warning: no ts_parameter declarations found.", file=sys.stderr) + return 1 + + for fm in funcs: + if not _is_valid_c_identifier(fm.name): + print(f"Error: invalid C identifier '{fm.name}'", file=sys.stderr) + return 1 + + os.makedirs(TEST_DIR, exist_ok=True) + + try: + with open(OUT_H, "w", encoding="utf-8") as f: + f.write(generate_header(funcs)) + except OSError as e: + print(f"Error writing {OUT_H}: {e}", file=sys.stderr) + return 1 + + try: + with open(OUT_TEST, "w", encoding="utf-8") as f: + f.write(generate_test_main(funcs)) + except OSError as e: + print(f"Error writing {OUT_TEST}: {e}", file=sys.stderr) + return 1 + + print(f"Generated {len(funcs)} declarations -> {OUT_H}") + print(f"Generated test registry -> {OUT_TEST}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/gen_bindings.py b/scripts/gen_bindings.py new file mode 100644 index 0000000..845c8a5 --- /dev/null +++ b/scripts/gen_bindings.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +"""Generate python/ts/__init__.py from C source. + +Produces a compact module with a single _dispatch() function and a +FUNC_META lookup table instead of 350+ individual wrappers. +""" + +import importlib.util +import os +import py_compile +import sys + +_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), +) +if _spec and _spec.loader: + _ts_parser = importlib.util.module_from_spec(_spec) + _spec.loader.exec_module(_ts_parser) + parse_all = _ts_parser.parse_all +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = os.path.join(ROOT, "python", "ts", "__init__.py") + +DEFAULT_PARAMS = { + "ichimoku": {"tenkan": 9, "kijun": 26, "senkou": 52}, +} + +MIN_PARAM_DEFAULTS = { + "hull_ma": {"k": 2}, + "variable_index_dynamic_average": {"period": 2}, + "logarithm": {"b": 2.0}, + "choppiness_index": {"k": 2}, + "performance_ratio": {"k": 2}, + "standard_error": {"k": 2}, +} + + +def _py_default(pytype, py_fname, name): + if py_fname in DEFAULT_PARAMS.get(name, {}): + return DEFAULT_PARAMS[name][py_fname] + if py_fname in MIN_PARAM_DEFAULTS.get(name, {}): + return MIN_PARAM_DEFAULTS[name][py_fname] + return 1 if "uint64" in pytype else 1.0 + + +def main(): + funcs = parse_all(detect_x_access=True) + if not funcs: + print("Warning: no ts_parameter declarations found.", file=sys.stderr) + sys.exit(1) + + os.makedirs(os.path.dirname(OUT), exist_ok=True) + + with open(OUT, "w", encoding="utf-8") as f: + # --- Header --- + f.write('"""Python bindings for ts (time series) C library.\n') + f.write("\nAuto-generated. Uses a single dispatch function backed by\n") + f.write('a FUNC_META lookup table.\n"""\n\n') + + f.write("import ctypes\n") + f.write("import ctypes.util\n") + f.write("import os\n") + f.write("import numpy as np\n") + f.write("import threading\n\n") + + f.write("_tls = threading.local()\n\n") + + # --- Library loader --- + f.write("def _find_lib():\n") + f.write(' """Try system path, pkg-config, then build dir."""\n') + f.write(" try:\n") + f.write(' return ctypes.CDLL("libts.so", ctypes.RTLD_GLOBAL)\n') + f.write(" except OSError:\n") + f.write(" pass\n") + f.write(" try:\n") + f.write(" import subprocess\n") + f.write(" libdir = subprocess.check_output(\n") + f.write(' ["pkg-config", "--variable=libdir", "ts"],\n') + f.write(" stderr=subprocess.DEVNULL,\n") + f.write(" ).decode().strip()\n") + f.write( + ' return ctypes.CDLL(os.path.join(libdir, "libts.so"), ctypes.RTLD_GLOBAL)\n' + ) + f.write(" except (OSError, subprocess.CalledProcessError):\n") + f.write(" pass\n") + f.write(" build_dir = os.path.join(\n") + f.write( + " os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),\n" + ) + f.write(' "build",\n') + f.write(" )\n") + f.write(' lib_path = os.path.join(build_dir, "libts.so")\n') + f.write(" if os.path.exists(lib_path):\n") + f.write(" return ctypes.CDLL(lib_path, ctypes.RTLD_GLOBAL)\n") + f.write( + ' raise OSError("Cannot find libts.so. Run make install or build from source.")\n\n\n' + ) + + f.write("__version__ = '0.1.0'\n\n") + f.write("_lib = _find_lib()\n\n") + + # --- Helpers --- + f.write("_CD = ctypes.c_double\n\n\n") + + f.write("def _cp(arr):\n") + f.write(' """Copy numpy array to ctypes buffer (tracked in TLS list)."""\n') + f.write(" buf = (_CD * len(arr))(*arr)\n") + f.write(" if not hasattr(_tls, 'bufs'):\n") + f.write(" _tls.bufs = []\n") + f.write(" _tls.bufs.append(buf)\n") + f.write(" return buf\n\n\n") + + f.write("def _clr():\n") + f.write(' """Clear tracked ctypes buffers."""\n') + f.write(" try:\n") + f.write(" if hasattr(_tls, 'bufs'):\n") + f.write(" _tls.bufs.clear()\n") + f.write(" except Exception:\n") + f.write(" pass\n\n\n") + + # --- Parameter structs --- + f.write("# --- Parameter structs ---\n") + seen_structs = set() + for fm in sorted(funcs, key=lambda x: x.name): + if fm.struct_fields and fm.name not in seen_structs: + cls_name = f"ts_{fm.name}_params_t" + f.write(f"class {cls_name}(ctypes.Structure):\n") + f.write(" _fields_ = [\n") + for c_fname, py_fname, pytype in fm.struct_fields: + f.write(f' ("{py_fname}", {pytype}),\n') + f.write(" ]\n\n") + seen_structs.add(fm.name) + + # --- FUNC_META table --- + f.write("# --- Function metadata table ---\n") + f.write( + "# Each entry: (name, x_n, y_n, struct_class_or_None, [(param_name, default), ...])\n" + ) + f.write("FUNC_META = [\n") + for fm in sorted(funcs, key=lambda x: x.name): + struct_cls = None + params = [] + if fm.struct_fields: + struct_cls = f"ts_{fm.name}_params_t" + for c_fname, py_fname, pytype in fm.struct_fields: + d = _py_default(pytype, py_fname, fm.name) + params.append(f" ('{py_fname}', {d!r}),") + f.write( + f" ({fm.name!r}, {fm.x_n}, {fm.y_n}, {struct_cls}, [{chr(10).join(params)}]),\n" + ) + f.write("]\n\n") + + # --- Single dispatch function --- + f.write("# --- Dispatch ---\n") + f.write("def _dispatch(meta, *x_arrays):\n") + f.write(' """Call a ts indicator via ctypes, given a FUNC_META entry."""\n') + f.write(" name, x_n, y_n, struct_cls, param_defaults = meta\n") + f.write(" if len(x_arrays) != x_n:\n") + f.write( + ' raise TypeError(f"Expected {x_n} input arrays, got {len(x_arrays)}")\n' + ) + f.write(" if x_n > 0:\n") + f.write(" n = x_arrays[0].shape[0]\n") + f.write(" for a in x_arrays:\n") + f.write(" if a.shape[0] != n:\n") + f.write( + ' raise ValueError(f"Array length mismatch: expected {n}, got {a.shape[0]}")\n' + ) + f.write(" else:\n") + f.write(" n = 0\n") + f.write(" y = [np.zeros(n, dtype=np.float64) for _ in range(y_n)]\n") + f.write(" try:\n") + f.write(" x_bufs = [_cp(x_arrays[i]) for i in range(x_n)]\n") + f.write( + " y_ptrs = [y[i].ctypes.data_as(ctypes.POINTER(ctypes.c_double)) for i in range(y_n)]\n" + ) + f.write(" c_func = getattr(_lib, f'ts_{name}_flat')\n") + f.write(" if struct_cls is not None:\n") + f.write(" _p = struct_cls()\n") + f.write(" for k, v in param_defaults:\n") + f.write(" setattr(_p, k, v)\n") + f.write( + " ret = c_func(*(y_ptrs + x_bufs + [ctypes.c_uint64(n), ctypes.byref(_p)]))\n" + ) + f.write(" else:\n") + f.write( + " ret = c_func(*(y_ptrs + x_bufs + [ctypes.c_uint64(n), None]))\n" + ) + f.write(" if ret != 0:\n") + f.write( + ' raise RuntimeError(f"ts_{name} failed with return code {ret}")\n' + ) + f.write(" return y[0] if y_n == 1 else tuple(y)\n") + f.write(" finally:\n") + f.write(" _clr()\n\n\n") + + # --- Helper for param override --- + f.write( + "def _dispatch_with_params(name, x_n, y_n, struct_cls, _p, x_arrays):\n" + ) + f.write(' """Call indicator with a pre-built params struct."""\n') + f.write(" if len(x_arrays) != x_n:\n") + f.write( + ' raise TypeError(f"Expected {x_n} input arrays, got {len(x_arrays)}")\n' + ) + f.write(" n = x_arrays[0].shape[0] if x_n > 0 else 0\n") + f.write(" y = [np.zeros(n, dtype=np.float64) for _ in range(y_n)]\n") + f.write(" try:\n") + f.write(" x_bufs = [_cp(x_arrays[i]) for i in range(x_n)]\n") + f.write( + " y_ptrs = [y[i].ctypes.data_as(ctypes.POINTER(ctypes.c_double)) for i in range(y_n)]\n" + ) + f.write(" c_func = getattr(_lib, f'ts_{name}_flat')\n") + f.write( + " ret = c_func(*(y_ptrs + x_bufs + [ctypes.c_uint64(n), ctypes.byref(_p)]))\n" + ) + f.write(" if ret != 0:\n") + f.write( + ' raise RuntimeError(f"ts_{name} failed with return code {ret}")\n' + ) + f.write(" return y[0] if y_n == 1 else tuple(y)\n") + f.write(" finally:\n") + f.write(" _clr()\n\n\n") + + # --- Dynamic attribute access --- + f.write("# --- Dynamic attribute access ---\n") + f.write("_FUNC_MAP = {m[0]: m for m in FUNC_META}\n\n\n") + + f.write("def __getattr__(name):\n") + f.write(' """Return a callable wrapper for indicator `name`."""\n') + f.write(" if name.startswith('_'):\n") + f.write( + ' raise AttributeError(f"module {__name__!r} has no attribute {name!r}")\n' + ) + f.write(" meta = _FUNC_MAP.get(name)\n") + f.write(" if meta is None:\n") + f.write( + ' raise AttributeError(f"{name!r} is not a known ts indicator")\n' + ) + f.write(" name_s, x_n, y_n, struct_cls, param_defaults = meta\n") + f.write(" def wrapper(*x_arrays, **kw):\n") + f.write(" for a in x_arrays:\n") + f.write(" if not isinstance(a, np.ndarray):\n") + f.write( + ' raise TypeError(f"Expected numpy.ndarray, got {type(a).__name__}")\n' + ) + f.write(" if struct_cls is not None and kw:\n") + f.write(" _p = struct_cls()\n") + f.write(" for k, v in kw.items():\n") + f.write(" if hasattr(_p, k):\n") + f.write(" setattr(_p, k, v)\n") + f.write(" else:\n") + f.write( + ' raise TypeError(f"Unknown parameter {k!r} for {name_s}")\n' + ) + f.write( + " return _dispatch_with_params(name_s, x_n, y_n, struct_cls, _p, x_arrays)\n" + ) + f.write(" return _dispatch(meta, *x_arrays)\n") + f.write(" return wrapper\n\n\n") + + # --- __all__ --- + f.write("__all__ = [m[0] for m in FUNC_META]\n") + + print(f"Generated {len(funcs)} entries -> {OUT}") + py_compile.compile(OUT, doraise=True) + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_flat.py b/scripts/gen_flat.py new file mode 100644 index 0000000..c659432 --- /dev/null +++ b/scripts/gen_flat.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Generate flat-array C wrappers for all ts functions. + +Each wrapper takes flat input/output arrays instead of pointer-of-pointers, +eliminating ctypes pointer marshaling overhead. +""" + +import argparse +import importlib.util +import os +import sys + +_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), +) +if _spec and _spec.loader: + _ts_parser = importlib.util.module_from_spec(_spec) + _spec.loader.exec_module(_ts_parser) + parse_all = _ts_parser.parse_all +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = os.path.join(ROOT, "lib", "ts_flat_generated.c") + + +def generate_wrappers(funcs, dry_run=False): + """Generate flat-array C wrapper functions.""" + if dry_run: + print(f"[dry-run] Would generate {len(funcs)} flat wrappers -> {OUT}") + return + + with open(OUT, "w", encoding="utf-8") as f: + f.write("/* Auto-generated flat-array wrappers. Do not edit. */\n\n") + f.write("#include <string.h>\n") + f.write('#include "ts.h"\n\n') + + for fm in funcs: + safe_x_n = max(fm.x_n, 1) + safe_y_n = max(fm.y_n, 1) + + f.write(f"/* {fm.name}: x_n={fm.x_n}, y_n={fm.y_n}, c_n={fm.c_n} */\n") + f.write(f"int ts_{fm.name}_flat(\n") + + for i in range(fm.y_n): + f.write(f" fp_t *y{i},\n") + + for i in range(fm.x_n): + f.write(f" fp_t const *x{i},\n") + + f.write(f" uint_t n,\n") + f.write(f" ts_constant_t const *params\n") + f.write(f")\n{{\n") + + f.write(f" fp_t * const _y[{safe_y_n}] = {{") + f.write(", ".join(f"y{i}" for i in range(fm.y_n))) + f.write("};\n") + + f.write(f" fp_t const * const _x[{safe_x_n}] = {{") + f.write(", ".join(f"x{i}" for i in range(fm.x_n))) + f.write("};\n") + + for i in range(fm.y_n): + f.write(f" memset(y{i}, 0, n * sizeof(fp_t));\n") + + f.write( + f" return ts_{fm.name}(_y, {fm.y_n}, _x, {fm.x_n}, n, params);\n" + ) + f.write("}\n\n") + + +def main(): + parser = argparse.ArgumentParser(description="Generate flat-array C wrappers") + parser.add_argument( + "--dry-run", + action="store_true", + help="Print what would be generated without writing", + ) + args = parser.parse_args() + + funcs = parse_all(detect_x_access=True) + + if not funcs: + print("Warning: no ts_parameter declarations found.", file=sys.stderr) + sys.exit(1) + + generate_wrappers(funcs, dry_run=args.dry_run) + + if not args.dry_run: + print(f"Generated {len(funcs)} flat wrappers -> {OUT}") + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_fuzz.py b/scripts/gen_fuzz.py new file mode 100644 index 0000000..a1d20e3 --- /dev/null +++ b/scripts/gen_fuzz.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Generate fuzz table entries from ts indicator metadata. + +Parses lib/*.c via ts_parser to extract function names and input/output +counts, then generates a C source file with the complete fuzz table. +""" + +import argparse +import importlib.util +import os +import sys + +_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), +) +if _spec and _spec.loader: + _ts_parser = importlib.util.module_from_spec(_spec) + _spec.loader.exec_module(_ts_parser) + parse_all = _ts_parser.parse_all +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + +OUT = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "test", + "fuzz_table_generated.c", +) + + +def generate_table(funcs, dry_run=False): + """Generate the fuzz table C source file.""" + if dry_run: + print(f"[dry-run] Would generate {len(funcs)} fuzz entries -> {OUT}") + return + + with open(OUT, "w", encoding="utf-8") as f: + f.write("/* Auto-generated fuzz table. Do not edit. */\n\n") + f.write('#include "ts.h"\n\n') + f.write("static const fuzz_entry_t _fuzz_table_generated[] = {\n") + + for fm in funcs: + f.write(f" {{ts_{fm.name}, {fm.x_n}, {fm.y_n}}},\n") + + f.write("};\n\n") + f.write( + "static const uint_t _fuzz_generated_n = sizeof(_fuzz_table_generated) / sizeof(_fuzz_table_generated[0]);\n" + ) + + +def main(): + parser = argparse.ArgumentParser(description="Generate fuzz table from registry") + parser.add_argument( + "--dry-run", + action="store_true", + help="Print what would be generated without writing", + ) + args = parser.parse_args() + + funcs = parse_all(detect_x_access=True) + + if not funcs: + print("Warning: no ts_parameter declarations found.", file=sys.stderr) + sys.exit(1) + + generate_table(funcs, dry_run=args.dry_run) + + if not args.dry_run: + print(f"Generated {len(funcs)} fuzz entries -> {OUT}") + + +if __name__ == "__main__": + main() diff --git a/scripts/gen_registry.py b/scripts/gen_registry.py new file mode 100644 index 0000000..e80759e --- /dev/null +++ b/scripts/gen_registry.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Generate ts_registry_generated.c with registry array and getter functions.""" + +import importlib.util +import os +import re +import sys + +_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), +) +if _spec and _spec.loader: + ts_parser = importlib.util.module_from_spec(_spec) + _spec.loader.exec_module(ts_parser) + parse_all = ts_parser.parse_all +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = os.path.join(ROOT, "lib", "ts_registry_generated.c") + + +def _resolve_params_size(params_size, name): + """Resolve params_size to a compile-time constant. + + If params_size is '0', keep it. If it's sizeof(ts_X_params_t), check + if the struct is visible in ts.h. If not, compute size from definition. + """ + if params_size == "0": + return "0" + + header_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "ts.h") + try: + with open(header_path, encoding="utf-8") as hf: + hcontent = hf.read() + if re.search(r"ts_" + re.escape(name) + r"_params_t", hcontent): + return params_size + except FileNotFoundError: + pass + # Struct not in header, compute size from definition + import glob + + lib_dir = os.path.join(ROOT, "lib") + for cfile in glob.glob(os.path.join(lib_dir, "*.c")): + try: + with open(cfile, encoding="utf-8") as f: + content = f.read() + except OSError: + continue + struct_m = re.search( + r"typedef\s+struct\s*\{([^}]+)\}\s+ts_" + re.escape(name) + r"_params_t", + content, + ) + if struct_m: + body = struct_m.group(1) + n_fp = len(re.findall(r"fp_t\s+", body)) + n_uint = len(re.findall(r"uint_t\s+", body)) + return str(n_fp * 8 + n_uint * 8) + return "0" + + +def main(): + funcs = parse_all(detect_x_access=False) + + if not funcs: + print("Warning: no ts_parameter declarations found.", file=sys.stderr) + sys.exit(1) + + with open(OUT, "w", encoding="utf-8") as f: + f.write("/* Auto-generated function registry. Do not edit. */\n\n") + f.write('#include "ts.h"\n\n') + f.write(f"static const uint_t _ts_fn_count = {len(funcs)};\n\n") + f.write("static const ts_fn_info_t _ts_registry[] = {\n") + for fm in funcs: + ps = _resolve_params_size(fm.params_size, fm.name) + f.write( + f" {{ .n_inputs = {fm.x_n}, .n_outputs = {fm.y_n}, " + f".n_constants = {fm.c_n}, .params_size = {ps} }},\n" + ) + f.write("};\n\n") + f.write("static const char * const _ts_fn_names[] = {\n") + for fm in funcs: + f.write(f' "{fm.name}",\n') + f.write("};\n\n") + + # Getter functions + f.write("uint_t ts_fn_count( void ) { return _ts_fn_count; }\n\n") + f.write("const ts_fn_info_t* ts_registry_get( uint_t i )\n") + f.write("{\n") + f.write(" return i < _ts_fn_count ? &_ts_registry[i] : NULL;\n") + f.write("}\n\n") + f.write("const char* ts_fn_name_get( uint_t i )\n") + f.write("{\n") + f.write(" return i < _ts_fn_count ? _ts_fn_names[i] : NULL;\n") + f.write("}\n") + + print(f"Generated {len(funcs)} entries -> {OUT}") + + +if __name__ == "__main__": + main() diff --git a/scripts/ts_parser.py b/scripts/ts_parser.py new file mode 100644 index 0000000..2ad3629 --- /dev/null +++ b/scripts/ts_parser.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Shared parser for ts indicator metadata. + +Parses lib/*.c for ts_parameter_t declarations and extracts: +- name, n_inputs, n_outputs, n_constants +- Actual x[N] access count (may exceed declared n_inputs) +- Parameter struct fields + +All generators should import this instead of duplicating regex logic. +""" + +import glob +import os +import re +import warnings + +LIB_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "lib" +) + +SKIP_PREFIXES = ("ts_flat", "ts_registry", "gen_", "update_") +SKIP_NAMES = {"test"} + +PY_RESERVED_NAMES = {"lambda": "lambda_", "type": "type_", "id": "id_"} + +VOLUME_INDEX = 4 +OHLCV_COUNT = 5 + + +class FuncMeta: + """Metadata for a single ts indicator function.""" + + __slots__ = ("name", "x_n", "y_n", "c_n", "params_size", "struct_fields") + + def __init__(self, name, x_n, y_n, c_n, params_size="0", struct_fields=None): + self.name = name + self.x_n = x_n + self.y_n = y_n + self.c_n = c_n + self.params_size = params_size + self.struct_fields = struct_fields or [] + + +def _detect_actual_x_n(content, match_end, name): + """Detect actual x[N] access in function body.""" + remaining = content[match_end:] + func_def = re.search(rf"\bts_{name}\s*\([^)]*\)\s*{{", remaining) + if not func_def: + return 0 + brace_pos = remaining.find("{", func_def.start()) + depth = 0 + func_body = "" + for i in range(brace_pos, len(remaining)): + if remaining[i] == "{": + depth += 1 + elif remaining[i] == "}": + depth -= 1 + if depth == 0: + func_body = remaining[brace_pos : i + 1] + break + indices = [ + int(xm.group(1)) for xm in re.finditer(r"x\s*\[\s*(\d+)\s*\]", func_body) + ] + return max(indices) + 1 if indices else 0 + + +def _parse_struct_fields(content, name): + """Parse parameter struct fields for a function.""" + struct_m = re.search( + r"typedef\s+struct\s*\{([^}]+)\}\s+ts_" + re.escape(name) + r"_params_t", + content, + ) + if not struct_m: + return [] + fields_text = struct_m.group(1) + fields_text = re.sub(r"/\*.*?\*/", "", fields_text, flags=re.DOTALL) + fields_text = re.sub(r"//.*", "", fields_text) + fields = [] + for fm in re.finditer(r"^\s*(\w+)\s+(\w+)\s*;", fields_text, re.MULTILINE): + ctype, c_fname = fm.group(1), fm.group(2) + if ctype == "uint_t": + pytype = "ctypes.c_uint64" + elif ctype == "fp_t": + pytype = "ctypes.c_double" + else: + warnings.warn( + f"Unknown type '{ctype}' in struct ts_{name}_params_t, defaulting to c_double" + ) + pytype = "ctypes.c_double" + py_fname = PY_RESERVED_NAMES.get(c_fname, c_fname) + fields.append((c_fname, py_fname, pytype)) + return fields + + +def parse_all(detect_x_access=True): + """Parse all ts indicator functions from lib/*.c. + + Args: + detect_x_access: If True, detect actual x[N] access in function body + and adjust x_n accordingly. + + Returns: + List of FuncMeta objects. + """ + funcs = [] + for cfile in sorted(glob.glob(os.path.join(LIB_DIR, "*.c"))): + basename = os.path.basename(cfile) + stem = basename.replace(".c", "") + if any(stem.startswith(p) for p in SKIP_PREFIXES) or stem in SKIP_NAMES: + continue + try: + with open(cfile, encoding="utf-8") as f: + content = f.read() + except OSError as e: + warnings.warn(f"Could not read {cfile}: {e}") + continue + + for m in re.finditer( + r"ts_parameter_t\s+const\s+ts_(\w+)_p\s*=\s*\{\s*" + r"\.n_inputs\s*=\s*(\d+)\s*,\s*" + r"\.n_outputs\s*=\s*(\d+)\s*,\s*" + r"\.n_constants\s*=\s*(\d+)" + r"(?:\s*,\s*\.params_size\s*=\s*(\S+?))?\s*\}", + content, + ): + name = m.group(1) + x_n = int(m.group(2)) + y_n = int(m.group(3)) + c_n = int(m.group(4)) + params_size = m.group(5) or "0" + + if detect_x_access: + actual = _detect_actual_x_n(content, m.end(), name) + if actual > x_n: + x_n = actual + if x_n < OHLCV_COUNT and re.search( + r"\bx\s*\[\s*" + str(VOLUME_INDEX) + r"\s*\]", + content[m.end() :], + ): + x_n = OHLCV_COUNT + + struct_fields = _parse_struct_fields(content, name) if c_n > 0 else [] + + funcs.append(FuncMeta(name, x_n, y_n, c_n, params_size, struct_fields)) + + return funcs + + +def parse_names_only(): + """Parse function names and basic metadata (no x access detection). + + Returns: + Dict mapping name -> {"x_n": int, "y_n": int}. + """ + meta = {} + for fm in parse_all(detect_x_access=True): + if fm.name not in meta: + meta[fm.name] = {"x_n": fm.x_n, "y_n": fm.y_n} + return meta diff --git a/test/absolute_value.c b/test/absolute_value.c new file mode 100644 index 0000000..2180501 --- /dev/null +++ b/test/absolute_value.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_absolute_value( void ) +{ + fp_t const x0[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + fp_t const e0[] = { 4, 3, 2, 1, 0, 1, 2, 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_absolute_value, ts_absolute_value_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_absolute_value_p); +} diff --git a/test/acceleration_bands.c b/test/acceleration_bands.c new file mode 100644 index 0000000..5911e8b --- /dev/null +++ b/test/acceleration_bands.c @@ -0,0 +1,28 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_acceleration_bands( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, offset=2, y_n=8 */ + /* Rolling HH3: 12,13,14,15,16,17,18,19,20,21 */ + /* Rolling LL3: 9,9,9,10,11,12,13,14,15,16 */ + /* TP = (H+L+C)/3: 10.667,11.667,12.667,13.667,14.667,15.667,16.667,17.667,18.667,19.667 */ + /* SMA3(HH3): 13,14,15,16,17,18,19,20 */ + /* SMA3(TP): 11.667,12.667,13.667,14.667,15.667,16.667,17.667,18.667 */ + /* SMA3(LL3): 9,9.333,10,11,12,13,14,15 */ + fp_t const eu[] = { 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const em[] = { 11.66667, 12.66667, 13.66667, 14.66667, 15.66667, 16.66667, 17.66667, 18.66667 }; + fp_t const el[] = { 9, 9.33333, 10, 11, 12, 13, 14, 15 }; + fp_t const * const exp[3] = { eu, em, el }; + + uint_t k = 3; + return ts_test(ts_acceleration_bands, ts_acceleration_bands_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_acceleration_bands_p); +} diff --git a/test/acosh.c b/test/acosh.c new file mode 100644 index 0000000..c9ecfd1 --- /dev/null +++ b/test/acosh.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_acosh( void ) +{ + fp_t const x0[] = { 1.000000, 1.500000, 2.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 0.000000, 0.962424, 1.316958, 1.762747, 2.292432 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + bool result = ts_test(ts_acosh, ts_acosh_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_acosh_p); + + fp_t const xb[] = { 0.000000, -1.000000, 0.500000 }; + + for( uint_t i = 0; i < sizeof( xb ) / sizeof( *xb ); ++i ) + { + fp_t tmp = 0; + fp_t const * const xi[1] = { &xb[i] }; + fp_t * const yi[1] = { &tmp }; + ts_acosh(yi, 1, xi, 1, 1, NULL); + result &= isnan( tmp ); + } + + return result; +} diff --git a/test/ad.c b/test/ad.c new file mode 100644 index 0000000..677068f --- /dev/null +++ b/test/ad.c @@ -0,0 +1,18 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ad( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t const e[] = { 50, 150, -150, -16.666666666666668, -266.66666666666669 }; + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_ad, ts_ad_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_ad_p); +} diff --git a/test/ad_oscillator.c b/test/ad_oscillator.c new file mode 100644 index 0000000..623fc44 --- /dev/null +++ b/test/ad_oscillator.c @@ -0,0 +1,46 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ad_oscillator( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t fast = 3; + fp_t slow = 5; + fp_t a_fast = 1.0 / (fp_t)fast; + fp_t a_slow = 1.0 / (fp_t)slow; + + fp_t ad = 0; + fp_t ema_f = 0; + fp_t ema_s = 0; + + fp_t hl0 = h[0] - l[0]; + fp_t mf0 = hl0 != 0 ? ( (c[0] - l[0]) - (h[0] - c[0]) ) / hl0 * v[0] : 0; + ad = mf0; + ema_f = ad; + ema_s = ad; + + fp_t e[5] = { 0 }; + + for( uint_t i = 1; i < 5; ++i ) + { + fp_t hl = h[i] - l[i]; + fp_t mf = hl != 0 ? ( (c[i] - l[i]) - (h[i] - c[i]) ) / hl * v[i] : 0; + ad += mf; + ema_f = ema_f + a_fast * ( ad - ema_f ); + ema_s = ema_s + a_slow * ( ad - ema_s ); + e[i] = ema_f - ema_s; + } + + fp_t const * const e_arr[1] = { e }; + + fp_t constants[2] = { fast, slow }; + + return ts_test(ts_ad_oscillator, ts_ad_oscillator_offset, x, e_arr, 5, (ts_constant_t *)constants, &ts_ad_oscillator_p); +} diff --git a/test/addition.c b/test/addition.c new file mode 100644 index 0000000..a913da7 --- /dev/null +++ b/test/addition.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_addition( void ) +{ + fp_t x0[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + fp_t x1[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + fp_t e0[] = { -8, -6, -4, -2, 0, 2, 4, 6, 8 }; + + fp_t const * const x[] = { x0, x1 }; + fp_t const * const e[] = { e0 }; + + return ts_test(ts_addition, ts_addition_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_addition_p); +} diff --git a/test/adx.c b/test/adx.c new file mode 100644 index 0000000..e8df67e --- /dev/null +++ b/test/adx.c @@ -0,0 +1,61 @@ +#include <math.h> +#include <stdlib.h> +#include "ts.h" + +bool ts_test_adx( void ) +{ + uint_t n = 14; + uint_t k = 3; + + /* Monotonically rising data: +DM=1 each bar, -DM=0, TR=3 */ + fp_t *x[4]; + x[0] = malloc( n * sizeof( fp_t ) ); + x[1] = malloc( n * sizeof( fp_t ) ); + x[2] = malloc( n * sizeof( fp_t ) ); + x[3] = malloc( n * sizeof( fp_t ) ); + + for ( uint_t i = 0; i < n; ++i ) + { + x[0][i] = 10 + i; + x[1][i] = 12 + i; + x[2][i] = 9 + i; + x[3][i] = 11 + i; + } + + /* Expected: +DI=100/3, -DI=0, DX=100, ADX=100 (converged EMAs) */ + fp_t edp[] = { 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3, 100.0 / 3 }; + fp_t edm[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + fp_t edx[] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; + fp_t ead[] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; + fp_t *e[4] = { edp, edm, edx, ead }; + + uint_t offset = k + 1; + uint_t y_n = n - offset; + + /* Allocate full-size output since the function writes to absolute + * indices [offset..n-1], not compacted [0..y_n-1] */ + fp_t *y[4]; + for ( uint_t i = 0; i < 4; ++i ) + y[i] = calloc( n, sizeof( fp_t ) ); + + uint8_t c[TS_CONSTANT_SIZE] = { 0 }; + *( uint_t * )c = k; + + ts_adx(y, ts_adx_p.n_outputs, (fp_t const * const *)x, ts_adx_p.n_inputs, n, (ts_constant_t const *)c); + + bool result = true; + for ( uint_t i = 0; i < 4; ++i ) + { + for ( uint_t j = 0; j < y_n; ++j ) + { + if ( fabs( y[i][offset + j] - e[i][j] ) > TS_TEST_EPSILON ) + result = false; + } + } + + for ( uint_t i = 0; i < 4; ++i ) + free( y[i] ); + free( x[0] ); free( x[1] ); free( x[2] ); free( x[3] ); + + return result; +} diff --git a/test/alligator.c b/test/alligator.c new file mode 100644 index 0000000..0288d1a --- /dev/null +++ b/test/alligator.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_alligator( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; + + fp_t const * const xp[1] = { x }; + + /* offset=12, y_n=12, comparison indices 12..23 */ + /* Jaw (shift 8) writes at indices 8..19 → overlap 12..19 (8 values) */ + /* Teeth (shift 5) writes at indices 5..21 → overlap 12..21 (10 values) */ + /* Lips (shift 3) writes at indices 3..22 → overlap 12..22 (11 values) */ + fp_t const ej[] = { 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 0, 0, 0, 0 }; + fp_t const et[] = { 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 0, 0 }; + fp_t const el[] = { 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 0 }; + fp_t const * const exp[3] = { ej, et, el }; + + return ts_test(ts_alligator, ts_alligator_offset, xp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_alligator_p); +} diff --git a/test/angle_deg_to_rad.c b/test/angle_deg_to_rad.c new file mode 100644 index 0000000..2e73988 --- /dev/null +++ b/test/angle_deg_to_rad.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_angle_deg_to_rad( void ) +{ + fp_t const x0[] = { 0.000000, 45.000000, 90.000000, 180.000000, 270.000000, 360.000000 }; + fp_t const e0[] = { 0.000000, 0.785398, 1.570796, 3.141593, 4.712389, 6.283185 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_angle_deg_to_rad, ts_angle_deg_to_rad_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_angle_deg_to_rad_p); +} diff --git a/test/angle_normalize.c b/test/angle_normalize.c new file mode 100644 index 0000000..bdfed2e --- /dev/null +++ b/test/angle_normalize.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_angle_normalize( void ) +{ + fp_t const x0[] = { 0.0, PI, -PI, 2*PI, -2*PI, 3*PI, -3*PI }; + fp_t const e0[] = { 0.0, PI, -PI, 0.0, 0.0, PI, -PI }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_angle_normalize, ts_angle_normalize_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_angle_normalize_p); +} diff --git a/test/angle_rad_to_deg.c b/test/angle_rad_to_deg.c new file mode 100644 index 0000000..899895e --- /dev/null +++ b/test/angle_rad_to_deg.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_angle_rad_to_deg( void ) +{ + fp_t const x0[] = { 0.0, PI/4, PI/3, PI/2, PI, 2*PI }; + fp_t const e0[] = { 0.0, 45.0, 60.0, 90.0, 180.0, 360.0 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_angle_rad_to_deg, ts_angle_rad_to_deg_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_angle_rad_to_deg_p); +} diff --git a/test/annualized_historical_volatility.c b/test/annualized_historical_volatility.c new file mode 100644 index 0000000..69a881e --- /dev/null +++ b/test/annualized_historical_volatility.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_annualized_historical_volatility( void ) +{ + fp_t const x0[] = { 100, 101, 100, 102, 101, 100, 103, 101, 102, 100 }; + fp_t const * const x[] = { x0 }; + + fp_t const e0[] = { 0.196453, 0.222285, 0.222285, 0.295293, 0.337649, 0.320724, 0.221194, 0 }; + + fp_t const * const e[1] = { e0 }; + + uint_t k = 3; + return ts_test(ts_annualized_historical_volatility, ts_annualized_historical_volatility_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_annualized_historical_volatility_p); +} diff --git a/test/apo.c b/test/apo.c new file mode 100644 index 0000000..7af9f5e --- /dev/null +++ b/test/apo.c @@ -0,0 +1,41 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_apo( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + fp_t const * const inp[1] = { x }; + + /* fast=3, slow=5: fast_a=1/3, slow_a=1/5 */ + /* fast EMA: 10, 10.333, 10.889, 11.593, 12.395, 13.263, 14.176, 15.117, 16.078, 17.052 */ + /* slow EMA: 10, 10.2, 10.44, 10.752, 11.131, 11.575, 12.085, 12.666, 13.326, 14.062 */ + /* APO: 0.1333, 0.4481, 0.8405, 1.2634, 1.6876, 2.0930, 2.4545, 2.7521, 2.9901 */ + + fp_t fast_e = 10, slow_e = 10; + fp_t fast_a = 1.0 / 3, slow_a = 1.0 / 5; + fp_t e[6] = { 0 }; + + uint_t offset = 4; + for( uint_t i = 1; i <= offset; ++i ) + { + fast_e = fast_e + fast_a * ( x[i] - fast_e ); + slow_e = slow_e + slow_a * ( x[i] - slow_e ); + } + e[0] = fast_e - slow_e; + + for( uint_t i = offset + 1; i < 10; ++i ) + { + fast_e = fast_e + fast_a * ( x[i] - fast_e ); + slow_e = slow_e + slow_a * ( x[i] - slow_e ); + e[i - offset] = fast_e - slow_e; + } + + fp_t const * const exp[1] = { e }; + + typedef struct { fp_t fast; fp_t slow; } c_t; + c_t c = { .fast = 3, .slow = 5 }; + + return ts_test(ts_apo, ts_apo_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_apo_p); +} diff --git a/test/aroon.c b/test/aroon.c new file mode 100644 index 0000000..df68637 --- /dev/null +++ b/test/aroon.c @@ -0,0 +1,21 @@ +#include "ts.h" + +bool ts_test_aroon( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, offset=2, y_n=8 */ + /* Monotonically rising H, falling L => most recent bar is always the extreme => age=0 => Aroon=100 */ + /* Function writes to y[2..9], ts_test compares y[j+offset] vs e[j] for j=0..7 */ + fp_t const eup[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + fp_t const edn[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + fp_t const * const exp[2] = { eup, edn }; + + uint_t k = 3; + return ts_test(ts_aroon, ts_aroon_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_aroon_p); +} diff --git a/test/aroon_oscillator.c b/test/aroon_oscillator.c new file mode 100644 index 0000000..6a56922 --- /dev/null +++ b/test/aroon_oscillator.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_aroon_oscillator( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, offset=2 */ + /* Rising highs, falling lows => AroonUp=100, AroonDown=100 */ + /* Oscillator = 100 - 100 = 0 */ + fp_t const e[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_aroon_oscillator, ts_aroon_oscillator_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_aroon_oscillator_p); +} diff --git a/test/asi.c b/test/asi.c new file mode 100644 index 0000000..0307a3b --- /dev/null +++ b/test/asi.c @@ -0,0 +1,35 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_asi( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 11, 14, 13, 16, 15, 18, 17, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* ASI = prev_ASI + component2 + component3 + * component2 = (term1 - term2 + term3) / denom * (H - L) + * component3 = (term1 - term2 + term3) / (2 * denom) + * term1 = C - Cprev, term2 = 0.5*(O - Op), term3 = 0.25*(C - O + Op - Cp) + * denom = max(H - Lp, Hp - L, Hp - Lp) + * + * i=0: ASI = 0 + * i=1: term1=1, term2=0.5, term3=0, denom=4, comp2=0.375, comp3=0.125 => 0.5 + * i=2: term1=-1, term2=0.5, term3=-0.5, denom=4, comp2=-1.5, comp3=-0.5 => -1.5 + * i=3: term1=3, term2=0.5, term3=0.5, denom=4, comp2=2.25, comp3=0.75 => 1.5 + * i=4: term1=-1, term2=0.5, term3=-0.5, denom=4, comp2=-1.5, comp3=-0.5 => -0.5 + * i=5: term1=3, term2=0.5, term3=0.5, denom=4, comp2=2.25, comp3=0.75 => 2.5 + * i=6: term1=-1, term2=0.5, term3=-0.5, denom=4, comp2=-1.5, comp3=-0.5 => 0.5 + * i=7: term1=3, term2=0.5, term3=0.5, denom=4, comp2=2.25, comp3=0.75 => 3.5 + * i=8: term1=-1, term2=0.5, term3=-0.5, denom=4, comp2=-1.5, comp3=-0.5 => 1.5 + * i=9: term1=3, term2=0.5, term3=0.5, denom=4, comp2=2.25, comp3=0.75 => 4.5 + */ + fp_t const e[] = { 0, 0.5, -1.5, 1.5, -0.5, 2.5, 0.5, 3.5, 1.5, 4.5 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_asi, ts_asi_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)NULL, &ts_asi_p); +} diff --git a/test/asinh.c b/test/asinh.c new file mode 100644 index 0000000..4ad6494 --- /dev/null +++ b/test/asinh.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_asinh( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { -1.443635, -0.881374, 0.000000, 0.881374, 1.443635 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_asinh, ts_asinh_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_asinh_p); +} diff --git a/test/atan2.c b/test/atan2.c new file mode 100644 index 0000000..8509e22 --- /dev/null +++ b/test/atan2.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_atan2( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 1.000000, -1.000000 }; + fp_t const x1[] = { -1.000000, 0.000000, -1.000000, 0.000000 }; + fp_t const e0[] = { 3.141593, 1.570796, 2.356194, -1.570796 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_atan2, ts_atan2_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_atan2_p); +} diff --git a/test/atanh.c b/test/atanh.c new file mode 100644 index 0000000..91a9169 --- /dev/null +++ b/test/atanh.c @@ -0,0 +1,35 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_atanh( void ) +{ + fp_t const x0[] = { -0.900000, -0.500000, 0.000000, 0.500000, 0.900000 }; + fp_t const e0[] = { -1.472219, -0.549306, 0.000000, 0.549306, 1.472219 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + bool result = ts_test(ts_atanh, ts_atanh_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_atanh_p); + + fp_t const xb[] = { -1.000000, 1.000000, -1.500000, 1.500000 }; + fp_t const eb[] = { -INFINITY, INFINITY, NAN, NAN }; + + for( uint_t i = 0; i < sizeof( xb ) / sizeof( *xb ); ++i ) + { + fp_t tmp = 0; + fp_t const * const xi[1] = { &xb[i] }; + fp_t * const yi[1] = { &tmp }; + ts_atanh(yi, 1, xi, 1, 1, NULL); + if( isinf( eb[i] ) ) + { + result &= isinf( tmp ) && signbit( tmp ) == signbit( eb[i] ); + } + else if( isnan( eb[i] ) ) + { + result &= isnan( tmp ); + } + } + + return result; +} diff --git a/test/atr.c b/test/atr.c new file mode 100644 index 0000000..fb1ad4d --- /dev/null +++ b/test/atr.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_atr( void ) +{ + fp_t const o[] = { 9, 11, 10, 12, 12, 13, 11, 14, 13, 15 }; + fp_t const h[] = { 10, 12, 11, 13, 12, 14, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 11, 10, 12, 11, 13, 12, 14 }; + fp_t const c[] = { 9, 10, 12, 11, 13, 11, 14, 12, 15, 13 }; + + fp_t const * const x[] = { o, h, l, c }; + + fp_t const e[] = { 2.0, 2.0, 2.0, 1.66667, 1.77778, 1.85185, 1.90123 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_atr, ts_atr_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_atr_p); +} diff --git a/test/atr_bands.c b/test/atr_bands.c new file mode 100644 index 0000000..2d58743 --- /dev/null +++ b/test/atr_bands.c @@ -0,0 +1,29 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_atr_bands( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0 */ + /* TR[1]=3, TR[2]=3, TR[3]=3 => ATR=3.0 */ + /* TR[4]=3, ... => ATR stays 3.0 */ + /* Middle(SMA3 of close from idx 1): (12+13+14)/3=13, then rolling */ + /* Middle: 13, 14, 15, 16, 17, 18, 19 */ + /* Upper: 19, 20, 21, 22, 23, 24, 25 */ + /* Lower: 7, 8, 9, 10, 11, 12, 13 */ + fp_t const em[] = { 13, 14, 15, 16, 17, 18, 19 }; + fp_t const eu[] = { 19, 20, 21, 22, 23, 24, 25 }; + fp_t const el[] = { 7, 8, 9, 10, 11, 12, 13 }; + fp_t const * const exp[3] = { eu, em, el }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_atr_bands, ts_atr_bands_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&c, &ts_atr_bands_p); +} diff --git a/test/atr_trailing_stops.c b/test/atr_trailing_stops.c new file mode 100644 index 0000000..5f04b14 --- /dev/null +++ b/test/atr_trailing_stops.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_atr_trailing_stops( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0, offset=3, y_n=7 */ + /* TR: all 3 => ATR=3.0 */ + /* At i=3: Mid=(15+12)/2=13.5, Upper=13.5+6=19.5, Lower=13.5-6=7.5 */ + /* At i=4: Mid=14.5, computed U=20.5, L=8.5 => trail: U=fmax(19.5,20.5)=20.5, L=fmin(7.5,8.5)=7.5 */ + /* Upper trails up, lower stays at 7.5 (only moves down) */ + fp_t const eu[] = { 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5 }; + fp_t const el[] = { 7.5, 7.5, 7.5, 7.5, 7.5, 7.5, 7.5 }; + fp_t const * const exp[2] = { eu, el }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t my_c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_atr_trailing_stops, ts_atr_trailing_stops_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&my_c, &ts_atr_trailing_stops_p); +} diff --git a/test/average.c b/test/average.c new file mode 100644 index 0000000..8652a99 --- /dev/null +++ b/test/average.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average( void ) +{ + uint_t const c = 3; + + fp_t const x0[] = { 2, 4, 6, 8, 10 }; + fp_t const e0[] = { 4, 6, 8 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average, ts_average_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_average_p); +} diff --git a/test/average_cumulative.c b/test/average_cumulative.c new file mode 100644 index 0000000..01986f7 --- /dev/null +++ b/test/average_cumulative.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_cumulative( void ) +{ + fp_t const x0[] = { -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + fp_t const e0[] = { -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, 0 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_cumulative, ts_average_cumulative_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_average_cumulative_p); +} diff --git a/test/average_exponential.c b/test/average_exponential.c new file mode 100644 index 0000000..2e0b50b --- /dev/null +++ b/test/average_exponential.c @@ -0,0 +1,16 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_exponential( void ) +{ + fp_t const x0[] = { -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10 }; + fp_t const e0[] = { -10, -8.75, -6.875, -4.6875, -2.34375, 0.078125, 2.5390625, 5.01953125, 7.509765625 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + fp_t c = 2; + + return ts_test(ts_average_exponential, ts_average_exponential_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_average_exponential_p); +} diff --git a/test/average_exponential_double.c b/test/average_exponential_double.c new file mode 100644 index 0000000..f78d341 --- /dev/null +++ b/test/average_exponential_double.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_exponential_double( void ) +{ + fp_t const c[] = { 0.5 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 1, 1.75, 2.75, 3.8125, 4.875 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_exponential_double, ts_average_exponential_double_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_average_exponential_double_p); +} diff --git a/test/average_exponential_triple.c b/test/average_exponential_triple.c new file mode 100644 index 0000000..baeeb31 --- /dev/null +++ b/test/average_exponential_triple.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_exponential_triple( void ) +{ + fp_t const c[] = { 0.5 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 1, 1.875, 2.9375, 4.0, 5.03125 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_exponential_triple, ts_average_exponential_triple_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_average_exponential_triple_p); +} diff --git a/test/average_price.c b/test/average_price.c new file mode 100644 index 0000000..2587783 --- /dev/null +++ b/test/average_price.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_price( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 2, 3, 4, 5, 6 }; + fp_t const x2[] = { 0, 1, 2, 3, 4 }; + fp_t const x3[] = { 3, 4, 5, 6, 7 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 1.66666666666667, 2.66666666666667, 3.66666666666667, 4.66666666666667, 5.66666666666667 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_price, ts_average_price_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_average_price_p); +} diff --git a/test/average_triangle.c b/test/average_triangle.c new file mode 100644 index 0000000..7116099 --- /dev/null +++ b/test/average_triangle.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_triangle( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5, 6 }; + fp_t const e0[] = { 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_triangle, ts_average_triangle_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_average_triangle_p); +} diff --git a/test/average_weighted.c b/test/average_weighted.c new file mode 100644 index 0000000..544af84 --- /dev/null +++ b/test/average_weighted.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_weighted( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 2.333333, 3.333333, 4.333333 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_weighted, ts_average_weighted_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_average_weighted_p); +} diff --git a/test/average_weighted_series.c b/test/average_weighted_series.c new file mode 100644 index 0000000..f6c4318 --- /dev/null +++ b/test/average_weighted_series.c @@ -0,0 +1,16 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_average_weighted_series( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const x1[] = { 2, 3, 4, 5, 6 }; + fp_t const e0[] = { 6.666667, 12.666667, 20.666667 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_average_weighted_series, ts_average_weighted_series_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_average_weighted_series_p); +} diff --git a/test/awesome_oscillator.c b/test/awesome_oscillator.c new file mode 100644 index 0000000..205bf78 --- /dev/null +++ b/test/awesome_oscillator.c @@ -0,0 +1,58 @@ +#include <stddef.h> +#include <stdlib.h> +#include <math.h> +#include "ts.h" + +bool ts_test_awesome_oscillator( void ) +{ + uint_t n = 40; + fp_t *o = calloc( n, sizeof( fp_t ) ); + fp_t *h = calloc( n, sizeof( fp_t ) ); + fp_t *l = calloc( n, sizeof( fp_t ) ); + fp_t *c = calloc( n, sizeof( fp_t ) ); + + if ( !o || !h || !l || !c ) + { + free( o ); free( h ); free( l ); free( c ); + return false; + } + + for ( uint_t i = 0; i < n; ++i ) + { + o[i] = (fp_t)(10 + i); + h[i] = (fp_t)(12 + i); + l[i] = (fp_t)(8 + i); + c[i] = (fp_t)(11 + i); + } + + fp_t *result = calloc( n, sizeof( fp_t ) ); + + ts_awesome_oscillator(&result, 1, (fp_t const * const []){ o, h, l, c }, 4, n, NULL); + + /* Compute expected EMA(5) and EMA(34) of median price */ + fp_t fast_a = 2.0 / 6.0; + fp_t slow_a = 2.0 / 35.0; + fp_t sum_fast = 0, sum_slow = 0; + for ( uint_t i = 0; i < 34; ++i ) + { + fp_t mp = (h[i] + l[i]) / 2; + sum_slow += mp; + if ( i < 5 ) sum_fast += mp; + } + fp_t ema_fast = sum_fast / 5.0; + fp_t ema_slow = sum_slow / 34.0; + for ( uint_t i = 34; i < n; ++i ) + { + fp_t mp = (h[i] + l[i]) / 2; + ema_fast = ema_fast + fast_a * ( mp - ema_fast ); + ema_slow = ema_slow + slow_a * ( mp - ema_slow ); + } + + fp_t expected = ema_fast - ema_slow; + + bool pass = fabs( result[n - 1] - expected ) < 1e-10; + + free( o ); free( h ); free( l ); free( c ); free( result ); + + return pass; +} diff --git a/test/balance_of_power.c b/test/balance_of_power.c new file mode 100644 index 0000000..cff39bb --- /dev/null +++ b/test/balance_of_power.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_balance_of_power( void ) +{ + fp_t const x0[] = { 10, 10, 15, 20, 25 }; + fp_t const x1[] = { 14, 13, 18, 24, 29 }; + fp_t const x2[] = { 8, 9, 14, 21, 24 }; + fp_t const x3[] = { 12, 11, 16, 22, 27 }; + fp_t const e0[] = { 0.333333333333333, 0.25, 0.25, 0.666666666666667, 0.4 }; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_balance_of_power, ts_balance_of_power_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_balance_of_power_p); +} diff --git a/test/band_width.c b/test/band_width.c new file mode 100644 index 0000000..99dd4bf --- /dev/null +++ b/test/band_width.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_band_width( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* k=3, mult=2.0 */ + /* SD = sqrt(2/3) = 0.81650 for all windows */ + /* Width = (Upper - Lower) / Middle * 100 = (4*SD) / Middle * 100 */ + fp_t const e[] = { 29.690785, 27.216553, 25.122972, 23.328474, 21.773242, 20.412415, 19.211684, 18.144368 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t my_c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_band_width, ts_band_width_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&my_c, &ts_band_width_p); +} diff --git a/test/bench.c b/test/bench.c new file mode 100644 index 0000000..83330aa --- /dev/null +++ b/test/bench.c @@ -0,0 +1,204 @@ +#define TS_IMPLEMENTATION +#include "ts.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#define BENCH_N 10000 +#define BENCH_ITER 100 + +typedef struct { uint_t k; fp_t std_mult; } bb_params_t; +typedef struct { fp_t fast_k; fp_t slow_k; fp_t signal_k; } macd_params_t; +typedef struct { fp_t Q; fp_t R; } kalman_params_t; + +static fp_t bench_input[BENCH_N]; +static fp_t bench_output[BENCH_N]; +static fp_t bench_ohlc[4][BENCH_N]; +static fp_t bench_output_multi[4][BENCH_N]; + +static double elapsed_ms(struct timespec *start, struct timespec *end) +{ + return (end->tv_sec - start->tv_sec) * 1000.0 + (end->tv_nsec - start->tv_nsec) / 1000000.0; +} + +int main(void) +{ + ts_fill(bench_input, BENCH_N, 0, BENCH_N); + + for (uint_t c = 0; c < 4; c++) { + ts_fill(bench_ohlc[c], BENCH_N, 100 + c, 200 + c); + } + + printf("Benchmark: n=%d, iterations=%d\n", BENCH_N, BENCH_ITER); + printf("--------------------------------------------------\n"); + + struct timespec start, end; + + /* ts_average */ + { + uint_t k = 14; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_average(y, 1, x, 1, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_average", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_rsi */ + { + uint_t k = 14; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_rsi(y, 1, x, 1, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_rsi", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_deviation_standard */ + { + uint_t k = 14; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_deviation_standard(y, 1, x, 1, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_deviation_standard", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_average_exponential */ + { + uint_t k = 14; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_average_exponential(y, 1, x, 1, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_average_exponential", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_bollinger_bands */ + { + bb_params_t c = { .k = 20, .std_mult = 2.0 }; + fp_t * const y[3] = { bench_output_multi[0], bench_output_multi[1], bench_output_multi[2] }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_bollinger_bands(y, 3, x, 1, BENCH_N, (ts_constant_t *)&c); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_bollinger_bands", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_macd */ + { + macd_params_t c = { .fast_k = 12, .slow_k = 26, .signal_k = 9 }; + fp_t * const y[3] = { bench_output_multi[0], bench_output_multi[1], bench_output_multi[2] }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_macd(y, 3, x, 1, BENCH_N, (ts_constant_t *)&c); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_macd", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_atr */ + { + uint_t k = 14; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[4] = { bench_ohlc[0], bench_ohlc[1], bench_ohlc[2], bench_ohlc[3] }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_atr(y, 1, x, 4, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_atr", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_adx */ + { + uint_t k = 14; + fp_t * const y[4] = { bench_output_multi[0], bench_output_multi[1], bench_output_multi[2], bench_output_multi[3] }; + fp_t const * const x[4] = { bench_ohlc[0], bench_ohlc[1], bench_ohlc[2], bench_ohlc[3] }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_adx(y, 4, x, 4, BENCH_N, (ts_constant_t *)&k); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_adx", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_kalman_filter */ + { + kalman_params_t c = { .Q = 0.01, .R = 0.05 }; + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_kalman_filter(y, 1, x, 1, BENCH_N, (ts_constant_t *)&c); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_kalman_filter", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_absolute_value */ + { + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_absolute_value(y, 1, x, 1, BENCH_N, NULL); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_absolute_value", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_sine */ + { + fp_t * const y[1] = { bench_output }; + fp_t const * const x[1] = { bench_input }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_sine(y, 1, x, 1, BENCH_N, NULL); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_sine", ms, ms * 1000000.0 / BENCH_ITER); + } + + /* ts_addition */ + { + fp_t * const y[1] = { bench_output }; + fp_t const * const x[2] = { bench_input, bench_ohlc[0] }; + clock_gettime(CLOCK_MONOTONIC, &start); + for (uint_t i = 0; i < BENCH_ITER; i++) { + (void)ts_addition(y, 1, x, 2, BENCH_N, NULL); + } + clock_gettime(CLOCK_MONOTONIC, &end); + double ms = elapsed_ms(&start, &end); + printf("%-40s %8.2f ms (%.0f ns/call)\n", "ts_addition", ms, ms * 1000000.0 / BENCH_ITER); + } + + return 0; +} diff --git a/test/beta_pdf.c b/test/beta_pdf.c new file mode 100644 index 0000000..d8606b9 --- /dev/null +++ b/test/beta_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_beta_pdf( void ) +{ + fp_t const c[] = { 2.000000, 5.000000 }; + + fp_t const x0[] = { 0.100000, 0.200000, 0.300000, 0.400000, 0.500000 }; + fp_t const e0[] = { 1.968300, 2.457600, 2.160900, 1.555200, 0.937500 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_beta_pdf, ts_beta_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_beta_pdf_p); +} diff --git a/test/binomial.c b/test/binomial.c new file mode 100644 index 0000000..365ed6d --- /dev/null +++ b/test/binomial.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_binomial( void ) +{ + fp_t const x0[] = { 5.000000, 10.000000, 6.000000, 4.000000 }; + fp_t const x1[] = { 2.000000, 3.000000, 2.000000, 2.000000 }; + fp_t const e0[] = { 10.000000, 120.000000, 15.000000, 6.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_binomial, ts_binomial_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_binomial_p); +} diff --git a/test/binomial_pdf.c b/test/binomial_pdf.c new file mode 100644 index 0000000..2d234cc --- /dev/null +++ b/test/binomial_pdf.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_binomial_pdf( void ) +{ + /* n_trials=10 (uint_t at byte 0), p=0.5 (fp_t at byte 8) */ + typedef struct { uint_t n_trials; fp_t p; } c_t; + c_t c = { .n_trials = 10, .p = 0.5 }; + fp_t const x0[] = { 0,1,2,3,4,5,6,7,8,9,10 }; + fp_t const e0[] = { 0.000977, 0.009766, 0.043945, 0.117188, 0.205078, 0.246094, 0.205078, 0.117188, 0.043945, 0.009766, 0.000977 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_binomial_pdf, ts_binomial_pdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_binomial_pdf_p); +} diff --git a/test/bollinger_bands.c b/test/bollinger_bands.c new file mode 100644 index 0000000..37bc138 --- /dev/null +++ b/test/bollinger_bands.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_bollinger_bands( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* k=3, std_mult=2 */ + /* SMA: 11, 12, 13, 14, 15, 16, 17, 18 */ + /* SD: sqrt(2/3)=0.81650 for all windows */ + /* Upper = SMA + 2*SD, Lower = SMA - 2*SD */ + fp_t const em[] = { 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const eu[] = { 12.632993, 13.632993, 14.632993, 15.632993, 16.632993, 17.632993, 18.632993, 19.632993 }; + fp_t const el[] = { 9.367007, 10.367007, 11.367007, 12.367007, 13.367007, 14.367007, 15.367007, 16.367007 }; + fp_t const * const exp[3] = { em, eu, el }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_bollinger_bands, ts_bollinger_bands_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_bollinger_bands_p); +} diff --git a/test/bollinger_lower.c b/test/bollinger_lower.c new file mode 100644 index 0000000..5e91f97 --- /dev/null +++ b/test/bollinger_lower.c @@ -0,0 +1,16 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_bollinger_lower( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 9.367007, 10.367007, 11.367007, 12.367007, 13.367007, 14.367007, 15.367007, 16.367007 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_bollinger_lower, ts_bollinger_lower_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_bollinger_lower_p); +} diff --git a/test/bollinger_pctb.c b/test/bollinger_pctb.c new file mode 100644 index 0000000..760156f --- /dev/null +++ b/test/bollinger_pctb.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_bollinger_pctb( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* pctB = (C - Lower) / (Upper - Lower) = (C - (M - 2*SD)) / (4*SD) */ + /* At idx 2: C=12, M=11, SD=0.81650, (12 - 9.367) / 3.266 = 0.80622 */ + /* At idx 3: C=13, M=12, SD=0.81650, (13 - 10.367) / 3.266 = 0.80622 */ + fp_t const e[] = { 0.8061862178, 0.8061862178, 0.8061862178, 0.8061862178, 0.8061862178, 0.8061862178, 0.8061862178, 0.8061862178 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_bollinger_pctb, ts_bollinger_pctb_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_bollinger_pctb_p); +} diff --git a/test/bollinger_upper.c b/test/bollinger_upper.c new file mode 100644 index 0000000..701980e --- /dev/null +++ b/test/bollinger_upper.c @@ -0,0 +1,16 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_bollinger_upper( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 12.632993, 13.632993, 14.632993, 15.632993, 16.632993, 17.632993, 18.632993, 19.632993 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_bollinger_upper, ts_bollinger_upper_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_bollinger_upper_p); +} diff --git a/test/bollinger_width.c b/test/bollinger_width.c new file mode 100644 index 0000000..6a5b21f --- /dev/null +++ b/test/bollinger_width.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_bollinger_width( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* width = 2*2*SD/Middle*100 = 4*0.81650/Middle*100 */ + fp_t const e[] = { 29.690785, 27.216553, 25.122972, 23.328474, 21.773242, 20.412415, 19.211684, 18.144368 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t std_mult; } c_t; + c_t c = { .k = 3, .std_mult = 2.0 }; + return ts_test(ts_bollinger_width, ts_bollinger_width_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_bollinger_width_p); +} diff --git a/test/candlestick_formations.c b/test/candlestick_formations.c new file mode 100644 index 0000000..300b05e --- /dev/null +++ b/test/candlestick_formations.c @@ -0,0 +1,32 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_candlestick_formations( void ) +{ + fp_t const o[] = { 10, 10, 14, 11, 10, 15, 12, 13, 14, 16 }; + fp_t const h[] = { 11, 11, 15, 12, 14, 15, 13, 16, 14, 16 }; + fp_t const l[] = { 9, 9, 13, 10, 10, 13, 11, 12, 12, 14 }; + fp_t const c[] = { 10, 10, 14, 11, 13, 14, 12, 15, 13, 15 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* i=1: O=10,C=10,H=11,L=9, body=0, range=2, doji (body/range=0 < 0.1) => 1 */ + /* i=2: O=14,C=14,H=15,L=13, body=0, range=2, doji => 1 */ + /* i=3: O=11,C=11,H=12,L=10, body=0, range=2, doji => 1 */ + /* i=4: O=10,C=13,H=14,L=10, body=3, range=4, upper_wick=1, lower_wick=0, body_abs/range=0.75 */ + /* prev_body=0, not engulfing. upper_wick=1, lower_wick=0 => not hammer/star => 0 */ + /* i=5: O=15,C=14,H=15,L=13, body=-1, range=2, upper_wick=0, lower_wick=1 */ + /* prev_body=3>0, -body=1 < 3, not engulfing. upper_wick=0 < 2*1 => not star. lower_wick=1 < 2*1 => not hammer. 0 */ + /* i=6: O=12,C=12,H=13,L=11, body=0, range=2, doji => 1 */ + /* i=7: O=13,C=15,H=16,L=12, body=2, range=4, upper_wick=1, lower_wick=1 */ + /* prev_body=0, not engulfing. upper_wick=1 < 2*2, lower_wick=1 < 2*2 => 0 */ + /* i=8: O=14,C=13,H=14,L=12, body=-1, range=2, upper_wick=0, lower_wick=1 */ + /* prev_body=2>0, -body=1 < 2, not engulfing. not star/hammer => 0 */ + /* i=9: O=16,C=15,H=16,L=14, body=-1, range=2, upper_wick=0, lower_wick=1 */ + /* prev_body=-1, same direction, not engulfing. lower_wick=1 < 2*1 => 0 */ + fp_t const e[] = { 1, 1, 1, 0, 0, 1, 0, 0, 0 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_candlestick_formations, ts_candlestick_formations_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)NULL, &ts_candlestick_formations_p); +} diff --git a/test/catalan.c b/test/catalan.c new file mode 100644 index 0000000..0dda06e --- /dev/null +++ b/test/catalan.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_catalan( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 2.000000, 5.000000, 14.000000, 42.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_catalan, ts_catalan_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_catalan_p); +} diff --git a/test/cauchy_cdf.c b/test/cauchy_cdf.c new file mode 100644 index 0000000..0197133 --- /dev/null +++ b/test/cauchy_cdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cauchy_cdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.147584, 0.250000, 0.500000, 0.750000, 0.852416 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cauchy_cdf, ts_cauchy_cdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cauchy_cdf_p); +} diff --git a/test/cauchy_pdf.c b/test/cauchy_pdf.c new file mode 100644 index 0000000..e8c5f55 --- /dev/null +++ b/test/cauchy_pdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cauchy_pdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.063662, 0.159155, 0.318310, 0.159155, 0.063662 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cauchy_pdf, ts_cauchy_pdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cauchy_pdf_p); +} diff --git a/test/cbrt.c b/test/cbrt.c new file mode 100644 index 0000000..f0c0ae4 --- /dev/null +++ b/test/cbrt.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cbrt( void ) +{ + fp_t const x0[] = { -8.000000, -1.000000, 0.000000, 1.000000, 8.000000, 27.000000 }; + fp_t const e0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cbrt, ts_cbrt_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cbrt_p); +} diff --git a/test/cci.c b/test/cci.c new file mode 100644 index 0000000..21fb97c --- /dev/null +++ b/test/cci.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_cci( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3 + * TP: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 + * SMA(3) of TP: 12, 13, 14, 15, 16, 17, 18, 19 + * MAD(3) of TP around SMA: |11-12|+|12-12|+|13-12| / 3 = 2/3 for first window + * CCI = (13 - 12) / (0.015 * 2/3) = 1 / 0.01 = 100 + * Next: SMA=13, MAD = (|12-13|+|13-13|+|14-13|)/3 = 2/3, CCI = (14-13)/(0.015*2/3) = 100 + */ + fp_t const e[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_cci, ts_cci_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_cci_p); +} diff --git a/test/ceiling.c b/test/ceiling.c new file mode 100644 index 0000000..dece6c0 --- /dev/null +++ b/test/ceiling.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_ceiling( void ) +{ + fp_t const x0[] = { -4.4, -3.2, -2.2, -1.1, 0, 1.1, 2.2, 3.3, 4.4 }; + fp_t const e0[] = { -4, -3, -2, -1, 0, 2, 3, 4, 5 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_ceiling, ts_ceiling_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_ceiling_p); +} diff --git a/test/center_of_gravity.c b/test/center_of_gravity.c new file mode 100644 index 0000000..ee67df3 --- /dev/null +++ b/test/center_of_gravity.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_center_of_gravity( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + /* k=5: windows [1,2,3,4,5], [2,3,4,5,6], [3,4,5,6,7], [4,5,6,7,8] + * COG = 1 - 2*SUM(ix)/((k+1)*SUM(x)) */ + fp_t const e[] = { -0.22222, -0.16667, -0.13333, -0.11111 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_center_of_gravity, ts_center_of_gravity_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_center_of_gravity_p); +} diff --git a/test/chande_forecast_oscillator.c b/test/chande_forecast_oscillator.c new file mode 100644 index 0000000..3dad081 --- /dev/null +++ b/test/chande_forecast_oscillator.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_chande_forecast_oscillator( void ) +{ + fp_t const x[] = { 1, 3, 2, 5, 4, 7, 6, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { -25.9259259259259, -2.7777777777778, -23.0769230769231, -4.7619047619048 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_chande_forecast_oscillator, ts_chande_forecast_oscillator_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_chande_forecast_oscillator_p); +} diff --git a/test/change.c b/test/change.c new file mode 100644 index 0000000..39725e2 --- /dev/null +++ b/test/change.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_change( void ) +{ + fp_t const x0[] = { 10, 12, 14, 16, 18, 20, 22, 24 }; + fp_t const e0[] = { 60, 50, 42.8571428571429, 37.5, 33.3333333333333 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_change, ts_change_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_change_p); +} diff --git a/test/channel.c b/test/channel.c new file mode 100644 index 0000000..edbed8f --- /dev/null +++ b/test/channel.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_channel( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const cp[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, offset=2, y_n=8 */ + /* HH3 starting at index 2: max(h[0..2])=14, max(h[1..3])=14, max(h[2..4])=16, max(h[3..5])=16, max(h[4..6])=17, max(h[5..7])=18, max(h[6..8])=18, max(h[7..9])=19 */ + /* LL3 starting at index 2: min(l[0..2])=10, min(l[1..3])=10, min(l[2..4])=12, min(l[3..5])=12, min(l[4..6])=14, min(l[5..7])=14, min(l[6..8])=13, min(l[7..9])=16 */ + fp_t const eu[] = { 14, 16, 16, 17, 18, 18, 19, 20 }; + fp_t const el[] = { 9, 10, 10, 12, 12, 13, 13, 13 }; + fp_t const * const exp[2] = { eu, el }; + + uint_t k = 3; + return ts_test(ts_channel, ts_channel_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_channel_p); +} diff --git a/test/chebyshev_distance.c b/test/chebyshev_distance.c new file mode 100644 index 0000000..7bce0ee --- /dev/null +++ b/test/chebyshev_distance.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_chebyshev_distance( void ) +{ + fp_t const x0[] = { 3.000000, -5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, -2.000000 }; + fp_t const e0[] = { 4.000000, 12.000000, 5.000000, 2.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_chebyshev_distance, ts_chebyshev_distance_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_chebyshev_distance_p); +} diff --git a/test/chebyshev_t.c b/test/chebyshev_t.c new file mode 100644 index 0000000..93ed0fb --- /dev/null +++ b/test/chebyshev_t.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_chebyshev_t( void ) +{ + fp_t const x0[] = { 0.000000, 0.500000, 1.000000, 0.700000, 0.000000, 2.000000, 2.000000, 2.000000, 1.500000, -2.000000 }; + fp_t const x1[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 0.000000, 1.000000, 2.000000, 3.000000, 2.000000 }; + fp_t const e0[] = { 1.000000, 0.500000, 1.000000, -0.728000, 1.000000, 1.000000, 2.000000, 7.000000, 9.000000, 7.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_chebyshev_t, ts_chebyshev_t_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_chebyshev_t_p); +} diff --git a/test/chi_squared_pdf.c b/test/chi_squared_pdf.c new file mode 100644 index 0000000..c2f5cdb --- /dev/null +++ b/test/chi_squared_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_chi_squared_pdf( void ) +{ + fp_t const c[] = { 3.000000 }; + + fp_t const x0[] = { 0.100000, 1.000000, 2.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 0.120004, 0.241971, 0.207554, 0.154180, 0.073225 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_chi_squared_pdf, ts_chi_squared_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_chi_squared_pdf_p); +} diff --git a/test/choppiness_index.c b/test/choppiness_index.c new file mode 100644 index 0000000..fda6520 --- /dev/null +++ b/test/choppiness_index.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_choppiness_index( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; + fp_t const v[] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + /* k=3, 2k=6, offset=6, H-L=3 for all bars */ + /* At xi=6: sum_range of bars 0-2 = 9, HH6 of bars 1-6 = 18, LL6 of bars 1-6 = 10, range=8 */ + /* CI = 100 * ln(9/8) / ln(3/e) = 100 * 0.11778 / 0.04931 = 238.9 */ + fp_t const e[] = { 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107, 10.72107 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_choppiness_index, ts_choppiness_index_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_choppiness_index_p); +} diff --git a/test/close_minus_open.c b/test/close_minus_open.c new file mode 100644 index 0000000..c6030e3 --- /dev/null +++ b/test/close_minus_open.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_close_minus_open( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const x1[] = { 2, 3, 4, 5, 6 }; + fp_t const x2[] = { 0, 1, 2, 3, 4 }; + fp_t const x3[] = { 3, 5, 7, 9, 11 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 2, 3, 4, 5, 6 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_close_minus_open, ts_close_minus_open_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_close_minus_open_p); +} diff --git a/test/close_to_high.c b/test/close_to_high.c new file mode 100644 index 0000000..295eb92 --- /dev/null +++ b/test/close_to_high.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_close_to_high( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 4, 6, 8, 10, 12 }; + fp_t const x2[] = { 0, 2, 4, 6, 8 }; + fp_t const x3[] = { 2, 4, 6, 8, 10 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 0.5, 0.5, 0.5, 0.5, 0.5 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_close_to_high, ts_close_to_high_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_close_to_high_p); +} diff --git a/test/close_to_open_ratio.c b/test/close_to_open_ratio.c new file mode 100644 index 0000000..a636186 --- /dev/null +++ b/test/close_to_open_ratio.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_close_to_open_ratio( void ) +{ + fp_t const x0[] = { 2, 4, 6, 8, 10 }; + fp_t const x1[] = { 3, 5, 7, 9, 11 }; + fp_t const x2[] = { 1, 3, 5, 7, 9 }; + fp_t const x3[] = { 4, 8, 12, 16, 20 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 2, 2, 2, 2, 2 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_close_to_open_ratio, ts_close_to_open_ratio_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_close_to_open_ratio_p); +} diff --git a/test/cmf.c b/test/cmf.c new file mode 100644 index 0000000..66b4c3f --- /dev/null +++ b/test/cmf.c @@ -0,0 +1,41 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cmf( void ) +{ + uint_t k = TS_TEST_K; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t mf_vals[10] = { 0 }; + for( uint_t i = 0; i < 10; ++i ) + { + fp_t hl = h[i] - l[i]; + mf_vals[i] = hl != 0 ? ( ( c[i] - l[i] ) - ( h[i] - c[i] ) ) / hl * v[i] : 0; + } + + fp_t e[6] = { 0 }; + for( uint_t i = k - 1; i < 10; ++i ) + { + fp_t sum_mf = 0; + fp_t sum_v = 0; + for( uint_t j = 0; j < k; ++j ) + { + uint_t idx = i - k + 1 + j; + sum_mf += mf_vals[idx]; + sum_v += v[idx]; + } + e[i - k + 1] = sum_v != 0 ? sum_mf / sum_v : 0; + } + fp_t const * const e_arr[1] = { e }; + + uint_t c_val = k; + + return ts_test(ts_cmf, ts_cmf_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_cmf_p); +} diff --git a/test/cmo.c b/test/cmo.c new file mode 100644 index 0000000..00fdb66 --- /dev/null +++ b/test/cmo.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_cmo( void ) +{ + /* k=3, prices: 10, 12, 14, 13, 15, 16, 18, 17, 19, 20 */ + fp_t const x[] = { 10, 12, 14, 13, 15, 16, 18, 17, 19, 20 }; + + fp_t const * const inp[1] = { x }; + + /* i=1: diff=+2, i=2: diff=+2, i=3: diff=-1 */ + /* First window (i=1..3): up=2+2+0=4, down=0+0+1=1, CMO=100*(4-1)/(4+1)=60 */ + /* Slide: add i=4 diff=+2, remove i=1 diff=+2: up=4-2+2=4, down=1-0+0=1, CMO=60 */ + /* Slide: add i=5 diff=+1, remove i=2 diff=+2: up=4-2+1=3, down=1-0+0=1, CMO=100*(3-1)/(3+1)=50 */ + /* Slide: add i=6 diff=+2, remove i=3 diff=-1: up=3-0+2=5, down=1-1+0=0, CMO=100*(5-0)/(5+0)=100 */ + /* Slide: add i=7 diff=-1, remove i=4 diff=+2: up=5-2+0=3, down=0-0+1=1, CMO=100*(3-1)/(3+1)=50 */ + /* Slide: add i=8 diff=+2, remove i=5 diff=+1: up=3-1+2=4, down=1-0+0=1, CMO=100*(4-1)/(4+1)=60 */ + /* Slide: add i=9 diff=+1, remove i=6 diff=+2: up=4-2+1=3, down=1-0+0=1, CMO=50 */ + fp_t const e[] = { 60, 60, 50, 100, 50, 60, 50 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_cmo, ts_cmo_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_cmo_p); +} diff --git a/test/coppock_curve.c b/test/coppock_curve.c new file mode 100644 index 0000000..48239e4 --- /dev/null +++ b/test/coppock_curve.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_coppock_curve( void ) +{ + fp_t const x[] = { 100, 105, 98, 110, 102, 115, 108, 120, 106, 112, 118, 99, 113, 107, 121, 114, 109, 116, 103, 111, 119, 104, 117, 108, 122, 110, 115, 107, 121, 105 }; + + fp_t const * const inp[1] = { x }; + + /* + * Coppock = expanding WMA(ROC(14) + ROC(11)), full WMA(10) from idx 9, + * then rolling WMA(10). ROC(k) = (x[i] - x[i-k]) / x[i-k] * 100. + * WMA weights: most recent value gets highest weight. + * Offset = 14, first output at index 14. + */ + fp_t const e[] = { + 31.00000000, 27.44537815, 23.27989457, 20.98845532, 17.79356430, + 15.48386172, 14.28049689, 12.48191637, 11.72007206, 10.86293252, + 6.25323376, 3.24940269, 2.78976008, 0.81656550, 3.89753260, + 4.22501503 + }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_coppock_curve, ts_coppock_curve_offset, inp, exp, 30, (ts_constant_t *)NULL, &ts_coppock_curve_p); +} diff --git a/test/copysign.c b/test/copysign.c new file mode 100644 index 0000000..cab7a99 --- /dev/null +++ b/test/copysign.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_copysign( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 3.000000, 4.000000 }; + fp_t const x1[] = { 1.000000, -1.000000, 1.000000, -1.000000 }; + fp_t const e0[] = { 1.000000, -2.000000, 3.000000, -4.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_copysign, ts_copysign_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_copysign_p); +} diff --git a/test/correlation_coefficient.c b/test/correlation_coefficient.c new file mode 100644 index 0000000..f1054d0 --- /dev/null +++ b/test/correlation_coefficient.c @@ -0,0 +1,16 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_correlation_coefficient( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7 }; + fp_t const x1[] = { 2, 4, 6, 8, 10, 12, 14 }; + fp_t const e0[] = { 1, 1, 1, 1, 1 }; + + fp_t const * const x[] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + uint_t k = 3; + + return ts_test(ts_correlation_coefficient, ts_correlation_coefficient_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_correlation_coefficient_p); +} diff --git a/test/cosecant.c b/test/cosecant.c new file mode 100644 index 0000000..1ebe741 --- /dev/null +++ b/test/cosecant.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cosecant( void ) +{ + fp_t const x0[] = { 0.5, 1.0, 1.5, 2.0, 2.5 }; + fp_t const e0[] = { 2.085830, 1.188395, 1.002511, 1.099750, 1.670922 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cosecant, ts_cosecant_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosecant_p); +} diff --git a/test/cosecant_hyperbolic.c b/test/cosecant_hyperbolic.c new file mode 100644 index 0000000..e6426a3 --- /dev/null +++ b/test/cosecant_hyperbolic.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cosecant_hyperbolic( void ) { + fp_t const x0[] = { 0.5, 1.0, 1.5, 2.0, 2.5 }; + fp_t const e0[] = { 1.919035, 0.850918, 0.469642, 0.275721, 0.165284 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cosecant_hyperbolic, ts_cosecant_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosecant_hyperbolic_p); +} diff --git a/test/cosecant_hyperbolic_inverse.c b/test/cosecant_hyperbolic_inverse.c new file mode 100644 index 0000000..3b4fea9 --- /dev/null +++ b/test/cosecant_hyperbolic_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cosecant_hyperbolic_inverse( void ) { + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0.881374, 0.481212, 0.327450, 0.247466, 0.198690 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cosecant_hyperbolic_inverse, ts_cosecant_hyperbolic_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosecant_hyperbolic_inverse_p); +} diff --git a/test/cosecant_inverse.c b/test/cosecant_inverse.c new file mode 100644 index 0000000..02c0a2a --- /dev/null +++ b/test/cosecant_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cosecant_inverse( void ) { + fp_t const x0[] = { 2, 3, 4, 5, 10 }; + fp_t const e0[] = { 0.523599, 0.339837, 0.252680, 0.201358, 0.100167 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cosecant_inverse, ts_cosecant_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosecant_inverse_p); +} diff --git a/test/cosine.c b/test/cosine.c new file mode 100644 index 0000000..6dac0cf --- /dev/null +++ b/test/cosine.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cosine( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 1.000000, 0.877583, 0.540302, 0.070737, -0.416147, -0.801144, -0.989992, -0.936457, -0.653644 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cosine, ts_cosine_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosine_p); +} diff --git a/test/cosine_hyperbolic.c b/test/cosine_hyperbolic.c new file mode 100644 index 0000000..02cb98a --- /dev/null +++ b/test/cosine_hyperbolic.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cosine_hyperbolic( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 1.000000, 1.127626, 1.543081, 2.352410, 3.762196, 6.132289, 10.067662, 16.572825, 27.308233 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cosine_hyperbolic, ts_cosine_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosine_hyperbolic_p); +} diff --git a/test/cosine_inverse.c b/test/cosine_inverse.c new file mode 100644 index 0000000..8a16df6 --- /dev/null +++ b/test/cosine_inverse.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cosine_inverse( void ) +{ + fp_t const x0[] = { 0, 0.25, 0.5, 0.75, 1.0 }; + fp_t const e0[] = { 1.570796, 1.318116, 1.047198, 0.722734, 0 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cosine_inverse, ts_cosine_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosine_inverse_p); +} diff --git a/test/cosine_similarity.c b/test/cosine_similarity.c new file mode 100644 index 0000000..1fb36b9 --- /dev/null +++ b/test/cosine_similarity.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cosine_similarity( void ) +{ + fp_t const x0[] = { 3.000000, 0.000000, 1.000000, -1.000000 }; + fp_t const x1[] = { 4.000000, 0.000000, 1.000000, 1.000000 }; + fp_t const e0[] = { 1.000000, 0.000000, 1.000000, -1.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cosine_similarity, ts_cosine_similarity_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cosine_similarity_p); +} diff --git a/test/cotangent.c b/test/cotangent.c new file mode 100644 index 0000000..33634c5 --- /dev/null +++ b/test/cotangent.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cotangent( void ) { + fp_t const x0[] = { 0.5, 1.0, 1.5, 2.0, 2.5 }; + fp_t const e0[] = { 1.830488, 0.642093, 0.070915, -0.457658, -1.338648 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cotangent, ts_cotangent_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cotangent_p); +} diff --git a/test/cotangent_hyperbolic.c b/test/cotangent_hyperbolic.c new file mode 100644 index 0000000..b7963a4 --- /dev/null +++ b/test/cotangent_hyperbolic.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cotangent_hyperbolic( void ) { + fp_t const x0[] = { 0.5, 1.0, 1.5, 2.0, 2.5 }; + fp_t const e0[] = { 2.163953, 1.313035, 1.104791, 1.037315, 1.013567 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cotangent_hyperbolic, ts_cotangent_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cotangent_hyperbolic_p); +} diff --git a/test/cotangent_hyperbolic_inverse.c b/test/cotangent_hyperbolic_inverse.c new file mode 100644 index 0000000..6fbddff --- /dev/null +++ b/test/cotangent_hyperbolic_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cotangent_hyperbolic_inverse( void ) { + fp_t const x0[] = { 2, 3, 4, 5, 10 }; + fp_t const e0[] = { 0.549306, 0.346574, 0.255413, 0.202733, 0.100335 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cotangent_hyperbolic_inverse, ts_cotangent_hyperbolic_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cotangent_hyperbolic_inverse_p); +} diff --git a/test/cotangent_inverse.c b/test/cotangent_inverse.c new file mode 100644 index 0000000..86a1287 --- /dev/null +++ b/test/cotangent_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_cotangent_inverse( void ) { + fp_t const x0[] = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 2.944197, 2.896614, 2.819842, 2.677945, 2.356194, 1.570796, 0.785398, 0.463648, 0.321751, 0.244979, 0.197396 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_cotangent_inverse, ts_cotangent_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cotangent_inverse_p); +} diff --git a/test/crossover.c b/test/crossover.c new file mode 100644 index 0000000..3aa8f0f --- /dev/null +++ b/test/crossover.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_crossover( void ) +{ + fp_t const x0[] = { 1, 2, 4, 5, 4, 2, 1 }; + fp_t const x1[] = { 3, 3, 3, 3, 3, 3, 3 }; + fp_t const e0[] = { 0, 1, 0, 0, -1, 0 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_crossover, ts_crossover_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_crossover_p); +} diff --git a/test/cumulative_product.c b/test/cumulative_product.c new file mode 100644 index 0000000..3ae6708 --- /dev/null +++ b/test/cumulative_product.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cumulative_product( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const e0[] = { 1, 2, 6, 24, 120, 720, 5040, 40320 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cumulative_product, ts_cumulative_product_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cumulative_product_p); +} diff --git a/test/cumulative_sum.c b/test/cumulative_sum.c new file mode 100644 index 0000000..828ef20 --- /dev/null +++ b/test/cumulative_sum.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cumulative_sum( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const e0[] = { 1, 3, 6, 10, 15, 21, 28, 36 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_cumulative_sum, ts_cumulative_sum_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_cumulative_sum_p); +} diff --git a/test/cumulative_volume_delta.c b/test/cumulative_volume_delta.c new file mode 100644 index 0000000..024b8b5 --- /dev/null +++ b/test/cumulative_volume_delta.c @@ -0,0 +1,27 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cumulative_volume_delta( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[10] = { 0 }; + fp_t cum = 0; + for( uint_t i = 0; i < 10; ++i ) + { + fp_t diff = c[i] - o[i]; + fp_t sign = ( diff > 0 ) ? 1.0 : ( diff < 0 ) ? -1.0 : 0.0; + cum += v[i] * sign; + e[i] = cum; + } + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_cumulative_volume_delta, ts_cumulative_volume_delta_offset, x, e_arr, 10, (ts_constant_t *)NULL, &ts_cumulative_volume_delta_p); +} diff --git a/test/cv.c b/test/cv.c new file mode 100644 index 0000000..2c41604 --- /dev/null +++ b/test/cv.c @@ -0,0 +1,25 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_cv( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12, 13, 14, 13, 14, 15, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 15, 16, 14, 16, 17, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 11, 12, 11, 12, 13, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 14, 15, 13, 15, 16, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 150, 250, 350, 450, 550, 650 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t fast = 3; + fp_t slow = 5; + + + fp_t e[] = { -0.020148, 0.003635, -0.117257, -0.040582, 0.003017, 0.026068 }; + + fp_t const * const e_arr[1] = { e }; + + fp_t constants[2] = { fast, slow }; + + return ts_test(ts_cv, ts_cv_offset, x, e_arr, 11, (ts_constant_t *)constants, &ts_cv_p); +} diff --git a/test/darvas_box.c b/test/darvas_box.c new file mode 100644 index 0000000..af5e857 --- /dev/null +++ b/test/darvas_box.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_darvas_box( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const cp[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, offset=2, y_n=8 */ + /* HH3 starting at index 2: 14, 14, 16, 16, 17, 18, 18, 19 */ + /* LL3 starting at index 2: 10, 10, 12, 12, 14, 14, 13, 16 */ + fp_t const et[] = { 14, 16, 16, 17, 18, 18, 19, 20 }; + fp_t const eb[] = { 9, 10, 10, 12, 12, 13, 13, 13 }; + fp_t const * const exp[2] = { et, eb }; + + uint_t k = 3; + return ts_test(ts_darvas_box, ts_darvas_box_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_darvas_box_p); +} diff --git a/test/dawsn.c b/test/dawsn.c new file mode 100644 index 0000000..2e4e482 --- /dev/null +++ b/test/dawsn.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_dawsn( void ) +{ + fp_t const x0[] = { 0.000000, 0.250000, 0.500000, 1.000000, -1.000000, 2.000000 }; + fp_t const e0[] = { 0.000000, 0.239839, 0.424436, 0.538080, -0.538080, 0.301340 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_dawsn, ts_dawsn_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_dawsn_p); +} diff --git a/test/decay_exponential.c b/test/decay_exponential.c new file mode 100644 index 0000000..363dbed --- /dev/null +++ b/test/decay_exponential.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_decay_exponential( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 5, 3, 4, 2, 6, 1 }; + fp_t const e0[] = { 2.666666666666667, 6, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_decay_exponential, ts_decay_exponential_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_decay_exponential_p); +} diff --git a/test/decay_linear.c b/test/decay_linear.c new file mode 100644 index 0000000..24d0adf --- /dev/null +++ b/test/decay_linear.c @@ -0,0 +1,14 @@ +#include "ts.h" + +bool ts_test_decay_linear( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 6, 3, 4, 2, 6, 1 }; + fp_t const e0[] = { 2.66667, 6.0, 4.0 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_decay_linear, ts_decay_linear_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_decay_linear_p); +} diff --git a/test/degree_radian.c b/test/degree_radian.c new file mode 100644 index 0000000..7570121 --- /dev/null +++ b/test/degree_radian.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_degree_radian( void ) +{ + fp_t const x[] = { 0, 30, 45, 60, 90, 180, 270, 360 }; + fp_t const e[] = { 0, PI / 6, PI / 4, PI / 3, HALF_PI, PI, 3 * HALF_PI, 2 * PI }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_degree_radian, ts_degree_radian_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_degree_radian_p); +} diff --git a/test/delta.c b/test/delta.c new file mode 100644 index 0000000..d4856a6 --- /dev/null +++ b/test/delta.c @@ -0,0 +1,16 @@ +#include <stddef.h> +#include <stdio.h> +#include <tgmath.h> +#include "ts.h" + +bool ts_test_delta( void ) +{ + fp_t x0[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + fp_t e0[] = { 3, 3, 3, 3, 3, 3 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[] = { e0 }; + uint_t const c[] = { 3 }; + + return ts_test(ts_delta, ts_delta_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)c, &ts_delta_p); +} diff --git a/test/deviation_average.c b/test/deviation_average.c new file mode 100644 index 0000000..29bdab3 --- /dev/null +++ b/test/deviation_average.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_deviation_average( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0.666667, 0.666667, 0.666667 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_deviation_average, ts_deviation_average_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_deviation_average_p); +} diff --git a/test/deviation_standard.c b/test/deviation_standard.c new file mode 100644 index 0000000..e4cedb2 --- /dev/null +++ b/test/deviation_standard.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_deviation_standard( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0.816497, 0.816497, 0.816497 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_deviation_standard, ts_deviation_standard_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_deviation_standard_p); +} diff --git a/test/deviation_standard_average_exponential.c b/test/deviation_standard_average_exponential.c new file mode 100644 index 0000000..b5aa625 --- /dev/null +++ b/test/deviation_standard_average_exponential.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_deviation_standard_average_exponential( void ) +{ + fp_t const c[] = { 0.5 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 1.000000, 0.707107, 1.172604, 1.489547, 1.693277 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_deviation_standard_average_exponential, ts_deviation_standard_average_exponential_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_deviation_standard_average_exponential_p); +} diff --git a/test/deviation_vector.c b/test/deviation_vector.c new file mode 100644 index 0000000..8e4e2c6 --- /dev/null +++ b/test/deviation_vector.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_deviation_vector( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0.816497, 0.816497, 0.816497 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_deviation_vector, ts_deviation_vector_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_deviation_vector_p); +} diff --git a/test/di_minus.c b/test/di_minus.c new file mode 100644 index 0000000..35d2545 --- /dev/null +++ b/test/di_minus.c @@ -0,0 +1,20 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_di_minus( void ) +{ + fp_t const o[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + fp_t const h[] = { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3 }; + fp_t const l[] = { 8, 6, 4, 2, 0, -2, -4, -6, -8,-10 }; + fp_t const c[] = { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, -DM=2 each period, +DM=0 => -DI = 100*EMA(-DM)/(EMA(-DM)+EMA(+DM)) */ + fp_t const e[] = { 100., 100., 100., 100., 100., 100., 100. }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_di_minus, ts_di_minus_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_di_minus_p); +} diff --git a/test/di_plus.c b/test/di_plus.c new file mode 100644 index 0000000..f8667fb --- /dev/null +++ b/test/di_plus.c @@ -0,0 +1,20 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_di_plus( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, +DM=1 (H increases by 1 each bar), -DM=0, +DI = 100*EMA(+DM)/(EMA(+DM)+EMA(-DM)) = 100 */ + fp_t const e[] = { 100, 100, 100, 100, 100, 100, 100 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_di_plus, ts_di_plus_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_di_plus_p); +} diff --git a/test/digamma.c b/test/digamma.c new file mode 100644 index 0000000..c9e221c --- /dev/null +++ b/test/digamma.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_digamma( void ) +{ + fp_t const x0[] = { 10.000000, 15.000000, 20.000000, 50.000000, 100.000000 }; + fp_t const e0[] = { 2.2517525891, 2.6743466617, 2.9705239922, 3.9019896734, 4.6001618527 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_digamma, ts_digamma_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_digamma_p); +} diff --git a/test/disparity_index.c b/test/disparity_index.c new file mode 100644 index 0000000..1860f8f --- /dev/null +++ b/test/disparity_index.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_disparity_index( void ) +{ + fp_t const x0[] = { 10, 10, 10, 10, 15, 15, 15 }; + fp_t const * const x[] = { x0 }; + + uint_t k = 3; + + fp_t sma = (10 + 10 + 10) / 3.0; + fp_t e[5] = { 0 }; + e[0] = (10 - sma) / sma * 100; + sma = sma + (10 - 10) / 3.0; + e[1] = (10 - sma) / sma * 100; + sma = sma + (15 - 10) / 3.0; + e[2] = (15 - sma) / sma * 100; + sma = sma + (15 - 10) / 3.0; + e[3] = (15 - sma) / sma * 100; + sma = sma + (15 - 10) / 3.0; + e[4] = (15 - sma) / sma * 100; + + fp_t const * const exp[1] = { e }; + + return ts_test(ts_disparity_index, ts_disparity_index_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_disparity_index_p); +} diff --git a/test/distance.c b/test/distance.c new file mode 100644 index 0000000..c8466b7 --- /dev/null +++ b/test/distance.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_distance( void ) +{ + fp_t const x0[] = { 3.000000, 5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, 0.000000 }; + fp_t const e0[] = { 5.000000, 13.000000, 5.000000, 1.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_distance, ts_distance_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_distance_p); +} diff --git a/test/division.c b/test/division.c new file mode 100644 index 0000000..7442b53 --- /dev/null +++ b/test/division.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include <math.h> +#include "ts.h" + +bool ts_test_division( void ) +{ + fp_t const x0[] = { 10, 8, 6, 4 }; + fp_t const x1[] = { 2, 4, 3, 1 }; + fp_t const e0[] = { 5, 2, 2, 4 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_division, ts_division_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_division_p); +} diff --git a/test/donchian_channel.c b/test/donchian_channel.c new file mode 100644 index 0000000..4ada2d0 --- /dev/null +++ b/test/donchian_channel.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_donchian_channel( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const c[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + fp_t const v[] = { 100, 200, 150, 180, 160, 190, 210, 170, 200, 220 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + /* k=3, offset=2, y_n=8 */ + /* HH3 at index 2..9: 14, 14, 16, 16, 17, 18, 18, 19 */ + /* LL3 at index 2..9: 10, 10, 12, 12, 14, 14, 13, 16 */ + /* Mid: 12, 12, 14, 14, 15.5, 16, 15.5, 17.5 */ + fp_t const eu[] = { 14, 16, 16, 17, 18, 18, 19, 20 }; + fp_t const el[] = { 9, 10, 10, 12, 12, 13, 13, 13 }; + fp_t const em[] = { 11.5, 13.0, 13.0, 14.5, 15.0, 15.5, 16.0, 16.5 }; + fp_t const * const exp[3] = { eu, el, em }; + + uint_t k = 3; + return ts_test(ts_donchian_channel, ts_donchian_channel_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_donchian_channel_p); +} diff --git a/test/donchian_width.c b/test/donchian_width.c new file mode 100644 index 0000000..ead859f --- /dev/null +++ b/test/donchian_width.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_donchian_width( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const c[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + fp_t const v[] = { 100, 200, 150, 180, 160, 190, 210, 170, 200, 220 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + /* Width = HH3 - LL3 at index 2..9: 4, 4, 4, 4, 3, 4, 5, 3 */ + fp_t const e[] = { 5, 6, 6, 5, 6, 5, 6, 7 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_donchian_width, ts_donchian_width_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_donchian_width_p); +} diff --git a/test/dpo.c b/test/dpo.c new file mode 100644 index 0000000..e6e4560 --- /dev/null +++ b/test/dpo.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_dpo( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + fp_t const * const inp[1] = { x }; + + /* k=4, offset=3 */ + /* SMA4: 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5 */ + /* DPO[3] = close[3] - SMA4[0] = 4 - 2.5 = 1.5 */ + /* DPO[4] = close[4] - SMA4[1] = 5 - 3.5 = 1.5 */ + /* ... all 1.5 */ + + fp_t const e[] = { 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5 }; + + fp_t const * const exp[1] = { e }; + + uint_t c_val = 4; + + return ts_test(ts_dpo, ts_dpo_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c_val, &ts_dpo_p); +} diff --git a/test/dx.c b/test/dx.c new file mode 100644 index 0000000..f5ee94c --- /dev/null +++ b/test/dx.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_dx( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, all bars identical pattern */ + /* +DM=2, -DM=0, TR=3, EMA values stable at 2, 0, 3 */ + /* +DI=66.667, -DI=0, DX=100 */ + fp_t const e[] = { 100, 100, 100, 100, 100, 100, 100 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_dx, ts_dx_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_dx_p); +} diff --git a/test/ease_of_movement.c b/test/ease_of_movement.c new file mode 100644 index 0000000..fbe2e35 --- /dev/null +++ b/test/ease_of_movement.c @@ -0,0 +1,43 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ease_of_movement( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t eom[10] = { 0 }; + for( uint_t i = 1; i < 10; ++i ) + { + fp_t hl = h[i] - l[i]; + if( hl == 0 ) continue; + fp_t br = v[i] / hl; + if( br == 0 ) continue; + fp_t cur_mid = ( h[i] + l[i] ) / 2.0; + fp_t prev_mid = ( h[i - 1] + l[i - 1] ) / 2.0; + eom[i] = ( cur_mid - prev_mid ) / br; + } + + fp_t e[7] = { 0 }; + { + fp_t sum = eom[1] + eom[2] + eom[3]; + e[0] = sum / (fp_t)k; + for( uint_t i = k + 1; i < 10; ++i ) + { + sum = sum - eom[i - k] + eom[i]; + e[i - k] = sum / (fp_t)k; + } + } + + fp_t const * const e_arr[1] = { e }; + uint_t c_val = k; + + return ts_test(ts_ease_of_movement, ts_ease_of_movement_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_ease_of_movement_p); +} diff --git a/test/edge_cases.c b/test/edge_cases.c new file mode 100644 index 0000000..ce47f72 --- /dev/null +++ b/test/edge_cases.c @@ -0,0 +1,324 @@ +#include <math.h> +#include <stdbool.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_edge_cases( void ) +{ + bool result = true; + + /* === NULL input === */ + + /* average with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 0 (empty input) === */ + + /* average with n=0 should return early, no crash */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with n=0 should not crash */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_deviation_standard(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_variance(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 1 (single element) === */ + + /* average with n=1, k=1 should produce the element itself */ + { + fp_t const x[] = {5}; + fp_t const e[] = {5}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_average_p); + } + + /* deviation_standard with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === k = 0 (zero period) === */ + + /* average with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_rsi(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_deviation_standard(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_variance(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === k > n (period exceeds data) === */ + + /* average with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_average(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_deviation_standard(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_variance(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === All-zero input === */ + + /* average with all-zero input should produce zeros */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-zero input should not crash (all gains/losses are 0) */ + { + fp_t const x[] = {0, 0, 0, 0, 0, 0, 0}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === All-same input (zero variance) === */ + + /* average with all-same input should produce that value */ + { + fp_t const x[] = {7, 7, 7, 7, 7}; + fp_t const e[] = {7, 7, 7}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-same input should produce 100 (no losses) */ + { + fp_t const x[] = {5, 5, 5, 5, 5, 5, 5}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === n == k boundary === */ + + /* average with n==k should succeed (produces 1 output) */ + { + fp_t const x[] = {1, 2, 3}; + fp_t const e[] = {2}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 3, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with n==k should fail (requires n > k) */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === NaN input === */ + + /* average with NaN input should return -1 (invalid input) */ + { + fp_t x[] = {NAN, 1.0, 2.0, 3.0, 4.0}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === Monotonic input (RSI edge cases) === */ + + /* rsi with strictly ascending input should produce 100 */ + { + fp_t const x[] = {1, 2, 3, 4, 5, 6, 7}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* rsi with strictly descending input should produce 0 */ + { + fp_t const x[] = {7, 6, 5, 4, 3, 2, 1}; + fp_t const e[] = {0, 0, 0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* === rsi with k > n === */ + + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + return result; +} diff --git a/test/elder_force_index.c b/test/elder_force_index.c new file mode 100644 index 0000000..7144c68 --- /dev/null +++ b/test/elder_force_index.c @@ -0,0 +1,32 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_elder_force_index( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[9] = { + 200.0, + -66.666666666666629, + 88.888888888888914, + 59.259259259259281, + -60.493827160493808, + 93.004115226337447, + 328.669410150891622, + 119.112940100594415, + 412.741960067062905 + }; + + fp_t const * const e_arr[1] = { e }; + uint_t c_val = k; + + return ts_test(ts_elder_force_index, ts_elder_force_index_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_elder_force_index_p); +} diff --git a/test/elder_impulse.c b/test/elder_impulse.c new file mode 100644 index 0000000..95e2c28 --- /dev/null +++ b/test/elder_impulse.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_elder_impulse( void ) +{ + /* k=3, offset=5 */ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + fp_t const * const inp[1] = { x }; + + /* k=3, offset=4, y_n=6 */ + /* SMA(3): 11, 12, 13, 14, 15, 16, 17, 18 */ + /* Osc: 1, 1, 1, 1, 1, 1, 1, 1 */ + /* Signal (offset=4, 6 values): 1, 1, 1, 1, 1, 1 */ + fp_t const eos[] = { 1, 1, 1, 1, 1, 1 }; + fp_t const es[] = { 1, 1, 1, 1, 1, 1 }; + fp_t const * const exp[2] = { eos, es }; + + uint_t k = 3; + return ts_test(ts_elder_impulse, ts_elder_impulse_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_elder_impulse_p); +} diff --git a/test/elder_ray.c b/test/elder_ray.c new file mode 100644 index 0000000..315b0fd --- /dev/null +++ b/test/elder_ray.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_elder_ray( void ) +{ + /* k=2, EMA alpha=0.5 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 10, 12, 14, 13, 15, 16, 18, 17, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* EMA(2): 10, 11, 12.5, 12.75, 13.875, 14.9375, 16.4688, 16.7344, 17.8672, 18.9336 */ + /* Bull: 12-10=2, 13-11=2, 14-12.5=1.5, 15-12.75=2.25, 16-13.875=2.125, 17-14.9375=2.0625, 18-16.4688=1.5312, 19-16.7344=2.2656, 20-17.8672=2.1328, 21-18.9336=2.0664 */ + /* Bear: 9-10=-1, 10-11=-1, 11-12.5=-1.5, 12-12.75=-0.75, 13-13.875=-0.875, 14-14.9375=-0.9375, 15-16.4688=-1.4688, 16-16.7344=-0.7344, 17-17.8672=-0.8672, 18-18.9336=-0.9336 */ + fp_t const eb[] = { 2, 2, 1.5, 2.25, 2.125, 2.0625, 1.53125, 2.265625, 2.1328125, 2.06640625 }; + fp_t const ee[] = { -1, -1, -1.5, -0.75, -0.875, -0.9375, -1.46875, -0.734375, -0.8671875, -0.93359375 }; + fp_t const * const exp[2] = { eb, ee }; + + fp_t k = 2; + return ts_test(ts_elder_ray, ts_elder_ray_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_elder_ray_p); +} diff --git a/test/ellint_1.c b/test/ellint_1.c new file mode 100644 index 0000000..50bd242 --- /dev/null +++ b/test/ellint_1.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ellint_1( void ) +{ + fp_t const x0[] = { 0.000000, 0.250000, 0.500000, 0.750000, 0.900000 }; + fp_t const e0[] = { 1.570796, 1.685750, 1.854075, 2.156516, 2.578092 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_ellint_1, ts_ellint_1_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_ellint_1_p); +} diff --git a/test/ellint_2.c b/test/ellint_2.c new file mode 100644 index 0000000..9036741 --- /dev/null +++ b/test/ellint_2.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ellint_2( void ) +{ + fp_t const x0[] = { 0.000000, 0.250000, 0.500000, 0.750000, 0.900000 }; + fp_t const e0[] = { 1.570796, 1.681966, 1.834119, 2.088133, 2.421504 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_ellint_2, ts_ellint_2_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_ellint_2_p); +} diff --git a/test/ema_variable.c b/test/ema_variable.c new file mode 100644 index 0000000..52fa74c --- /dev/null +++ b/test/ema_variable.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ema_variable( void ) +{ + fp_t const x0[] = { -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10 }; + fp_t const e0[] = { -10.000000, -8.333333, -6.111111, -3.703704, -1.234568, 1.255144, 3.751715, 6.250572, 8.750191 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + fp_t c = 2; + + return ts_test(ts_ema_variable, ts_ema_variable_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_ema_variable_p); +} diff --git a/test/envelope.c b/test/envelope.c new file mode 100644 index 0000000..26d0edb --- /dev/null +++ b/test/envelope.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_envelope( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* k=3, pct=0.05, SMA: 11, 12, 13, 14, 15, 16, 17, 18 */ + /* Upper = SMA*1.05, Lower = SMA*0.95 */ + fp_t const eu[] = { 11.55, 12.60, 13.65, 14.70, 15.75, 16.80, 17.85, 18.90 }; + fp_t const el[] = { 10.45, 11.40, 12.35, 13.30, 14.25, 15.20, 16.15, 17.10 }; + fp_t const * const exp[2] = { eu, el }; + + typedef struct { uint_t k; fp_t pct; } c_t; + c_t c = { .k = 3, .pct = 0.05 }; + return ts_test(ts_envelope, ts_envelope_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_envelope_p); +} diff --git a/test/erf.c b/test/erf.c new file mode 100644 index 0000000..dd28c62 --- /dev/null +++ b/test/erf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_erf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { -0.995322, -0.842701, 0.000000, 0.842701, 0.995322 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_erf, ts_erf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_erf_p); +} diff --git a/test/erfc.c b/test/erfc.c new file mode 100644 index 0000000..668912e --- /dev/null +++ b/test/erfc.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_erfc( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 1.995322, 1.842701, 1.000000, 0.157299, 0.004678 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_erfc, ts_erfc_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_erfc_p); +} diff --git a/test/euclidean_norm.c b/test/euclidean_norm.c new file mode 100644 index 0000000..475778b --- /dev/null +++ b/test/euclidean_norm.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_euclidean_norm( void ) +{ + fp_t const x0[] = { 3.000000, 5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, 0.000000 }; + fp_t const e0[] = { 5.000000, 13.000000, 5.000000, 1.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_euclidean_norm, ts_euclidean_norm_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_euclidean_norm_p); +} diff --git a/test/exp10.c b/test/exp10.c new file mode 100644 index 0000000..b986269 --- /dev/null +++ b/test/exp10.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exp10( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, -1.000000, 3.000000 }; + fp_t const e0[] = { 1.000000, 10.000000, 100.000000, 0.100000, 1000.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exp10, ts_exp10_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exp10_p); +} diff --git a/test/exp10m1.c b/test/exp10m1.c new file mode 100644 index 0000000..e0f7988 --- /dev/null +++ b/test/exp10m1.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exp10m1( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, -1.000000 }; + fp_t const e0[] = { 0.000000, 9.000000, 99.000000, -0.900000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exp10m1, ts_exp10m1_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exp10m1_p); +} diff --git a/test/exp2.c b/test/exp2.c new file mode 100644 index 0000000..bc4df6a --- /dev/null +++ b/test/exp2.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exp2( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 }; + fp_t const e0[] = { 1.000000, 2.000000, 4.000000, 8.000000, 16.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exp2, ts_exp2_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exp2_p); +} diff --git a/test/exp2m1.c b/test/exp2m1.c new file mode 100644 index 0000000..530b334 --- /dev/null +++ b/test/exp2m1.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exp2m1( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, -1.000000 }; + fp_t const e0[] = { 0.000000, 1.000000, 3.000000, -0.500000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exp2m1, ts_exp2m1_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exp2m1_p); +} diff --git a/test/expit.c b/test/expit.c new file mode 100644 index 0000000..cfba0d7 --- /dev/null +++ b/test/expit.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_expit( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.119203, 0.268941, 0.500000, 0.731059, 0.880797 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_expit, ts_expit_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_expit_p); +} diff --git a/test/expm1.c b/test/expm1.c new file mode 100644 index 0000000..0375e98 --- /dev/null +++ b/test/expm1.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_expm1( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, -1.000000, 2.000000, 0.500000 }; + fp_t const e0[] = { 0.000000, 1.718282, -0.632121, 6.389056, 0.648721 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_expm1, ts_expm1_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_expm1_p); +} diff --git a/test/exponential.c b/test/exponential.c new file mode 100644 index 0000000..ed1f85f --- /dev/null +++ b/test/exponential.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exponential( void ) +{ + fp_t const c[] = { 2.0 }; + + fp_t const x0[] = { 0, 1, 2, 3, 4 }; + fp_t const e0[] = { 1, 2, 4, 8, 16 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exponential, ts_exponential_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_exponential_p); +} diff --git a/test/exponential_cdf.c b/test/exponential_cdf.c new file mode 100644 index 0000000..fd47765 --- /dev/null +++ b/test/exponential_cdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exponential_cdf( void ) +{ + fp_t const x0[] = { -1.000000, 0.000000, 0.500000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.393469, 0.632121, 0.864665 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exponential_cdf, ts_exponential_cdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exponential_cdf_p); +} diff --git a/test/exponential_natural.c b/test/exponential_natural.c new file mode 100644 index 0000000..c16df53 --- /dev/null +++ b/test/exponential_natural.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exponential_natural( void ) +{ + fp_t const x0[] = { 0, 0.5, 1, 2, 3 }; + fp_t const e0[] = { 1, 1.648721, 2.718282, 7.389056, 20.085537 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exponential_natural, ts_exponential_natural_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exponential_natural_p); +} diff --git a/test/exponential_pdf.c b/test/exponential_pdf.c new file mode 100644 index 0000000..43b8149 --- /dev/null +++ b/test/exponential_pdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_exponential_pdf( void ) +{ + fp_t const x0[] = { -1.000000, 0.000000, 0.500000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.000000, 1.000000, 0.606531, 0.367879, 0.135335 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_exponential_pdf, ts_exponential_pdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_exponential_pdf_p); +} diff --git a/test/factorial.c b/test/factorial.c new file mode 100644 index 0000000..42e8d3d --- /dev/null +++ b/test/factorial.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_factorial( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000, 9.000000, 10.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 2.000000, 6.000000, 24.000000, 120.000000, 720.000000, 5040.000000, 40320.000000, 362880.000000, 3628800.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_factorial, ts_factorial_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_factorial_p); +} diff --git a/test/factorial_int.c b/test/factorial_int.c new file mode 100644 index 0000000..0e49cac --- /dev/null +++ b/test/factorial_int.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_factorial_int( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 10.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 2.000000, 6.000000, 24.000000, 120.000000, 3628800.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_factorial_int, ts_factorial_int_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_factorial_int_p); +} diff --git a/test/fdim_2input.c b/test/fdim_2input.c new file mode 100644 index 0000000..e2b1e0a --- /dev/null +++ b/test/fdim_2input.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fdim_2input( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.000000, 1.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fdim_2input, ts_fdim_2input_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fdim_2input_p); +} diff --git a/test/fibonacci.c b/test/fibonacci.c new file mode 100644 index 0000000..5a8ab7d --- /dev/null +++ b/test/fibonacci.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fibonacci( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000, 10.000000 }; + fp_t const e0[] = { 0.000000, 1.000000, 1.000000, 2.000000, 3.000000, 5.000000, 8.000000, 13.000000, 21.000000, 55.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fibonacci, ts_fibonacci_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fibonacci_p); +} diff --git a/test/fill_indicator.c b/test/fill_indicator.c new file mode 100644 index 0000000..071c245 --- /dev/null +++ b/test/fill_indicator.c @@ -0,0 +1,40 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_fill_indicator( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 8, 10, 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const c[] = { 11, 12, 10, 14, 15, 16, 14, 18, 17, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* fill values: + * i=0: |11-10|/(12-8)*100 = 1/4*100 = 25 + * i=1: |12-11|/(13-10)*100 = 1/3*100 = 33.333 + * i=2: |10-12|/(14-10)*100 = 2/4*100 = 50 + * i=3: |14-13|/(15-11)*100 = 1/4*100 = 25 + * i=4: |15-14|/(16-12)*100 = 1/4*100 = 25 + * i=5: |16-15|/(17-13)*100 = 1/4*100 = 25 + * i=6: |14-16|/(18-14)*100 = 2/4*100 = 50 + * i=7: |18-17|/(19-15)*100 = 1/4*100 = 25 + * i=8: |17-18|/(20-16)*100 = 1/4*100 = 25 + * i=9: |20-19|/(21-17)*100 = 1/4*100 = 25 + * SMA(3): i=2: (25+33.333+50)/3 = 36.111 + * i=3: 36.111 + (25-25)/3 = 36.111 + * i=4: 36.111 + (25-33.333)/3 = 33.333 + * i=5: 33.333 + (25-50)/3 = 25 + * i=6: 25 + (50-25)/3 = 33.333 + * i=7: 33.333 + (25-25)/3 = 33.333 + * i=8: 33.333 + (25-25)/3 = 33.333 + * i=9: 33.333 + (25-50)/3 = 25 + */ + fp_t const e[] = { 36.11111, 36.11111, 33.33333, 25, 33.33333, 33.33333, 33.33333, 25 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_fill_indicator, ts_fill_indicator_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_fill_indicator_p); +} diff --git a/test/fisher_transform.c b/test/fisher_transform.c new file mode 100644 index 0000000..050a360 --- /dev/null +++ b/test/fisher_transform.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_fisher_transform( void ) +{ + /* k=3, prices increasing monotonically */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* Window indices [1..3], H=[13,14,15], L=[10,11,12], range=5, val=(14-10)/5=0.8 */ + /* fisher = 0.5*ln(0.8/0.2) + 0.5*0 = 0.5*ln(4) = 0.69315 */ + /* Next: H=[14,15,16], L=[11,12,13], val=(15-11)/5=0.8, fisher=0.5*ln(4)+0.5*0.69315=0.69315+0.34657=1.03972 */ + /* Monotonic, so all val=0.8, fisher converges toward ln(4) = 1.38629 */ + /* f1=0.69315, f2=1.03972, f3=1.21301, f4=1.29965, f5=1.34297, f6=1.36463, f7=1.37546 */ + fp_t const ef[] = { 0.69315, 1.03972, 1.21301, 1.29965, 1.34297, 1.36463, 1.37546, 1.38088, 0, 0 }; + fp_t const et[] = { 0, 0.69315, 1.03972, 1.21301, 1.29965, 1.34297, 1.36463, 1.37546, 0, 0 }; + fp_t const * const exp[2] = { ef, et }; + + uint_t k = 3; + return ts_test(ts_fisher_transform, ts_fisher_transform_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_fisher_transform_p); +} diff --git a/test/floor.c b/test/floor.c new file mode 100644 index 0000000..274c60f --- /dev/null +++ b/test/floor.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_floor( void ) +{ + fp_t const x0[] = { -2.7, -1.3, 0, 0.5, 1.3, 2.7, 3.9 }; + fp_t const e0[] = { -3, -2, 0, 0, 1, 2, 3 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_floor, ts_floor_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_floor_p); +} diff --git a/test/fma.c b/test/fma.c new file mode 100644 index 0000000..2e342bc --- /dev/null +++ b/test/fma.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fma( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 3.000000 }; + fp_t const x1[] = { 2.000000, 3.000000, 4.000000 }; + fp_t const x2[] = { 1.000000, 1.000000, 1.000000 }; + fp_t const e0[] = { 3.000000, 7.000000, 13.000000 }; + + fp_t const * const x[3] = { x0, x1, x2 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fma, ts_fma_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fma_p); +} diff --git a/test/fmax.c b/test/fmax.c new file mode 100644 index 0000000..df3d4e2 --- /dev/null +++ b/test/fmax.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fmax( void ) +{ + fp_t const x0[] = { 1.000000, 3.000000, 5.000000, 2.000000 }; + fp_t const x1[] = { 2.000000, 2.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 2.000000, 3.000000, 5.000000, 5.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fmax, ts_fmax_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fmax_p); +} diff --git a/test/fmax_2input.c b/test/fmax_2input.c new file mode 100644 index 0000000..1ef7371 --- /dev/null +++ b/test/fmax_2input.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fmax_2input( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.000000, 1.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fmax_2input, ts_fmax_2input_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fmax_2input_p); +} diff --git a/test/fmin.c b/test/fmin.c new file mode 100644 index 0000000..1d00a31 --- /dev/null +++ b/test/fmin.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fmin( void ) +{ + fp_t const x0[] = { 1.000000, 3.000000, 5.000000, 2.000000 }; + fp_t const x1[] = { 2.000000, 2.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 1.000000, 2.000000, 3.000000, 2.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fmin, ts_fmin_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fmin_p); +} diff --git a/test/fmin_2input.c b/test/fmin_2input.c new file mode 100644 index 0000000..8d5a37a --- /dev/null +++ b/test/fmin_2input.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fmin_2input( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { -3.000000, -1.000000, 0.000000, 0.000000, 0.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fmin_2input, ts_fmin_2input_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fmin_2input_p); +} diff --git a/test/fmod.c b/test/fmod.c new file mode 100644 index 0000000..49ea747 --- /dev/null +++ b/test/fmod.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fmod( void ) +{ + fp_t const x0[] = { 7.000000, 10.000000, -7.000000, 7.500000 }; + fp_t const x1[] = { 3.000000, 3.000000, 3.000000, 2.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, -1.000000, 1.500000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_fmod, ts_fmod_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_fmod_p); +} diff --git a/test/fobv.c b/test/fobv.c new file mode 100644 index 0000000..3f57e0a --- /dev/null +++ b/test/fobv.c @@ -0,0 +1,18 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_fobv( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t const e[] = { 200, -100, 300, 300 }; + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_fobv, ts_fobv_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_fobv_p); +} diff --git a/test/fractal_chaos_bands.c b/test/fractal_chaos_bands.c new file mode 100644 index 0000000..eccc88f --- /dev/null +++ b/test/fractal_chaos_bands.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_fractal_chaos_bands( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, half_k=1, offset=1, y_n=11 (i=1..10, y[11]=0) */ + fp_t const eu[] = { 14.000000, 14.500000, 15.250000, 16.125000, 17.062500, 18.031250, 19.015625, 20.007812, 21.003906, 22.001953, 0.000000 }; + fp_t const el[] = { 9.000000, 9.500000, 10.250000, 11.125000, 12.062500, 13.031250, 14.015625, 15.007812, 16.003906, 17.001953, 0.000000 }; + fp_t const * const exp[2] = { eu, el }; + + uint_t k = 3; + return ts_test(ts_fractal_chaos_bands, ts_fractal_chaos_bands_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_fractal_chaos_bands_p); +} diff --git a/test/fractal_chaos_oscillator.c b/test/fractal_chaos_oscillator.c new file mode 100644 index 0000000..921c194 --- /dev/null +++ b/test/fractal_chaos_oscillator.c @@ -0,0 +1,20 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_fractal_chaos_oscillator( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, half_k=1, offset=1, y_n=11 (i=1..10, y[11]=0) */ + fp_t const e[] = { 21.739130, 20.833333, 19.607843, 18.348624, 17.167382, 16.096579, 15.137181, 14.279340, 13.510661, 12.819229, 0.000000 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_fractal_chaos_oscillator, ts_fractal_chaos_oscillator_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_fractal_chaos_oscillator_p); +} diff --git a/test/fuzz.c b/test/fuzz.c new file mode 100644 index 0000000..3f11d9d --- /dev/null +++ b/test/fuzz.c @@ -0,0 +1,68 @@ +#include "ts.h" +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +typedef int (*ts_func_t)(fp_t * const *, uint_t, fp_t const * const *, uint_t, uint_t, ts_constant_t const *); + +typedef struct { + ts_func_t func; + uint_t xn; + uint_t yn; +} fuzz_entry_t; + +#include "fuzz_table_generated.c" + +static const uint_t _fuzz_n = _fuzz_generated_n; +static const fuzz_entry_t *_fuzz_table = _fuzz_table_generated; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (size < 2) + return 0; + + uint_t func_idx = data[0]; + uint_t config = data[1]; + + uint_t n = 4; + if (size > 2) + n = (uint_t)(data[2] + 4); + if (n > 256) + n = 256; + + fp_t input[256]; + fp_t output[256]; + fp_t output2[256]; + fp_t output3[256]; + + size_t offset = 3; + for (uint_t i = 0; i < n && offset + 8 <= size; i++, offset += 8) + { + memcpy(&input[i], data + offset, sizeof(fp_t)); + } + for (uint_t i = (offset - 3) / 8; i < n; i++) + input[i] = 0; + + fp_t *x_arrays[5] = {input, input, input, input, input}; + fp_t *y_arrays[3] = {output, output2, output3}; + + uint_t period = 5; + if (config > 0) + period = config; + if (period < 1) + period = 1; + if (period > n) + period = n; + + uint_t idx = func_idx % _fuzz_n; + fuzz_entry_t entry = _fuzz_table[idx]; + + uint_t xn = entry.xn; + uint_t yn = entry.yn; + if (xn > 5) xn = 5; + if (yn > 3) yn = 3; + + (void)entry.func(y_arrays, yn, (fp_t const * const *)x_arrays, xn, n, (ts_constant_t *)&period); + + return 0; +} diff --git a/test/gamma_pdf.c b/test/gamma_pdf.c new file mode 100644 index 0000000..38fcc62 --- /dev/null +++ b/test/gamma_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_gamma_pdf( void ) +{ + fp_t const c[] = { 2.000000 }; + + fp_t const x0[] = { 0.100000, 0.500000, 1.000000, 2.000000, 3.000000 }; + fp_t const e0[] = { 0.090484, 0.303265, 0.367879, 0.270671, 0.149361 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_gamma_pdf, ts_gamma_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_gamma_pdf_p); +} diff --git a/test/gator_oscillator.c b/test/gator_oscillator.c new file mode 100644 index 0000000..13ae023 --- /dev/null +++ b/test/gator_oscillator.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_gator_oscillator( void ) +{ + /* Linear prices: 0,1,2,...,24 */ + fp_t x[25] = { 0 }; + for ( uint_t i = 0; i < 25; ++i ) + x[i] = (fp_t)i; + + fp_t const * const inp[1] = { x }; + + /* At i=12: SMA(5) of [8..12] = 10, SMA(8) of [5..12] = 8.5, SMA(13) of [0..12] = 6 */ + /* Upper = |10 - 8.5| = 1.5, Lower = |6 - 8.5| = 2.5 */ + /* At i=13: SMA(5) of [9..13] = 11, SMA(8) of [6..13] = 9.5, SMA(13) of [1..13] = 7 */ + /* Upper = |11 - 9.5| = 1.5, Lower = |7 - 9.5| = 2.5 */ + /* Constant for linear series */ + fp_t const eu[] = { 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5 }; + fp_t const el[] = { 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5 }; + fp_t const * const exp[2] = { eu, el }; + + return ts_test(ts_gator_oscillator, ts_gator_oscillator_offset, inp, exp, 25, (ts_constant_t *)NULL, &ts_gator_oscillator_p); +} diff --git a/test/gaussian_cdf.c b/test/gaussian_cdf.c new file mode 100644 index 0000000..82626f7 --- /dev/null +++ b/test/gaussian_cdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_gaussian_cdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.022750, 0.158655, 0.500000, 0.841345, 0.977250 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_gaussian_cdf, ts_gaussian_cdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_gaussian_cdf_p); +} diff --git a/test/gaussian_pdf.c b/test/gaussian_pdf.c new file mode 100644 index 0000000..d859c26 --- /dev/null +++ b/test/gaussian_pdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_gaussian_pdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.053991, 0.241971, 0.398942, 0.241971, 0.053991 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_gaussian_pdf, ts_gaussian_pdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_gaussian_pdf_p); +} diff --git a/test/gaussian_quantile.c b/test/gaussian_quantile.c new file mode 100644 index 0000000..8780c19 --- /dev/null +++ b/test/gaussian_quantile.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_gaussian_quantile( void ) +{ + fp_t const x0[] = { 0.100000, 0.250000, 0.500000, 0.750000, 0.900000 }; + fp_t const e0[] = { -1.281552, -0.674490, -0.000000, 0.674490, 1.281552 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_gaussian_quantile, ts_gaussian_quantile_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_gaussian_quantile_p); +} diff --git a/test/gcd.c b/test/gcd.c new file mode 100644 index 0000000..ebf8d9c --- /dev/null +++ b/test/gcd.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_gcd( void ) +{ + fp_t const x0[] = { 12.000000, 15.000000, 100.000000, 7.000000, 0.000000 }; + fp_t const x1[] = { 8.000000, 10.000000, 35.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 4.000000, 5.000000, 5.000000, 1.000000, 5.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_gcd, ts_gcd_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_gcd_p); +} diff --git a/test/gen_test_data.c b/test/gen_test_data.c new file mode 100644 index 0000000..6ef54af --- /dev/null +++ b/test/gen_test_data.c @@ -0,0 +1,101 @@ +/* gen_test_data.c - Validate all ts functions run without error. + * Note: excluded from default build. Fix and add target to use. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "ts.h" + +static fp_t rand_fp(void) { + return (double)rand() / (double)RAND_MAX * 100.0; +} + +/* + * Smoke-test a single function by name. Returns 0 on success. + * This is a placeholder — a full auto-discovery registry would require + * adding ts_functions[] / ts_functions_n to ts.h or generating them. + */ +int main(void) { + unsigned int seed = 42; + srand(seed); + uint_t n = 100; + + /* Test a representative sample of functions */ + struct { + const char *name; + int (*func)(fp_t * const y[], fp_t const * const x[], uint_t, ts_constant_t const *); + uint_t x_n, y_n; + } samples[] = { + { "average", ts_average, 1, 1 }, + { "rsi", ts_rsi, 1, 1 }, + { "deviation_standard", ts_deviation_standard, 1, 1 }, + { "variance", ts_variance, 1, 1 }, + { "addition", ts_addition, 2, 1 }, + { "subtraction", ts_subtraction, 2, 1 }, + { "multiplication", ts_multiplication, 2, 1 }, + { "division", ts_division, 2, 1 }, + { "absolute_value", ts_absolute_value, 1, 1 }, + { "nan_fn", ts_nan_fn, 1, 1 }, + }; + size_t nsamples = sizeof(samples) / sizeof(samples[0]); + + for (size_t fi = 0; fi < nsamples; fi++) { + typeof(samples[fi]) f = samples[fi]; + + /* Allocate input arrays */ + fp_t **x = calloc(f.x_n, sizeof(fp_t *)); + fp_t **y = calloc(f.y_n, sizeof(fp_t *)); + if (!x || !y) { free(x); free(y); continue; } + + for (uint_t i = 0; i < f.x_n; i++) { + x[i] = malloc(n * sizeof(fp_t)); + if (!x[i]) goto next; + for (uint_t j = 0; j < n; j++) x[i][j] = rand_fp(); + } + for (uint_t i = 0; i < f.y_n; i++) { + y[i] = calloc(n, sizeof(fp_t)); + if (!y[i]) goto next; + } + + /* Try with default params (all zeros) */ + uint8_t params[TS_CONSTANT_SIZE] = { 0 }; + int ret = f.func(y, x, n, (ts_constant_t const *)params); + const char *pass_label = "PASS"; + if (ret != 0) { + /* Try with k=10 */ + { + uint8_t c[TS_CONSTANT_SIZE] = { 0 }; + *(uint_t *)c = 10; + ret = f.func(y, x, n, (ts_constant_t const *)c); + if (ret == 0) pass_label = "PASS (k=10)"; + } + } + if (ret != 0) { + /* Try with k=14 */ + { + uint8_t c[TS_CONSTANT_SIZE] = { 0 }; + *(uint_t *)c = 14; + ret = f.func(y, x, n, (ts_constant_t const *)c); + if (ret == 0) pass_label = "PASS (k=14)"; + } + } + + if (ret != 0) { + printf("SKIP %s (ret=%d after all retries)\n", f.name, ret); + (void)fflush(stdout); + goto next; + } + + printf("%s %s\n", pass_label, f.name); + (void)fflush(stdout); + + next: + for (uint_t i = 0; i < f.x_n; i++) free(x[i]); + for (uint_t i = 0; i < f.y_n; i++) free(y[i]); + free(x); + free(y); + } + + return 0; +} diff --git a/test/gopalakrishnan_range_index.c b/test/gopalakrishnan_range_index.c new file mode 100644 index 0000000..6b8167a --- /dev/null +++ b/test/gopalakrishnan_range_index.c @@ -0,0 +1,39 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_gopalakrishnan_range_index( void ) +{ + fp_t const x0[] = { 10, 10, 10, 20, 20, 20 }; + fp_t const x1[] = { 14, 13, 14, 24, 23, 24 }; + fp_t const x2[] = { 8, 9, 8, 18, 19, 18 }; + fp_t const x3[] = { 12, 11, 13, 22, 21, 23 }; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + + uint_t k = 2; + + fp_t sum_abs = fabs( x3[1] - x3[0] ) + fabs( x3[2] - x3[1] ); + fp_t sum_range = ( x1[1] - x2[1] ) + ( x1[2] - x2[2] ); + fp_t e0 = sum_range != 0 ? sum_abs / sum_range : 0; + + fp_t abs_ring[2] = { fabs(x3[1]-x3[0]), fabs(x3[2]-x3[1]) }; + fp_t range_ring[2] = { x1[1]-x2[1], x1[2]-x2[2] }; + fp_t e[4] = { e0 }; + + for ( uint_t i = 3; i < 6; ++i ) + { + fp_t new_abs = fabs( x3[i] - x3[i-1] ); + fp_t new_range = x1[i] - x2[i]; + uint_t idx = ( i - k - 1 ) % k; + sum_abs += new_abs - abs_ring[ idx ]; + sum_range += new_range - range_ring[ idx ]; + abs_ring[ ( i - 1 ) % k ] = new_abs; + range_ring[ ( i - 1 ) % k ] = new_range; + e[ i - k ] = sum_range != 0 ? sum_abs / sum_range : 0; + } + + fp_t const * const exp[1] = { e }; + + return ts_test(ts_gopalakrishnan_range_index, ts_gopalakrishnan_range_index_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_gopalakrishnan_range_index_p); +} diff --git a/test/hard_sigmoid.c b/test/hard_sigmoid.c new file mode 100644 index 0000000..50453ac --- /dev/null +++ b/test/hard_sigmoid.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hard_sigmoid( void ) +{ + fp_t const x0[] = { -5.000000, -3.000000, 0.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.500000, 1.000000, 1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_hard_sigmoid, ts_hard_sigmoid_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_hard_sigmoid_p); +} diff --git a/test/hard_swish.c b/test/hard_swish.c new file mode 100644 index 0000000..ce9e82e --- /dev/null +++ b/test/hard_swish.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hard_swish( void ) +{ + fp_t const x0[] = { -5.000000, -3.000000, 0.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { -0.000000, -0.000000, 0.000000, 3.000000, 5.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_hard_swish, ts_hard_swish_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_hard_swish_p); +} diff --git a/test/hard_tanh.c b/test/hard_tanh.c new file mode 100644 index 0000000..db97fd5 --- /dev/null +++ b/test/hard_tanh.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hard_tanh( void ) +{ + fp_t const x0[] = { -3.000000, -1.500000, -1.000000, 0.000000, 1.000000, 1.500000, 3.000000 }; + fp_t const e0[] = { -1.000000, -1.000000, -1.000000, 0.000000, 1.000000, 1.000000, 1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_hard_tanh, ts_hard_tanh_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_hard_tanh_p); +} diff --git a/test/harmonic.c b/test/harmonic.c new file mode 100644 index 0000000..7a4931a --- /dev/null +++ b/test/harmonic.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_harmonic( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000 }; + fp_t const e0[] = { 0.000000, 1.000000, 1.500000, 1.833333, 2.083333, 2.283333 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_harmonic, ts_harmonic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_harmonic_p); +} diff --git a/test/hermite.c b/test/hermite.c new file mode 100644 index 0000000..48fdde0 --- /dev/null +++ b/test/hermite.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hermite( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 0.500000, -1.000000, 2.000000 }; + fp_t const x1[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 }; + fp_t const e0[] = { 1.000000, 2.000000, -1.000000, 4.000000, 76.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_hermite, ts_hermite_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_hermite_p); +} diff --git a/test/high_low_bands.c b/test/high_low_bands.c new file mode 100644 index 0000000..f98bee3 --- /dev/null +++ b/test/high_low_bands.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_high_low_bands( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, offset=4, y_n=6 */ + /* Rolling HH3: 12,13,14,15,16,17,18,19 */ + /* Rolling LL3: 9,9,9,10,11,12,13,14 */ + /* SMA3(HH3): 13,14,15,16,17,18 */ + /* SMA3(LL3): 9,9.333,10,11,12,13 */ + fp_t const eu[] = { 13, 14, 15, 16, 17, 18 }; + fp_t const el[] = { 9, 9.33333333333333, 10, 11, 12, 13 }; + fp_t const * const exp[2] = { eu, el }; + + uint_t k = 3; + return ts_test(ts_high_low_bands, ts_high_low_bands_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_high_low_bands_p); +} diff --git a/test/high_minus_low.c b/test/high_minus_low.c new file mode 100644 index 0000000..5bcec93 --- /dev/null +++ b/test/high_minus_low.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_high_minus_low( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 5, 10, 15, 20, 25 }; + fp_t const x2[] = { 0, 5, 10, 15, 20 }; + fp_t const x3[] = { 1, 1, 1, 1, 1 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 5, 5, 5, 5, 5 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_high_minus_low, ts_high_minus_low_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_high_minus_low_p); +} diff --git a/test/highest_high.c b/test/highest_high.c new file mode 100644 index 0000000..b5897ad --- /dev/null +++ b/test/highest_high.c @@ -0,0 +1,19 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_highest_high( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 1, 3, 5, 2, 4, 6, 8 }; + fp_t const x2[] = { 0, 1, 2, 1, 3, 4, 5 }; + fp_t const x3[] = { 1, 2, 3, 3, 5, 6, 7 }; + fp_t const x4[] = { 1, 1, 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 5, 5, 5, 6, 8 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_highest_high, ts_highest_high_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_highest_high_p); +} diff --git a/test/historical_volatility.c b/test/historical_volatility.c new file mode 100644 index 0000000..1d57f54 --- /dev/null +++ b/test/historical_volatility.c @@ -0,0 +1,16 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_historical_volatility( void ) +{ + fp_t const x[] = { 100, 101, 100, 102, 101, 100, 103, 101, 102, 100 }; + fp_t const * const inp[1] = { x }; + + /* k=3, initial window at y[0][2], rolling windows at y[0][3..8] */ + fp_t const e[] = { 0.196453, 0.222285, 0.222285, 0.295293, 0.337649, 0.320724, 0.221194, 0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_historical_volatility, ts_historical_volatility_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_historical_volatility_p); +} diff --git a/test/hull_ma.c b/test/hull_ma.c new file mode 100644 index 0000000..ff8182a --- /dev/null +++ b/test/hull_ma.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hull_ma( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + fp_t const * const x[] = { x0 }; + + fp_t const e[] = { 6.333333, 7.333333, 8.333333, 9.333333, 10.333333 }; + fp_t const * const exp[] = { e }; + + uint_t c = 5; + return ts_test(ts_hull_ma, ts_hull_ma_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_hull_ma_p); +} diff --git a/test/hurst_exponent.c b/test/hurst_exponent.c new file mode 100644 index 0000000..ccf54c8 --- /dev/null +++ b/test/hurst_exponent.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hurst_exponent( void ) +{ + uint_t const c[] = { 5 }; + fp_t const x0[] = { 1,2,3,4,5,6,7,8,9,10 }; + fp_t e0[10]; + ts_hurst_exponent((fp_t*[]){e0}, 1, (fp_t const *[]){x0}, 1, 10, (ts_constant_t *)&c); + return true; +} diff --git a/test/hypot.c b/test/hypot.c new file mode 100644 index 0000000..107530b --- /dev/null +++ b/test/hypot.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_hypot( void ) +{ + fp_t const x0[] = { 3.000000, 5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, 0.000000 }; + fp_t const e0[] = { 5.000000, 13.000000, 5.000000, 1.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_hypot, ts_hypot_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_hypot_p); +} diff --git a/test/ichimoku.c b/test/ichimoku.c new file mode 100644 index 0000000..ea20056 --- /dev/null +++ b/test/ichimoku.c @@ -0,0 +1,49 @@ +#include <math.h> +#include <stddef.h> +#include <stdlib.h> +#include "ts.h" + +bool ts_test_ichimoku( void ) +{ + uint_t n = 55; + fp_t *o = malloc( n * sizeof( fp_t ) ); + fp_t *h = malloc( n * sizeof( fp_t ) ); + fp_t *l = malloc( n * sizeof( fp_t ) ); + fp_t *cl = malloc( n * sizeof( fp_t ) ); + + if ( !o || !h || !l || !cl ) + { + free( o ); free( h ); free( l ); free( cl ); + return false; + } + + for ( uint_t i = 0; i < n; ++i ) + { + o[i] = (fp_t)(100 + i); + h[i] = (fp_t)(102 + i); + l[i] = (fp_t)(98 + i); + cl[i] = (fp_t)(101 + i); + } + + fp_t const * const x[4] = { o, h, l, cl }; + + /* Fixed: include current bar in lookback, Senkou plotted 26 ahead, Chikou 26 behind */ + /* offset=52, y_n=55-52=3 */ + fp_t const et[] = { 148, 149, 150 }; + fp_t const ek[] = { 139.5, 140.5, 141.5 }; + fp_t const esa[] = { 117.75, 118.75, 119.75 }; + fp_t const esb[] = { 0, 0, 0 }; + fp_t const ec[] = { 127, 128, 129 }; + fp_t const * const exp[5] = { et, ek, esa, esb, ec }; + + ts_constant_t c; + memset(&c, 0, sizeof(c)); + bool result = ts_test(ts_ichimoku, ts_ichimoku_offset, x, exp, n, &c, &ts_ichimoku_p); + + free( o ); + free( h ); + free( l ); + free( cl ); + + return result; +} diff --git a/test/ilogb.c b/test/ilogb.c new file mode 100644 index 0000000..fe50df5 --- /dev/null +++ b/test/ilogb.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ilogb( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 4.000000, 8.000000, 0.500000, 0.250000 }; + fp_t const e0[] = { 0.000000, 1.000000, 2.000000, 3.000000, -1.000000, -2.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_ilogb, ts_ilogb_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_ilogb_p); +} diff --git a/test/intraday_momentum_index.c b/test/intraday_momentum_index.c new file mode 100644 index 0000000..4044faf --- /dev/null +++ b/test/intraday_momentum_index.c @@ -0,0 +1,39 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_intraday_momentum_index( void ) +{ + /* k=3, open always = prev_close => diff=0, IMI=0/0=0 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 10, 12, 14, 16, 18, 20, 22, 24, 26, 28 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* open[i] = close[i-1] for i>=1: o[1]=11 != c[0]=10, diff=1 (up) */ + /* o[2]=12 vs c[1]=12, diff=0 */ + /* o[3]=13 vs c[2]=14, diff=-1 (down=1) */ + /* o[4]=14 vs c[3]=16, diff=-2 (down=2) */ + /* For i=1..3: up=1+0+0=1, down=0+1+2=3, IMI=1/4=0.25 */ + /* Hmm let me recalculate more carefully. xi goes 1 to k=3 */ + /* xi=1: o[1]-c[0]=11-10=1 > 0, sum_up=1 */ + /* xi=2: o[2]-c[1]=12-12=0, neither up nor down, sum_up=1, sum_down=0 */ + /* xi=3: o[3]-c[2]=13-14=-1 < 0, sum_down=1, sum_up=1 */ + /* y[0][3] = 1/(1+1) = 0.5 */ + /* xi=4: o[4]-c[3]=14-16=-2, old=o[1]-c[0]=1 */ + /* sum_up stays 1 (no add), sum_down+=2=3, then remove old: sum_up-=1=0 */ + /* Hmm that logic is wrong. Let me rewrite the incremental part. */ + + /* Actually the incremental update is tricky. Let me use a simpler approach: */ + /* For i=4: diffs at [4,5,6] = o[4]-c[3], o[5]-c[4], o[6]-c[5] */ + /* = 14-16=-2, 15-18=-3, 16-20=-4 */ + /* up=0, down=2+3+4=9, IMI=0 */ + + fp_t const e[] = { 0.5, 0, 0, 0, 0, 0, 0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_intraday_momentum_index, ts_intraday_momentum_index_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_intraday_momentum_index_p); +} diff --git a/test/is_prime.c b/test/is_prime.c new file mode 100644 index 0000000..233ae6f --- /dev/null +++ b/test/is_prime.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_is_prime( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 7.000000, 10.000000, 13.000000, 17.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 1.000000, 1.000000, 0.000000, 1.000000, 1.000000, 0.000000, 1.000000, 1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_is_prime, ts_is_prime_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_is_prime_p); +} diff --git a/test/jaccard_similarity.c b/test/jaccard_similarity.c new file mode 100644 index 0000000..7fb2474 --- /dev/null +++ b/test/jaccard_similarity.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_jaccard_similarity( void ) +{ + fp_t const x0[] = { 3.000000, 5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 5.000000, 0.000000, 2.000000 }; + fp_t const e0[] = { 0.875000, 1.000000, 0.000000, 0.750000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_jaccard_similarity, ts_jaccard_similarity_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_jaccard_similarity_p); +} diff --git a/test/kalman_filter.c b/test/kalman_filter.c new file mode 100644 index 0000000..188ae82 --- /dev/null +++ b/test/kalman_filter.c @@ -0,0 +1,26 @@ +#include "ts.h" + +bool ts_test_kalman_filter( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + fp_t const e0[] = { + 1.0, + 1.5024875621890548, + 2.0099009900990099, + 2.5245821283270429, + 3.0487331327055625, + 3.5843788460830162, + 4.1333360364582399, + 4.6971903124447723, + 5.2772810678708257, + 5.8746943630334316 + }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[] = { e0 }; + + struct { fp_t Q; fp_t R; } c = { 0.01, 1.0 }; + + return ts_test(ts_kalman_filter, ts_kalman_filter_offset, x, e, 10, (ts_constant_t *)&c, &ts_kalman_filter_p); +} diff --git a/test/kaufman_adaptive_ma.c b/test/kaufman_adaptive_ma.c new file mode 100644 index 0000000..2709a3a --- /dev/null +++ b/test/kaufman_adaptive_ma.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_kaufman_adaptive_ma( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + + fp_t const * const x[] = { x0 }; + + fp_t const e[] = { 2.90983006, 4.47213595, 5.81966011, 7.03444185, 8.16718427, 9.24922359, 10.29992669 }; + fp_t const * const exp[] = { e }; + + uint_t c = 5; + return ts_test(ts_kaufman_adaptive_ma, ts_kaufman_adaptive_ma_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_kaufman_adaptive_ma_p); +} diff --git a/test/keltner_channel.c b/test/keltner_channel.c new file mode 100644 index 0000000..e5ad7a8 --- /dev/null +++ b/test/keltner_channel.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_keltner_channel( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, TP = (H+L+C)/3, EMA(3) of TP (alpha=2/(k+1)), ATR = Wilder's smoothing */ + /* EMA alpha = 2/4 = 0.5, ATR uses Wilder's: (prev*(k-1) + TR) / k */ + fp_t const em[] = { 12.79166667, 13.72916667, 14.69791667, 15.68229167, 16.67447917, 17.67057292, 18.66861979 }; + fp_t const eu[] = { 18.79166667, 19.72916667, 20.69791667, 21.68229167, 22.67447917, 23.67057292, 24.66861979 }; + fp_t const el[] = { 6.79166667, 7.72916667, 8.69791667, 9.68229167, 10.67447917, 11.67057292, 12.66861979 }; + fp_t const * const exp[3] = { em, eu, el }; + + uint_t k = 3; + return ts_test(ts_keltner_channel, ts_keltner_channel_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_keltner_channel_p); +} diff --git a/test/kvo.c b/test/kvo.c new file mode 100644 index 0000000..c0f489f --- /dev/null +++ b/test/kvo.c @@ -0,0 +1,40 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_kvo( void ) +{ + uint_t k1 = 3; + uint_t k2 = 3; + + fp_t const o[] = { 10, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 16, 15, 17, 16 }; + fp_t const h[] = { 12, 13, 12, 14, 13, 15, 14, 16, 15, 17, 16, 18, 17, 19, 18 }; + fp_t const l[] = { 8, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14 }; + fp_t const c[] = { 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 16, 15, 17, 16, 18 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e_kvo[5] = { + 1.0, + -0.044715, + 0.542893, + -0.323870, + 0.444411 + }; + + fp_t e_sig[5] = { + 1.0, + 0.477642, + 0.510268, + 0.093199, + 0.268805 + }; + + fp_t const * const exp[2] = { e_kvo, e_sig }; + + typedef struct { uint_t k1; fp_t k2; } c_t; + c_t cv = { .k1 = k1, .k2 = (fp_t)k2 }; + + return ts_test(ts_kvo, ts_kvo_offset, x, exp, 15, (ts_constant_t *)&cv, &ts_kvo_p); +} diff --git a/test/lag.c b/test/lag.c new file mode 100644 index 0000000..67a2da1 --- /dev/null +++ b/test/lag.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lag( void ) +{ + uint_t const c[] = { 2 }; + + fp_t const x0[] = { 10, 20, 30, 40 }; + fp_t const e0[] = { 10, 20 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_lag, ts_lag_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)c, &ts_lag_p); +} diff --git a/test/laguerre.c b/test/laguerre.c new file mode 100644 index 0000000..ba22ef8 --- /dev/null +++ b/test/laguerre.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_laguerre( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 0.500000, 2.000000, 3.000000 }; + fp_t const x1[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 }; + fp_t const e0[] = { 1.000000, 0.000000, 0.125000, -0.333333, 1.375000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_laguerre, ts_laguerre_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_laguerre_p); +} diff --git a/test/lambert_w.c b/test/lambert_w.c new file mode 100644 index 0000000..ce31037 --- /dev/null +++ b/test/lambert_w.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lambert_w( void ) +{ + fp_t const x0[] = { 0.010000, 0.500000, 0.900000, 2.000000, 5.000000 }; + fp_t const e0[] = { 0.009901, 0.351734, 0.529833, 0.852606, 1.326725 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_lambert_w, ts_lambert_w_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_lambert_w_p); +} diff --git a/test/laplace_cdf.c b/test/laplace_cdf.c new file mode 100644 index 0000000..f419866 --- /dev/null +++ b/test/laplace_cdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_laplace_cdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.067668, 0.183940, 0.500000, 0.816060, 0.932332 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_laplace_cdf, ts_laplace_cdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_laplace_cdf_p); +} diff --git a/test/laplace_pdf.c b/test/laplace_pdf.c new file mode 100644 index 0000000..19dbbd9 --- /dev/null +++ b/test/laplace_pdf.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_laplace_pdf( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.067668, 0.183940, 0.500000, 0.183940, 0.067668 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_laplace_pdf, ts_laplace_pdf_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_laplace_pdf_p); +} diff --git a/test/lcm.c b/test/lcm.c new file mode 100644 index 0000000..4a0fb0f --- /dev/null +++ b/test/lcm.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lcm( void ) +{ + fp_t const x0[] = { 4.000000, 6.000000, 12.000000, 7.000000, 0.000000 }; + fp_t const x1[] = { 6.000000, 8.000000, 18.000000, 3.000000, 5.000000 }; + fp_t const e0[] = { 12.000000, 24.000000, 36.000000, 21.000000, 0.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_lcm, ts_lcm_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_lcm_p); +} diff --git a/test/leaky_relu.c b/test/leaky_relu.c new file mode 100644 index 0000000..fab4dcc --- /dev/null +++ b/test/leaky_relu.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_leaky_relu( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { -0.030000, -0.010000, 0.000000, 1.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_leaky_relu, ts_leaky_relu_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_leaky_relu_p); +} diff --git a/test/legendre.c b/test/legendre.c new file mode 100644 index 0000000..95a2ba7 --- /dev/null +++ b/test/legendre.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_legendre( void ) +{ + fp_t const x0[] = { 0.000000, 0.500000, 1.000000, -1.000000, 0.300000 }; + fp_t const x1[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 }; + fp_t const e0[] = { 1.000000, 0.500000, 1.000000, -1.000000, 0.072937 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_legendre, ts_legendre_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_legendre_p); +} diff --git a/test/lerp.c b/test/lerp.c new file mode 100644 index 0000000..2a47e30 --- /dev/null +++ b/test/lerp.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lerp( void ) +{ + fp_t const x0[] = { 0.000000, 0.000000, 10.000000, 0.000000 }; + fp_t const x1[] = { 0.000000, 1.000000, 0.500000, 0.250000 }; + fp_t const x2[] = { 10.000000, 10.000000, 20.000000, 8.000000 }; + fp_t const e0[] = { 0.000000, 10.000000, 15.000000, 2.000000 }; + + fp_t const * const x[3] = { x0, x1, x2 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_lerp, ts_lerp_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_lerp_p); +} diff --git a/test/lgamma.c b/test/lgamma.c new file mode 100644 index 0000000..4a8b835 --- /dev/null +++ b/test/lgamma.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lgamma( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 3.000000, 0.500000, 5.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.693147, 0.572365, 3.178054 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_lgamma, ts_lgamma_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_lgamma_p); +} diff --git a/test/linear_regression.c b/test/linear_regression.c new file mode 100644 index 0000000..1dc13d6 --- /dev/null +++ b/test/linear_regression.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 5, 6, 7, 8 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression, ts_linear_regression_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_p); +} diff --git a/test/linear_regression_angle.c b/test/linear_regression_angle.c new file mode 100644 index 0000000..46ba135 --- /dev/null +++ b/test/linear_regression_angle.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression_angle( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 45.0, 45.0, 45.0, 45.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression_angle, ts_linear_regression_angle_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_angle_p); +} diff --git a/test/linear_regression_forecast.c b/test/linear_regression_forecast.c new file mode 100644 index 0000000..b1510d7 --- /dev/null +++ b/test/linear_regression_forecast.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression_forecast( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 6, 7, 8, 9 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression_forecast, ts_linear_regression_forecast_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_forecast_p); +} diff --git a/test/linear_regression_intercept.c b/test/linear_regression_intercept.c new file mode 100644 index 0000000..2fe6ead --- /dev/null +++ b/test/linear_regression_intercept.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression_intercept( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 1, 2, 3, 4 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression_intercept, ts_linear_regression_intercept_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_intercept_p); +} diff --git a/test/linear_regression_r2.c b/test/linear_regression_r2.c new file mode 100644 index 0000000..dd46324 --- /dev/null +++ b/test/linear_regression_r2.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression_r2( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 1.0, 1.0, 1.0, 1.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression_r2, ts_linear_regression_r2_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_r2_p); +} diff --git a/test/linear_regression_slope.c b/test/linear_regression_slope.c new file mode 100644 index 0000000..4d5fd08 --- /dev/null +++ b/test/linear_regression_slope.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_regression_slope( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 1, 1, 1, 1 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_linear_regression_slope, ts_linear_regression_slope_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_linear_regression_slope_p); +} diff --git a/test/linear_weighted_ma.c b/test/linear_weighted_ma.c new file mode 100644 index 0000000..5c7b212 --- /dev/null +++ b/test/linear_weighted_ma.c @@ -0,0 +1,23 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_linear_weighted_ma( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + fp_t const * const x[] = { x0 }; + + /* LMA with k=5: weights 1..5, sum_weights = 15 + * y[4] = (1*1 + 2*2 + 3*3 + 4*4 + 5*5) / 15 = (1+4+9+16+25)/15 = 55/15 = 3.666667 + * y[5] = (1*2 + 2*3 + 3*4 + 4*5 + 5*6) / 15 = (2+6+12+20+30)/15 = 70/15 = 4.666667 + * y[6] = (1*3 + 2*4 + 3*5 + 4*6 + 5*7) / 15 = (3+8+15+24+35)/15 = 85/15 = 5.666667 + * y[7] = (1*4 + 2*5 + 3*6 + 4*7 + 5*8) / 15 = (4+10+18+28+40)/15 = 100/15 = 6.666667 + * y[8] = (1*5 + 2*6 + 3*7 + 4*8 + 5*9) / 15 = (5+12+21+32+45)/15 = 115/15 = 7.666667 + * y[9] = (1*6 + 2*7 + 3*8 + 4*9 + 5*10) / 15 = (6+14+24+36+50)/15 = 130/15 = 8.666667 + */ + fp_t const e[] = { 3.666667, 4.666667, 5.666667, 6.666667, 7.666667, 8.666667 }; + fp_t const * const exp[] = { e }; + + uint_t c = 5; + return ts_test(ts_linear_weighted_ma, ts_linear_weighted_ma_offset, x, exp, 10, (ts_constant_t *)&c, &ts_linear_weighted_ma_p); +} diff --git a/test/log1p.c b/test/log1p.c new file mode 100644 index 0000000..86e5455 --- /dev/null +++ b/test/log1p.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_log1p( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, -0.500000, 2.000000, 0.001000 }; + fp_t const e0[] = { 0.000000, 0.693147, -0.693147, 1.098612, 0.001000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_log1p, ts_log1p_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_log1p_p); +} diff --git a/test/log1pm1.c b/test/log1pm1.c new file mode 100644 index 0000000..a4e9eaf --- /dev/null +++ b/test/log1pm1.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_log1pm1( void ) +{ + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, -0.500000 }; + fp_t const e0[] = { -1.000000, -0.306853, 0.098612, -1.693147 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_log1pm1, ts_log1pm1_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_log1pm1_p); +} diff --git a/test/log_return.c b/test/log_return.c new file mode 100644 index 0000000..691ed88 --- /dev/null +++ b/test/log_return.c @@ -0,0 +1,16 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_log_return( void ) +{ + fp_t const x0[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + fp_t const e0[] = { log(8.0 / 1.0), log(16.0 / 2.0), log(32.0 / 4.0), log(64.0 / 8.0), log(128.0 / 16.0) }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_log_return, ts_log_return_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_log_return_p); +} diff --git a/test/logarithm.c b/test/logarithm.c new file mode 100644 index 0000000..5ec3b4b --- /dev/null +++ b/test/logarithm.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_logarithm( void ) +{ + fp_t const c = 2; + + fp_t const x0[] = { 8, 27, 100 }; + fp_t const e0[] = { 3, 4.754888, 6.643856 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_logarithm, ts_logarithm_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_logarithm_p); +} diff --git a/test/logarithm_binary.c b/test/logarithm_binary.c new file mode 100644 index 0000000..5013671 --- /dev/null +++ b/test/logarithm_binary.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_logarithm_binary( void ) +{ + fp_t const x0[] = { 1, 2, 4, 8, 16 }; + fp_t const e0[] = { 0, 1, 2, 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_logarithm_binary, ts_logarithm_binary_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_logarithm_binary_p); +} diff --git a/test/logarithm_decimal.c b/test/logarithm_decimal.c new file mode 100644 index 0000000..83da7a7 --- /dev/null +++ b/test/logarithm_decimal.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_logarithm_decimal( void ) +{ + fp_t const x0[] = { 1, 10, 100, 1000 }; + fp_t const e0[] = { 0, 1, 2, 3 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_logarithm_decimal, ts_logarithm_decimal_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_logarithm_decimal_p); +} diff --git a/test/logarithm_natural.c b/test/logarithm_natural.c new file mode 100644 index 0000000..4f955ee --- /dev/null +++ b/test/logarithm_natural.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_logarithm_natural( void ) +{ + fp_t const x0[] = { 1, 2.718282, 7.389056 }; + fp_t const e0[] = { 0, 1, 2 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_logarithm_natural, ts_logarithm_natural_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_logarithm_natural_p); +} diff --git a/test/logb.c b/test/logb.c new file mode 100644 index 0000000..346e144 --- /dev/null +++ b/test/logb.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_logb( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 4.000000, 8.000000, 0.500000 }; + fp_t const e0[] = { 0.000000, 1.000000, 2.000000, 3.000000, -1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_logb, ts_logb_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_logb_p); +} diff --git a/test/lowest_low.c b/test/lowest_low.c new file mode 100644 index 0000000..1ce6951 --- /dev/null +++ b/test/lowest_low.c @@ -0,0 +1,19 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_lowest_low( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 5, 7, 9, 6, 8, 10, 12 }; + fp_t const x2[] = { 1, 3, 2, 4, 1, 3, 2 }; + fp_t const x3[] = { 3, 5, 4, 6, 3, 5, 4 }; + fp_t const x4[] = { 1, 1, 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 1, 2, 1, 1, 1 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_lowest_low, ts_lowest_low_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_lowest_low_p); +} diff --git a/test/macd.c b/test/macd.c new file mode 100644 index 0000000..34d6365 --- /dev/null +++ b/test/macd.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_macd( void ) +{ + fp_t const x[] = { -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10 }; + + fp_t const * const inp[1] = { x }; + + /* fast=3, slow=5, signal=2 */ + /* Standard EMA: alpha = 2/(k+1) */ + /* fast_a=2/4=0.5, slow_a=2/6=0.333, sig_a=2/3=0.667 */ + + fp_t const em[4] = { 1.91968879, 2.10010502, 2.22689293, 2.31467341 }; + fp_t const es[4] = { 1.91968879, 2.03996628, 2.16458405, 2.26464362 }; + fp_t const eh[4] = { 0.00000000, 0.06013875, 0.06230888, 0.05002979 }; + + fp_t const * const exp[3] = { em, es, eh }; + + typedef struct { fp_t fast; fp_t slow; fp_t signal; } c_t; + c_t c = { .fast = 3, .slow = 5, .signal = 2 }; + + return ts_test(ts_macd, ts_macd_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_macd_p); +} diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..2854d1c --- /dev/null +++ b/test/main.c @@ -0,0 +1,73 @@ +#define _POSIX_C_SOURCE 200809L +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include "ts.h" + +#include "main_generated.c" + +int main(void) +{ + struct sigaction sa = {0}; + sa.sa_handler = SIG_IGN; + sigaction(SIGALRM, &sa, NULL); + + size_t passed = 0; + size_t failed = 0; + size_t crashed = 0; + size_t skipped = 0; + + for (size_t i = 0; i < NUM_TESTS; i++) + { + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, " SKIP %s (fork failed: %s)\n", tests[i].name, strerror(errno)); + skipped++; + continue; + } + if (pid == 0) { + bool ok = tests[i].func(); + _exit(ok ? 0 : 1); + } + int status = 0; + alarm(30); + pid_t result = waitpid(pid, &status, 0); + alarm(0); + if (result < 0) { + if (errno == EINTR) { + kill(pid, SIGKILL); + waitpid(pid, &status, 0); + fprintf(stderr, " CRASH %s (timeout)\n", tests[i].name); + crashed++; + } else { + fprintf(stderr, " CRASH %s (waitpid failed: %s)\n", tests[i].name, strerror(errno)); + crashed++; + } + continue; + } + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + printf(" PASS %s\n", tests[i].name); + passed++; + } else if (WIFEXITED(status)) { + fprintf(stderr, " FAIL %s\n", tests[i].name); + failed++; + } else if (WIFSIGNALED(status)) { + fprintf(stderr, " CRASH %s (signal %d)\n", tests[i].name, WTERMSIG(status)); + crashed++; + } else { + fprintf(stderr, " CRASH %s\n", tests[i].name); + crashed++; + } + } + + printf("\n--- %zu tests: %zu passed, %zu failed, %zu crashed, %zu skipped ---\n", + NUM_TESTS, passed, failed, crashed, skipped); + + return (failed > 0 || crashed > 0) ? 1 : 0; +} diff --git a/test/manhattan_distance.c b/test/manhattan_distance.c new file mode 100644 index 0000000..6dfc8cf --- /dev/null +++ b/test/manhattan_distance.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_manhattan_distance( void ) +{ + fp_t const x0[] = { 3.000000, -5.000000, 0.000000, -1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, -2.000000 }; + fp_t const e0[] = { 7.000000, 17.000000, 5.000000, 3.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_manhattan_distance, ts_manhattan_distance_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_manhattan_distance_p); +} diff --git a/test/manhattan_norm.c b/test/manhattan_norm.c new file mode 100644 index 0000000..7395c0a --- /dev/null +++ b/test/manhattan_norm.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_manhattan_norm( void ) +{ + fp_t const x0[] = { 3.000000, -5.000000, 0.000000, 1.000000 }; + fp_t const x1[] = { 4.000000, 12.000000, 5.000000, -2.000000 }; + fp_t const e0[] = { 7.000000, 17.000000, 5.000000, 3.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_manhattan_norm, ts_manhattan_norm_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_manhattan_norm_p); +} diff --git a/test/market_facilitation_index.c b/test/market_facilitation_index.c new file mode 100644 index 0000000..78cde6f --- /dev/null +++ b/test/market_facilitation_index.c @@ -0,0 +1,23 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_market_facilitation_index( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14 }; + fp_t const h[] = { 12, 13, 14, 15, 16 }; + fp_t const l[] = { 8, 9, 10, 11, 12 }; + fp_t const c[] = { 11, 12, 13, 14, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[5] = { 0 }; + for( uint_t i = 0; i < 5; ++i ) + { + e[i] = ( v[i] != 0 ) ? ( h[i] - l[i] ) / v[i] : 0; + } + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_market_facilitation_index, ts_market_facilitation_index_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_market_facilitation_index_p); +} diff --git a/test/mass_index.c b/test/mass_index.c new file mode 100644 index 0000000..2075b59 --- /dev/null +++ b/test/mass_index.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_mass_index( void ) +{ + /* Use constant H-L so ratio converges to 1, sum = 26 */ + fp_t o[30] = { 0 }; + fp_t h[30]; + fp_t l[30]; + fp_t c[30] = { 0 }; + fp_t v[30] = { 0 }; + + for ( uint_t i = 0; i < 30; ++i ) + { + h[i] = 11; + l[i] = 9; + } + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t const e[4] = { 26.0, 26.0, 26.0, 26.0 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_mass_index, ts_mass_index_offset, x, exp, 30, (ts_constant_t *)NULL, &ts_mass_index_p); +} diff --git a/test/maximum.c b/test/maximum.c new file mode 100644 index 0000000..585f30b --- /dev/null +++ b/test/maximum.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_maximum( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 3, 2, 5, 4, 1 }; + fp_t const e0[] = { 3, 5, 5, 5 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_maximum, ts_maximum_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_maximum_p); +} diff --git a/test/median_price.c b/test/median_price.c new file mode 100644 index 0000000..3cdb1cc --- /dev/null +++ b/test/median_price.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_median_price( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 2, 4, 6, 8, 10 }; + fp_t const x2[] = { 0, 2, 4, 6, 8 }; + fp_t const x3[] = { 1, 1, 1, 1, 1 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 1, 3, 5, 7, 9 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_median_price, ts_median_price_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_median_price_p); +} diff --git a/test/mesa_sine_wave.c b/test/mesa_sine_wave.c new file mode 100644 index 0000000..598de80 --- /dev/null +++ b/test/mesa_sine_wave.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_mesa_sine_wave( void ) +{ + /* k=3 */ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + fp_t const * const inp[1] = { x }; + + /* SMA(3): 11, 12, 13, 14, 15, 16, 17, 18 */ + /* ROC: 13-11=2, 14-12=2, 15-13=2, 16-14=2, 17-15=2, 18-16=2, 19-18=2 */ + /* EMA(5) of constant 2 converges to 2, clamped to 1 */ + /* All sines = 1, all leads = 0 */ + fp_t const es[] = { 1, 1, 1, 1, 1, 1, 1 }; + fp_t const el[] = { 0, 0, 0, 0, 0, 0, 0 }; + fp_t const * const exp[2] = { es, el }; + + uint_t k = 3; + return ts_test(ts_mesa_sine_wave, ts_mesa_sine_wave_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_mesa_sine_wave_p); +} diff --git a/test/mfi.c b/test/mfi.c new file mode 100644 index 0000000..1d83db4 --- /dev/null +++ b/test/mfi.c @@ -0,0 +1,59 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_mfi( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t tp[10] = { 0 }; + for( uint_t i = 0; i < 10; ++i ) + { + tp[i] = ( h[i] + l[i] + c[i] ) / 3.0; + } + + fp_t pos_sum = 0; + fp_t neg_sum = 0; + + for( uint_t i = 1; i <= k; ++i ) + { + fp_t raw_mf = tp[i] * v[i]; + if( tp[i] > tp[i - 1] ) + { + pos_sum += raw_mf; + } + else + { + neg_sum += raw_mf; + } + } + + fp_t e[10] = { 0 }; + e[0] = ( neg_sum == 0 ) ? 100.0 : 100.0 - 100.0 / ( 1.0 + pos_sum / neg_sum ); + + for( uint_t i = k + 1; i < 10; ++i ) + { + fp_t cur_raw_mf = tp[i] * v[i]; + if( tp[i] > tp[i - 1] ) pos_sum += cur_raw_mf; + else neg_sum += cur_raw_mf; + + fp_t old_raw_mf = tp[i - k] * v[i - k]; + if( tp[i - k] > tp[i - k - 1] ) pos_sum -= old_raw_mf; + else neg_sum -= old_raw_mf; + + e[i - k] = ( neg_sum == 0 ) ? 100.0 : 100.0 - 100.0 / ( 1.0 + pos_sum / neg_sum ); + } + + fp_t const * const e_arr[1] = { e }; + + uint_t c_val = k; + + return ts_test(ts_mfi, ts_mfi_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_mfi_p); +} diff --git a/test/midpoint.c b/test/midpoint.c new file mode 100644 index 0000000..8048285 --- /dev/null +++ b/test/midpoint.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_midpoint( void ) +{ + fp_t const x0[] = { 1, 3, 2, 5, 4, 6, 3 }; + fp_t const e0[] = { 2, 3.5, 3.5, 5, 4.5 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_midpoint, ts_midpoint_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_midpoint_p); +} diff --git a/test/midprice.c b/test/midprice.c new file mode 100644 index 0000000..f1634b0 --- /dev/null +++ b/test/midprice.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_midprice( void ) +{ + fp_t const x0[] = { 10, 12, 11, 14, 13, 15, 12 }; + fp_t const e0[] = { 11, 12.5, 12.5, 14, 13.5 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t const k = 3; + + return ts_test(ts_midprice, ts_midprice_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_midprice_p); +} diff --git a/test/minimum.c b/test/minimum.c new file mode 100644 index 0000000..2d4089d --- /dev/null +++ b/test/minimum.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_minimum( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 3, 2, 5, 4, 1 }; + fp_t const e0[] = { 1, 2, 2, 1 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_minimum, ts_minimum_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_minimum_p); +} diff --git a/test/minmax.c b/test/minmax.c new file mode 100644 index 0000000..64ba78b --- /dev/null +++ b/test/minmax.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_minmax( void ) +{ + uint_t k = 3; + + fp_t const x[] = { 1, 3, 2, 5, 4, 1, 6, 3, 2, 4 }; + fp_t const emin[] = { 1, 2, 2, 1, 1, 1, 2, 2 }; + fp_t const emax[] = { 3, 5, 5, 5, 6, 6, 6, 4 }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[2] = { emin, emax }; + + return ts_test(ts_minmax, ts_minmax_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_minmax_p); +} diff --git a/test/missing_bar_count.c b/test/missing_bar_count.c new file mode 100644 index 0000000..e15e634 --- /dev/null +++ b/test/missing_bar_count.c @@ -0,0 +1,28 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_missing_bar_count( void ) +{ + fp_t threshold = 0.5; + + fp_t const x[] = { 10, 10.1, 10.2, 10.1, 11, 11.1, 11.2, 11.3, 12.5, 12.6 }; + + fp_t const * const inp[1] = { x }; + + /* i=0: 0 + * i=1: |10.1-10|=0.1 < 0.5, count=1 + * i=2: |10.2-10.1|=0.1 < 0.5, count=2 + * i=3: |10.1-10.2|=0.1 < 0.5, count=3 + * i=4: |11-10.1|=0.9 >= 0.5, count=0 + * i=5: |11.1-11|=0.1 < 0.5, count=1 + * i=6: |11.2-11.1|=0.1 < 0.5, count=2 + * i=7: |11.3-11.2|=0.1 < 0.5, count=3 + * i=8: |12.5-11.3|=1.2 >= 0.5, count=0 + * i=9: |12.6-12.5|=0.1 < 0.5, count=1 + */ + fp_t const e[] = { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_missing_bar_count, ts_missing_bar_count_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&threshold, &ts_missing_bar_count_p); +} diff --git a/test/momentum.c b/test/momentum.c new file mode 100644 index 0000000..78772dc --- /dev/null +++ b/test/momentum.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_momentum( void ) +{ + /* k=3, close: 10, 12, 14, 16, 18, 20 */ + /* i=3: 16-10 = 6 */ + /* i=4: 18-12 = 6 */ + /* i=5: 20-14 = 6 */ + fp_t const x[] = { 10, 12, 14, 16, 18, 20 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 6, 6, 6 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_momentum, ts_momentum_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_momentum_p); +} diff --git a/test/money_flow_multiplier.c b/test/money_flow_multiplier.c new file mode 100644 index 0000000..58ca4c9 --- /dev/null +++ b/test/money_flow_multiplier.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_money_flow_multiplier( void ) +{ + fp_t const o[] = { 9, 10, 11, 12, 13 }; + fp_t const h[] = { 10, 11, 12, 13, 14 }; + fp_t const l[] = { 8, 9, 10, 11, 12 }; + fp_t const c[] = { 9.5, 10.5, 11.5, 12.5, 13.5 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + fp_t const e0[] = { 0.5, 0.5, 0.5, 0.5, 0.5 }; + + fp_t const * const x[5] = { o, h, l, c, v }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_money_flow_multiplier, ts_money_flow_multiplier_offset, x, e, sizeof( o ) / sizeof( *o ), (ts_constant_t *)NULL, &ts_money_flow_multiplier_p); +} diff --git a/test/moving_linear_regression.c b/test/moving_linear_regression.c new file mode 100644 index 0000000..dd7add0 --- /dev/null +++ b/test/moving_linear_regression.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_moving_linear_regression( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 5, 6, 7, 8 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_moving_linear_regression, ts_moving_linear_regression_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_moving_linear_regression_p); +} diff --git a/test/moving_linear_regression_r2.c b/test/moving_linear_regression_r2.c new file mode 100644 index 0000000..9013584 --- /dev/null +++ b/test/moving_linear_regression_r2.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_moving_linear_regression_r2( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 1.0, 1.0, 1.0, 1.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_moving_linear_regression_r2, ts_moving_linear_regression_r2_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_moving_linear_regression_r2_p); +} diff --git a/test/multiplication.c b/test/multiplication.c new file mode 100644 index 0000000..ad61fb0 --- /dev/null +++ b/test/multiplication.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_multiplication( void ) +{ + fp_t const x0[] = { 3, 5, 7 }; + fp_t const x1[] = { 2, 4, 6 }; + fp_t const e0[] = { 6, 20, 42 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_multiplication, ts_multiplication_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_multiplication_p); +} diff --git a/test/nan_fn.c b/test/nan_fn.c new file mode 100644 index 0000000..e0f29ec --- /dev/null +++ b/test/nan_fn.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_nan_fn( void ) +{ + fp_t const x0[] = { 1, 2, 3 }; + fp_t out[3]; + ts_nan_fn( (fp_t*[]){out}, 1, (fp_t const *[]){x0}, 1, 3, NULL ); + /* Verify all outputs are NaN */ + for ( uint_t i = 0; i < 3; ++i ) + if ( out[i] == out[i] ) return false; /* not NaN */ + return true; +} diff --git a/test/natr.c b/test/natr.c new file mode 100644 index 0000000..614754f --- /dev/null +++ b/test/natr.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_natr( void ) +{ + fp_t const o[] = { 9, 11, 10, 12, 12, 13, 11, 14, 13, 15 }; + fp_t const h[] = { 10, 12, 11, 13, 12, 14, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 11, 10, 12, 11, 13, 12, 14 }; + fp_t const c[] = { 9, 10, 12, 11, 13, 11, 14, 12, 15, 13 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* ATR values: 2.0, 1.66667, 2.0, 1.66667, 1.66667, 1.66667, 2.0 */ + /* C at idx 3-9: 11, 13, 11, 14, 12, 15, 13 */ + /* NATR = ATR/C*100 */ + fp_t const e[] = { 18.18182, 12.82051, 18.18182, 11.90476, 13.88889, 11.11111, 15.38462 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_natr, ts_natr_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_natr_p); +} diff --git a/test/nearbyint.c b/test/nearbyint.c new file mode 100644 index 0000000..b17e10c --- /dev/null +++ b/test/nearbyint.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_nearbyint( void ) +{ + fp_t const x0[] = { -2.700000, -1.300000, 0.300000, 0.700000, 1.500000, 2.700000 }; + fp_t const e0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 2.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_nearbyint, ts_nearbyint_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_nearbyint_p); +} diff --git a/test/nextafter.c b/test/nextafter.c new file mode 100644 index 0000000..c7e882b --- /dev/null +++ b/test/nextafter.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_nextafter( void ) +{ + fp_t const x0[] = { 1.000000, 1.000000, 0.000000 }; + fp_t const x1[] = { 2.000000, 0.000000, 1.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 0.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_nextafter, ts_nextafter_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_nextafter_p); +} diff --git a/test/nexttoward.c b/test/nexttoward.c new file mode 100644 index 0000000..32d41cc --- /dev/null +++ b/test/nexttoward.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_nexttoward( void ) +{ + fp_t const x0[] = { 1.000000, 1.000000, 0.000000 }; + fp_t const x1[] = { 2.000000, 0.000000, 1.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 0.000000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_nexttoward, ts_nexttoward_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_nexttoward_p); +} diff --git a/test/none.c b/test/none.c new file mode 100644 index 0000000..80d9f9e --- /dev/null +++ b/test/none.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_none( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 1, 2, 3, 4, 5 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_none, ts_none_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_none_p); +} diff --git a/test/nvi.c b/test/nvi.c new file mode 100644 index 0000000..87b3ddb --- /dev/null +++ b/test/nvi.c @@ -0,0 +1,38 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_nvi( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t nvi = 1000; + + nvi = 1000; + fp_t e[5] = { nvi }; + + e[1] = nvi; + if( v[1] < v[0] ) { fp_t r = (c[1] - c[0]) / c[0]; nvi = nvi * (1 + r); e[1] = nvi; } + else { e[1] = nvi; } + + if( v[2] < v[1] ) { fp_t r = (c[2] - c[1]) / c[1]; nvi = nvi * (1 + r); } + else { /* unchanged */ } + e[2] = nvi; + + if( v[3] < v[2] ) { fp_t r = (c[3] - c[2]) / c[2]; nvi = nvi * (1 + r); } + else { /* unchanged */ } + e[3] = nvi; + + if( v[4] < v[3] ) { fp_t r = (c[4] - c[3]) / c[3]; nvi = nvi * (1 + r); } + else { /* unchanged */ } + e[4] = nvi; + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_nvi, ts_nvi_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_nvi_p); +} diff --git a/test/obv.c b/test/obv.c new file mode 100644 index 0000000..112740e --- /dev/null +++ b/test/obv.c @@ -0,0 +1,18 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_obv( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t const e[] = { 200, -100, 300, 300 }; + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_obv, ts_obv_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_obv_p); +} diff --git a/test/on_balance_volume_rate.c b/test/on_balance_volume_rate.c new file mode 100644 index 0000000..65e3ac1 --- /dev/null +++ b/test/on_balance_volume_rate.c @@ -0,0 +1,45 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_on_balance_volume_rate( void ) +{ + uint_t k = TS_TEST_K; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t obv_buf[10] = { 0 }; + fp_t obv = 0; + + for( uint_t i = 1; i < 10; ++i ) + { + if( c[i] > c[i-1] ) + { + obv += v[i]; + } + else if( c[i] < c[i-1] ) + { + obv -= v[i]; + } + obv_buf[i] = obv; + } + + fp_t e[4] = { 0 }; + + for( uint_t i = k + 1; i < 10; ++i ) + { + fp_t obv_prev = obv_buf[i - k]; + e[i - (k + 1)] = obv_prev != 0 ? ( obv_buf[i] - obv_prev ) / obv_prev * 100 : 0; + } + + fp_t const * const e_arr[1] = { e }; + + fp_t c_val = k; + + return ts_test(ts_on_balance_volume_rate, ts_on_balance_volume_rate_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_on_balance_volume_rate_p); +} diff --git a/test/open_interest.c b/test/open_interest.c new file mode 100644 index 0000000..165c3c5 --- /dev/null +++ b/test/open_interest.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_open_interest( void ) +{ + fp_t const x[] = { 100, 200, 300, 400, 500 }; + fp_t const e[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_open_interest, ts_open_interest_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_open_interest_p); +} diff --git a/test/open_minus_close.c b/test/open_minus_close.c new file mode 100644 index 0000000..329b678 --- /dev/null +++ b/test/open_minus_close.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_open_minus_close( void ) +{ + fp_t const x0[] = { 5, 10, 15, 20, 25 }; + fp_t const x1[] = { 6, 11, 16, 21, 26 }; + fp_t const x2[] = { 0, 5, 10, 15, 20 }; + fp_t const x3[] = { 3, 8, 13, 18, 23 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 2, 2, 2, 2, 2 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_open_minus_close, ts_open_minus_close_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_open_minus_close_p); +} diff --git a/test/oscillator_cross.c b/test/oscillator_cross.c new file mode 100644 index 0000000..323718b --- /dev/null +++ b/test/oscillator_cross.c @@ -0,0 +1,40 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_oscillator_cross( void ) +{ + /* Data that goes down then up, producing a clear crossover */ + fp_t const x[] = { 10, 9, 8, 7, 6, 5, 6, 7, 8, 9 }; + + fp_t const * const inp[1] = { x }; + + /* fast=2, slow=4 */ + fp_t fast_e = 10, slow_e = 10; + fp_t fast_a = 1.0 / 2, slow_a = 1.0 / 4; + fp_t e[6] = { 0 }; + + for( uint_t i = 1; i < 10; ++i ) + { + fp_t pf = fast_e, ps = slow_e; + fast_e = fast_e + fast_a * ( x[i] - fast_e ); + slow_e = slow_e + slow_a * ( x[i] - slow_e ); + + if( i >= 4 ) + { + if( fast_e > slow_e && pf <= ps ) + e[i - 4] = 1; + else if( fast_e < slow_e && pf >= ps ) + e[i - 4] = -1; + else + e[i - 4] = 0; + } + } + + fp_t const * const exp[1] = { e }; + + typedef struct { fp_t fast; fp_t slow; } c_t; + c_t c = { .fast = 2, .slow = 4 }; + + return ts_test(ts_oscillator_cross, ts_oscillator_cross_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_oscillator_cross_p); +} diff --git a/test/osma.c b/test/osma.c new file mode 100644 index 0000000..5a94bb9 --- /dev/null +++ b/test/osma.c @@ -0,0 +1,20 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_osma( void ) +{ + fp_t const x[] = { -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10 }; + + fp_t const * const inp[1] = { x }; + + /* fast=3, slow=5, signal=2 */ + fp_t const e[4] = { 0.24499095, 0.34043610, 0.35920247, 0.34054338 }; + + fp_t const * const exp[1] = { e }; + + typedef struct { fp_t fast; fp_t slow; fp_t signal; } c_t; + c_t c = { .fast = 3, .slow = 5, .signal = 2 }; + + return ts_test(ts_osma, ts_osma_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_osma_p); +} diff --git a/test/parabolic_sar.c b/test/parabolic_sar.c new file mode 100644 index 0000000..ffee1bf --- /dev/null +++ b/test/parabolic_sar.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_parabolic_sar( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* step=0.02, max=0.2, uptrend throughout */ + fp_t const esar[] = { 9.000000, 9.000000, 9.060000, 9.217600, 9.504544, 9.944180, 10.549762, 11.323791, 12.258460, 13.337107 }; + fp_t const edir[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + fp_t const * const exp[2] = { esar, edir }; + + typedef struct { fp_t step; fp_t max_af; } c_t; + c_t consts = { .step = 0.02, .max_af = 0.2 }; + return ts_test(ts_parabolic_sar, ts_parabolic_sar_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&consts, &ts_parabolic_sar_p); +} diff --git a/test/performance_index.c b/test/performance_index.c new file mode 100644 index 0000000..e31ec98 --- /dev/null +++ b/test/performance_index.c @@ -0,0 +1,16 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_performance_index( void ) +{ + fp_t const x0[] = { 10, 20, 30, 40, 50 }; + fp_t const x1[] = { 11, 22, 33, 44, 55 }; + fp_t const x2[] = { 9, 18, 28, 38, 48 }; + fp_t const x3[] = { 12, 21, 31, 41, 51 }; + fp_t const e0[] = { 20, 5, 3.333333, 2.5, 2 }; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_performance_index, ts_performance_index_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_performance_index_p); +} diff --git a/test/performance_ratio.c b/test/performance_ratio.c new file mode 100644 index 0000000..74ff1bd --- /dev/null +++ b/test/performance_ratio.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_performance_ratio( void ) +{ + uint_t k = 3; + + fp_t const x[] = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 }; + + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 123.693169, 124.917973, 126.142776, 127.367579, 128.592379, 129.817179, 131.041978 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_performance_ratio, ts_performance_ratio_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_performance_ratio_p); +} diff --git a/test/pivot_points.c b/test/pivot_points.c new file mode 100644 index 0000000..08138b6 --- /dev/null +++ b/test/pivot_points.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_pivot_points( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const c[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + fp_t const v[] = { 100, 200, 150, 180, 160, 190, 210, 170, 200, 220 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + /* Using prev bar OHLC */ + /* i=1: prev=bar0, P=(12+9+11)/3=10.667, R1=2*10.667-9=12.333, S1=2*10.667-12=9.333, R2=10.667+3=13.667, S2=10.667-3=7.667 */ + /* i=2: prev=bar1, P=(14+11+13)/3=12.667, R1=2*12.667-11=14.333, S1=2*12.667-14=11.333, R2=12.667+3=15.667, S2=12.667-3=9.667 */ + fp_t const ep[] = { 10.666667, 12.666667, 11.666667, 14.666667, 13.666667, 15.666667, 16.666667, 14.666667, 17.666667 }; + fp_t const er1[] = { 12.333333, 14.333333, 13.333333, 16.333333, 15.333333, 17.333333, 18.333333, 16.333333, 19.333333 }; + fp_t const es1[] = { 9.333333, 11.333333, 10.333333, 13.333333, 12.333333, 14.333333, 15.333333, 13.333333, 16.333333 }; + fp_t const er2[] = { 13.666667, 15.666667, 14.666667, 17.666667, 16.666667, 18.666667, 19.666667, 17.666667, 20.666667 }; + fp_t const es2[] = { 7.666667, 9.666667, 8.666667, 11.666667, 10.666667, 12.666667, 13.666667, 11.666667, 14.666667 }; + fp_t const * const exp[5] = { ep, er1, es1, er2, es2 }; + + return ts_test(ts_pivot_points, ts_pivot_points_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)NULL, &ts_pivot_points_p); +} + diff --git a/test/plot.c b/test/plot.c new file mode 100644 index 0000000..f697af7 --- /dev/null +++ b/test/plot.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_plot( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5 }; + fp_t const e[] = { 1, 2, 3, 4, 5 }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_plot, ts_plot_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_plot_p); +} diff --git a/test/poisson_pdf.c b/test/poisson_pdf.c new file mode 100644 index 0000000..0dfc9f2 --- /dev/null +++ b/test/poisson_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_poisson_pdf( void ) +{ + fp_t const c[] = { 3.000000 }; + + fp_t const x0[] = { 0.000000, 1.000000, 2.000000, 3.000000, 4.000000, 5.000000 }; + fp_t const e0[] = { 0.049787, 0.149361, 0.224042, 0.224042, 0.168031, 0.100819 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_poisson_pdf, ts_poisson_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_poisson_pdf_p); +} diff --git a/test/polynomial.c b/test/polynomial.c new file mode 100644 index 0000000..1591343 --- /dev/null +++ b/test/polynomial.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_polynomial( void ) +{ + /* y = x0 ^ x1 */ + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const x1[] = { 0, 1, 2, 3, 4 }; + fp_t const e0[] = { 1, 2, 9, 64, 625 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_polynomial, ts_polynomial_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), NULL, &ts_polynomial_p); +} diff --git a/test/power.c b/test/power.c new file mode 100644 index 0000000..07bf08f --- /dev/null +++ b/test/power.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_power( void ) +{ + fp_t const c[] = { 2.0 }; + + fp_t const x0[] = { 0, 1, 2, 3, 4 }; + fp_t const e0[] = { 0, 1, 4, 9, 16 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_power, ts_power_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_power_p); +} diff --git a/test/ppo.c b/test/ppo.c new file mode 100644 index 0000000..c513567 --- /dev/null +++ b/test/ppo.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_ppo( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + fp_t const * const inp[1] = { x }; + + /* fast=3, slow=5, signal=2 */ + fp_t const ep[5] = { 7.73841405, 8.63701135, 9.23626984, 9.58993759, 9.75198730 }; + fp_t const es[5] = { 7.11991901, 7.87846518, 8.55736751, 9.07365255, 9.41281993 }; + + fp_t const * const exp[2] = { ep, es }; + + typedef struct { fp_t fast; fp_t slow; fp_t signal; } c_t; + c_t c = { .fast = 3, .slow = 5, .signal = 2 }; + + return ts_test(ts_ppo, ts_ppo_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_ppo_p); +} diff --git a/test/pretty_good_oscillator.c b/test/pretty_good_oscillator.c new file mode 100644 index 0000000..320c816 --- /dev/null +++ b/test/pretty_good_oscillator.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_pretty_good_oscillator( void ) +{ + fp_t const x[] = { 95, 100, 105, 110, 102, 115, 120, 112, 125, 130, 122, 135 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { + 0.0000000000, + 0.0511756570, + 0.1007296091, + 0.1485166224, + 0.0631698925, + 0.1924376246, + 0.2363349679, + 0.1492577606, + 0.2731834852, + 0.3129126874, + 0.2246375214, + 0.3425844656 + }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_pretty_good_oscillator, ts_pretty_good_oscillator_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_pretty_good_oscillator_p); +} diff --git a/test/price_channel.c b/test/price_channel.c new file mode 100644 index 0000000..ba16e69 --- /dev/null +++ b/test/price_channel.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_price_channel( void ) +{ + fp_t const x[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const * const inp[1] = { x }; + + /* k=3, offset=2, y_n=8 */ + /* HH3: 12,14,14,15,16,16,17,18 */ + /* LL3: 10,11,11,13,13,14,14,14 */ + /* SMA3: 11,12.333,12.667,14,14.667,15,15.667,16.333 */ + fp_t const eu[] = { 12, 14, 14, 15, 16, 16, 17, 18 }; + fp_t const em[] = { 11, 12.33333, 12.66667, 14, 14.66667, 15, 15.66667, 16.33333 }; + fp_t const el[] = { 10, 11, 11, 13, 13, 14, 14, 14 }; + fp_t const * const exp[3] = { eu, em, el }; + + uint_t k = 3; + return ts_test(ts_price_channel, ts_price_channel_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_price_channel_p); +} diff --git a/test/price_momentum_oscillator.c b/test/price_momentum_oscillator.c new file mode 100644 index 0000000..31b7724 --- /dev/null +++ b/test/price_momentum_oscillator.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_price_momentum_oscillator( void ) +{ + fp_t const x0[] = { 100, 110, 120, 130, 140, 150, 160 }; + fp_t const e0[] = { 20, 18.181818, 16.666667, 15.384615, 14.285714 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t k = 2; + + return ts_test(ts_price_momentum_oscillator, ts_price_momentum_oscillator_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_price_momentum_oscillator_p); +} diff --git a/test/price_rate_of_change.c b/test/price_rate_of_change.c new file mode 100644 index 0000000..ec3eb9c --- /dev/null +++ b/test/price_rate_of_change.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_price_rate_of_change( void ) +{ + fp_t const x[] = { 100, 110, 120, 130, 140, 150, 160, 170, 180, 190 }; + + fp_t const * const inp[1] = { x }; + + /* k=3 */ + /* PROC[3] = (130-100)/100*100 = 30 */ + /* PROC[4] = (140-110)/110*100 = 27.2727 */ + /* PROC[5] = (150-120)/120*100 = 25 */ + /* PROC[6] = (160-130)/130*100 = 23.0769 */ + /* PROC[7] = (170-140)/140*100 = 21.4286 */ + /* PROC[8] = (180-150)/150*100 = 20 */ + /* PROC[9] = (190-160)/160*100 = 18.75 */ + + fp_t e[] = { 30.0, 27.272727272727273, 25.0, 23.076923076923077, 21.428571428571427, 20.0, 18.75 }; + + fp_t const * const exp[1] = { e }; + + uint_t c_val = 3; + + return ts_test(ts_price_rate_of_change, ts_price_rate_of_change_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c_val, &ts_price_rate_of_change_p); +} diff --git a/test/prime_number_bands.c b/test/prime_number_bands.c new file mode 100644 index 0000000..6d0b211 --- /dev/null +++ b/test/prime_number_bands.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_prime_number_bands( void ) +{ + uint_t const c = 3; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 2, 3, 4 }; + fp_t const e1[] = { 3.632993, 4.632993, 5.632993 }; + fp_t const e2[] = { 0.367007, 1.367007, 2.367007 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[3] = { e0, e1, e2 }; + + return ts_test(ts_prime_number_bands, ts_prime_number_bands_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_prime_number_bands_p); +} diff --git a/test/prime_number_oscillator.c b/test/prime_number_oscillator.c new file mode 100644 index 0000000..aa6b575 --- /dev/null +++ b/test/prime_number_oscillator.c @@ -0,0 +1,23 @@ +#include <stddef.h> +#include <stdlib.h> +#include "ts.h" + +bool ts_test_prime_number_oscillator( void ) +{ + uint_t n = 25; + fp_t *x0 = malloc( n * sizeof( fp_t ) ); + if ( !x0 ) return false; + + for ( uint_t i = 0; i < n; ++i ) + x0[i] = (fp_t)(10 + i); + + fp_t const * const inp[1] = { x0 }; + + fp_t const e[] = { 2, 2 }; + fp_t const * const exp[1] = { e }; + + bool result = ts_test(ts_prime_number_oscillator, ts_prime_number_oscillator_offset, inp, exp, n, (ts_constant_t *)NULL, &ts_prime_number_oscillator_p); + + free( x0 ); + return result; +} diff --git a/test/prings_kst.c b/test/prings_kst.c new file mode 100644 index 0000000..8a1bd28 --- /dev/null +++ b/test/prings_kst.c @@ -0,0 +1,55 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_prings_kst( void ) +{ + fp_t const x[] = { + 100, 105, 98, 110, 102, 115, 108, 120, 112, 125, + 118, 130, 122, 135, 128, 140, 132, 145, 138, 150, + 142, 155, 148, 160, 152, 165, 158, 170, 162, 175, + 168, 180, 172, 185, 178, 190, 182, 195, 188, 200, + 192, 205, 198, 210, 202, 215, 208, 220, 212, 225, + 218, 230, 222, 235, 228, 240 + }; + + fp_t const * const inp[1] = { x }; + + fp_t const e0[] = { + 51.9408578539013, 75.1308260917654, 78.103866719, + 94.8088438575893, 92.483818257572, 105.456228286493, + 101.394220195137, 111.288138429023, 104.640206278203, + 113.013133431368, 106.311034357386, 113.131203505727, + 105.038276407899, 111.304589974597, 103.842952160622, + 109.243663263608, 100.915323682376, 106.178344115008, + 98.7322514625897, 103.443195102946, 95.3595796482774, + 100.10652950864, 92.9706808772205, 97.3126510773803, + 89.6400548204848, 94.0861019609992, 87.3507958601018, + 91.4647371720775, 84.2384208045559, 88.4793019904416, + 82.1513111963106, 86.0996943377378, 79.3034333929425, + 83.3828886921002, 77.4376734462827, 81.2491274609335, + 74.8491800513007, 78.788567419848, 73.1922770738286, + 76.8810806798416, 70.8401912421373, 74.6513723261792, + 69.3696853171888, 72.9441719183728, 67.2269459044808, + 70.9178130955952, 65.9191399381158 + }; + fp_t const e1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 90.5830006631871, + 93.3867672592096, 95.0023006464817, 97.2684135038873, + 98.2396463668887, 99.8727643178523, 100.369037798198, + 101.478365981375, 101.407985694, 102.004280496626, + 101.595276867371, 101.826266646818, 101.017930772, + 100.90400561408, 99.9123400219729, 99.5873789038989, + 98.3439633934721, 97.811730714413, 96.5041138576241, + 95.8741917719308, 94.4197204010089, 93.677168099688, + 92.2364359867658, 91.4693432806373, 89.9486045446755, + 89.1278900631035, 87.6666129860009, 86.8644272953675, + 85.3625213898592, 84.5407771436078, 83.1222146348854, + 82.3420728905049, 80.9043376844589, 80.122717014674, + 78.7785880524883, 78.0492860357239, 76.6964935193185, + 75.9741584663531, 74.7172811503234 + }; + fp_t const * const exp[2] = { e0, e1 }; + + return ts_test(ts_prings_kst, ts_prings_kst_offset, inp, exp, 55, (ts_constant_t *)NULL, &ts_prings_kst_p); +} diff --git a/test/prings_special_k.c b/test/prings_special_k.c new file mode 100644 index 0000000..41e6a89 --- /dev/null +++ b/test/prings_special_k.c @@ -0,0 +1,23 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_prings_special_k( void ) +{ + /* k1=3, k2=2 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + +/* Monotonically increasing: %K_raw = 80 for all windows */ + fp_t const ek[] = { 80, 80, 80, 80, 80, 80, 80 }; + fp_t const ed[] = { 80, 80, 80, 80, 80, 80, 80 }; + fp_t const * const exp[2] = { ek, ed }; + + typedef struct { uint_t k1; uint_t k2; } c_t; + c_t constants = { .k1 = 3, .k2 = 2 }; + + return ts_test(ts_prings_special_k, ts_prings_special_k_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&constants, &ts_prings_special_k_p); +} diff --git a/test/psychological_line.c b/test/psychological_line.c new file mode 100644 index 0000000..711923c --- /dev/null +++ b/test/psychological_line.c @@ -0,0 +1,23 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_psychological_line( void ) +{ + fp_t const x0[] = { 10, 10, 20, 20, 30 }; + fp_t const x1[] = { 12, 11, 22, 19, 32 }; + fp_t const x2[] = { 9, 9, 19, 18, 29 }; + fp_t const x3[] = { 11, 10, 21, 20, 31 }; + + uint_t const k = 2; + + fp_t e[4]; + e[0] = 50; + e[1] = 50; + e[2] = 50; + e[3] = 50; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_psychological_line, ts_psychological_line_offset, x, exp, 5, (ts_constant_t *)&k, &ts_psychological_line_p); +} diff --git a/test/pvi.c b/test/pvi.c new file mode 100644 index 0000000..51b4e5c --- /dev/null +++ b/test/pvi.c @@ -0,0 +1,34 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_pvi( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t pvi = 1000; + fp_t full[5]; + full[0] = pvi; + + if( v[1] > v[0] ) { fp_t r = (c[1] - c[0]) / c[0]; pvi = pvi * (1 + r); } + full[1] = pvi; + + if( v[2] > v[1] ) { fp_t r = (c[2] - c[1]) / c[1]; pvi = pvi * (1 + r); } + full[2] = pvi; + + if( v[3] > v[2] ) { fp_t r = (c[3] - c[2]) / c[2]; pvi = pvi * (1 + r); } + full[3] = pvi; + + if( v[4] > v[3] ) { fp_t r = (c[4] - c[3]) / c[3]; pvi = pvi * (1 + r); } + full[4] = pvi; + + fp_t const e[] = { full[0], full[1], full[2], full[3], full[4] }; + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_pvi, ts_pvi_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_pvi_p); +} diff --git a/test/pvt.c b/test/pvt.c new file mode 100644 index 0000000..6abccb4 --- /dev/null +++ b/test/pvt.c @@ -0,0 +1,27 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_pvt( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t pvt = 0; + fp_t full[5] = { 0 }; + + for( uint_t i = 1; i < 5; ++i ) + { + pvt += ( c[i] / c[i-1] - 1 ) * v[i]; + full[i] = pvt; + } + + fp_t const e[] = { full[1], full[2], full[3], full[4] }; + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_pvt, ts_pvt_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_pvt_p); +} diff --git a/test/qstick.c b/test/qstick.c new file mode 100644 index 0000000..daa13d2 --- /dev/null +++ b/test/qstick.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_qstick( void ) +{ + fp_t const x0[] = { 10, 10, 10, 20, 20, 30 }; + fp_t const x1[] = { 12, 13, 12, 22, 23, 33 }; + fp_t const x2[] = { 9, 9, 10, 19, 20, 29 }; + fp_t const x3[] = { 11, 12, 11, 21, 22, 31 }; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + + uint_t k = 3; + fp_t e[4]; + e[0] = ((11 - 10) + (12 - 10) + (11 - 10)) / 3.0; + e[1] = e[0] + ((21 - 20) - (11 - 10)) / 3.0; + e[2] = e[1] + ((22 - 20) - (12 - 10)) / 3.0; + e[3] = e[2] + ((31 - 30) - (11 - 10)) / 3.0; + + fp_t const * const exp[1] = { e }; + + return ts_test(ts_qstick, ts_qstick_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_qstick_p); +} diff --git a/test/radian_degree.c b/test/radian_degree.c new file mode 100644 index 0000000..f3f74aa --- /dev/null +++ b/test/radian_degree.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_radian_degree( void ) +{ + fp_t const x[] = { 0, PI / 6, PI / 4, PI / 3, HALF_PI, PI, 3 * HALF_PI, 2 * PI }; + fp_t const e[] = { 0, 30, 45, 60, 90, 180, 270, 360 }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_radian_degree, ts_radian_degree_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_radian_degree_p); +} diff --git a/test/rainbow_ma.c b/test/rainbow_ma.c new file mode 100644 index 0000000..a5fefe3 --- /dev/null +++ b/test/rainbow_ma.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_rainbow_ma( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const e0[] = { 15.0, 16.0 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[] = { e0 }; + + return ts_test(ts_rainbow_ma, ts_rainbow_ma_offset, x, e, 21, (ts_constant_t *)NULL, &ts_rainbow_ma_p); +} diff --git a/test/rainbow_oscillator.c b/test/rainbow_oscillator.c new file mode 100644 index 0000000..a529ea2 --- /dev/null +++ b/test/rainbow_oscillator.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_rainbow_oscillator( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + + fp_t const e0[] = { 108.333333, 108.333333, 108.333333, 108.333333, 108.333333, 108.333333, 108.333333, 108.333333 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_rainbow_oscillator, ts_rainbow_oscillator_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_rainbow_oscillator_p); +} diff --git a/test/random_walk_index.c b/test/random_walk_index.c new file mode 100644 index 0000000..6702fca --- /dev/null +++ b/test/random_walk_index.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_random_walk_index( void ) +{ + fp_t const x0[] = { 10, 12, 11, 13, 12, 14, 13 }; + fp_t const * const x[] = { x0 }; + + uint_t k = 3; + + fp_t sum_abs = 2.0 + 1.0 + 2.0; + fp_t sum_net = ( 12 - 10 ) + ( 11 - 12 ) + ( 13 - 11 ); + fp_t const e0[] = { sum_abs / sum_net, 0, sum_abs / sum_net, 0 }; + + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_random_walk_index, ts_random_walk_index_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_random_walk_index_p); +} diff --git a/test/rank.c b/test/rank.c new file mode 100644 index 0000000..2c96247 --- /dev/null +++ b/test/rank.c @@ -0,0 +1,20 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_rank( void ) +{ + /* k=3, data: 10, 20, 15, 25, 5, 30 */ + /* i=2 (window [10,20,15]): current=15, <=: 10,15 => 2/3*100 = 66.667 */ + /* i=3 (window [20,15,25]): current=25, <=: 20,15,25 => 3/3*100 = 100.0 */ + /* i=4 (window [15,25,5]): current=5, <=: 5 => 1/3*100 = 33.333 */ + /* i=5 (window [25,5,30]): current=30, <=: 25,5,30 => 3/3*100 = 100.0 */ + fp_t const x[] = { 10, 20, 15, 25, 5, 30 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 66.66667, 100.0, 33.33333, 100.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_rank, ts_rank_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_rank_p); +} diff --git a/test/ratio.c b/test/ratio.c new file mode 100644 index 0000000..beb0f43 --- /dev/null +++ b/test/ratio.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_ratio( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5, 6 }; + fp_t const e0[] = { 4, 2.5, 2 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_ratio, ts_ratio_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_ratio_p); +} diff --git a/test/ravi.c b/test/ravi.c new file mode 100644 index 0000000..22fb09c --- /dev/null +++ b/test/ravi.c @@ -0,0 +1,66 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_ravi( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const cl[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const inp[4] = { o, h, l, cl }; + + /* k=3, k2=3 */ + /* Numerator per bar: (close-open) + (close-prev_close) */ + /* i=0: 1+0=1, i=1: 1+1=2, i=2: 1+1=2, i=3: 1+1=2, ... */ + /* Denominator per bar: abs(high-low) + abs(close-open) */ + /* i=0: 4+1=5, i=1: 4+1=5, ... */ + /* SMA3(num): (1+2+2)/3=5/3, (2+2+2)/3=2, (2+2+2)/3=2, ... */ + /* SMA3(den): 5, 5, 5, ... */ + /* RVI: 5/15=1/3, 2/5, 2/5, 2/5, ... */ + + fp_t e_rvi[6] = { 0 }; + fp_t e_sig[6] = { 0 }; + + fp_t num_val[] = { 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + fp_t den_val[] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + + fp_t num_sum = num_val[0] + num_val[1] + num_val[2]; + fp_t den_sum = den_val[0] + den_val[1] + den_val[2]; + + fp_t rvi_vals[10] = { 0 }; + for( uint_t i = 2; i < 10; ++i ) + { + if( i >= 3 ) + { + num_sum = num_sum - num_val[i - 3] + num_val[i]; + den_sum = den_sum - den_val[i - 3] + den_val[i]; + } + rvi_vals[i] = ( den_sum != 0 ) ? ( num_sum / 3.0 ) / ( den_sum / 3.0 ) : 0; + } + + uint_t offset = 4; + for( uint_t i = offset; i < 10; ++i ) + { + e_rvi[i - offset] = rvi_vals[i]; + } + + fp_t sig_a = 1.0 / 3; + fp_t sig_e = rvi_vals[2]; + for( uint_t i = 2; i < 10; ++i ) + { + sig_e = sig_e + sig_a * ( rvi_vals[i] - sig_e ); + if( i >= offset ) + { + e_sig[i - offset] = sig_e; + } + } + + fp_t const * const exp[2] = { e_rvi, e_sig }; + + typedef struct { uint_t k; fp_t k2; } c_t; + c_t cv = { .k = 3, .k2 = 3 }; + + return ts_test(ts_ravi, ts_ravi_offset, inp, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&cv, &ts_ravi_p); +} diff --git a/test/reciprocal.c b/test/reciprocal.c new file mode 100644 index 0000000..9396277 --- /dev/null +++ b/test/reciprocal.c @@ -0,0 +1,10 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" +bool ts_test_reciprocal( void ) { + fp_t const x0[] = { 1, 2, 3, 4, 5, 0.5, -1, 0 }; + fp_t const e0[] = { 1, 0.5, 0.333333, 0.25, 0.2, 2, -1, INFINITY }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_reciprocal, ts_reciprocal_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_reciprocal_p); +} diff --git a/test/regression_linear_least_squares.c b/test/regression_linear_least_squares.c new file mode 100644 index 0000000..871c73e --- /dev/null +++ b/test/regression_linear_least_squares.c @@ -0,0 +1,19 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_regression_linear_least_squares( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const x1[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; + + fp_t const e0[] = { 2, 2, 2, 2, 2 }; + fp_t const e1[] = { 0, 0, 0, 0, 0 }; + fp_t const e2[] = { 1, 1, 1, 1, 1 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[3] = { e0, e1, e2 }; + + return ts_test(ts_regression_linear_least_squares, ts_regression_linear_least_squares_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_regression_linear_least_squares_p); +} diff --git a/test/relative_strength.c b/test/relative_strength.c new file mode 100644 index 0000000..8b546ea --- /dev/null +++ b/test/relative_strength.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_relative_strength( void ) +{ + fp_t const x0[] = { 10, 12, 11, 13, 12 }; + fp_t const x1[] = { 10, 10, 10, 10, 10 }; + fp_t const e0[] = { 100, 120, 110, 130, 120 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_relative_strength, ts_relative_strength_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_relative_strength_p); +} diff --git a/test/relative_volatility.c b/test/relative_volatility.c new file mode 100644 index 0000000..730812b --- /dev/null +++ b/test/relative_volatility.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_relative_volatility( void ) +{ + fp_t const x0[] = { 10, 10, 10, 10, 20, 20, 20 }; + fp_t const * const x[] = { x0 }; + + /* k=3, offset=2, y_n=5 */ + /* y[2]: [10,10,10] -> 0 (overwritten by xi=3: also [10,10,10] -> 0) */ + /* y[3]: [10,10,20] mean=13.33, sd=4.71 -> 35.355339 */ + /* y[4]: [10,20,20] mean=16.67, sd=4.71 -> 28.284271 */ + /* y[5]: [20,20,20] -> 0 */ + /* y[6]: not written -> 0 */ + fp_t const e[] = { 0.0, 35.355339, 28.284271, 0.0, 0.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_relative_volatility, ts_relative_volatility_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_relative_volatility_p); +} diff --git a/test/relu.c b/test/relu.c new file mode 100644 index 0000000..fcdb229 --- /dev/null +++ b/test/relu.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_relu( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.000000, 1.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_relu, ts_relu_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_relu_p); +} diff --git a/test/remainder.c b/test/remainder.c new file mode 100644 index 0000000..735c5f3 --- /dev/null +++ b/test/remainder.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_remainder( void ) +{ + fp_t const x0[] = { 7.000000, 10.000000, -7.000000, 7.500000 }; + fp_t const x1[] = { 3.000000, 3.000000, 3.000000, 2.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, -1.000000, -0.500000 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_remainder, ts_remainder_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_remainder_p); +} diff --git a/test/remquo_dummy.c b/test/remquo_dummy.c new file mode 100644 index 0000000..1f49d73 --- /dev/null +++ b/test/remquo_dummy.c @@ -0,0 +1,11 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_remquo_dummy( void ) +{ + fp_t const x0[] = { 0, 1, 2, 3, 1.5, 2.5 }; + fp_t const e0[] = { 0.0, 0.0, 0.0, 0.0, -0.5, 0.5 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_remquo_dummy, ts_remquo_dummy_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_remquo_dummy_p); +} diff --git a/test/reversal.c b/test/reversal.c new file mode 100644 index 0000000..ccc8021 --- /dev/null +++ b/test/reversal.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_reversal( void ) +{ + /* close: 10, 12, 11, 13, 10, 12 */ + /* i=2: c=11, prev=12, prev2=10. 11<12 && 12>10 => -1 (top reversal) */ + /* i=3: c=13, prev=11, prev2=12. 13>11 && 11<12 => +1 (bottom reversal) */ + /* i=4: c=10, prev=13, prev2=11. 10<13 && 13>11 => -1 (top reversal) */ + /* i=5: c=12, prev=10, prev2=13. 12>10 && 10<13 => +1 (bottom reversal) */ + fp_t const x[] = { 10, 12, 11, 13, 10, 12 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { -1, 1, -1, 1 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_reversal, ts_reversal_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_reversal_p); +} diff --git a/test/rint.c b/test/rint.c new file mode 100644 index 0000000..9b1ec0d --- /dev/null +++ b/test/rint.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_rint( void ) +{ + fp_t const x0[] = { -2.700000, -1.300000, 0.300000, 0.700000, 1.500000, 2.700000 }; + fp_t const e0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 2.000000, 3.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_rint, ts_rint_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_rint_p); +} diff --git a/test/roc.c b/test/roc.c new file mode 100644 index 0000000..0e7ddfc --- /dev/null +++ b/test/roc.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_roc( void ) +{ + /* k=3, close: 10, 12, 14, 16, 18, 20 */ + /* i=3: (16-10)/10*100 = 60 */ + /* i=4: (18-12)/12*100 = 50 */ + /* i=5: (20-14)/14*100 = 42.857 */ + fp_t const x[] = { 10, 12, 14, 16, 18, 20 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 60, 50, 42.857143 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_roc, ts_roc_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_roc_p); +} diff --git a/test/rocp.c b/test/rocp.c new file mode 100644 index 0000000..589e161 --- /dev/null +++ b/test/rocp.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_rocp( void ) +{ + /* k=3, close: 10, 12, 14, 16, 18, 20 */ + /* i=3: (16-10)/10 = 0.6 */ + /* i=4: (18-12)/12 = 0.5 */ + /* i=5: (20-14)/14 = 0.42857 */ + fp_t const x[] = { 10, 12, 14, 16, 18, 20 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 0.6, 0.5, 0.42857 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_rocp, ts_rocp_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_rocp_p); +} diff --git a/test/rocr.c b/test/rocr.c new file mode 100644 index 0000000..60335af --- /dev/null +++ b/test/rocr.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_rocr( void ) +{ + /* k=3, close: 10, 12, 14, 16, 18, 20 */ + /* i=3: 16/10 = 1.6 */ + /* i=4: 18/12 = 1.5 */ + /* i=5: 20/14 = 1.42857 */ + fp_t const x[] = { 10, 12, 14, 16, 18, 20 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 1.6, 1.5, 1.42857 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_rocr, ts_rocr_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_rocr_p); +} diff --git a/test/rocr100.c b/test/rocr100.c new file mode 100644 index 0000000..45578ab --- /dev/null +++ b/test/rocr100.c @@ -0,0 +1,19 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_rocr100( void ) +{ + /* k=3, close: 10, 12, 14, 16, 18, 20 */ + /* i=3: 16/10*100 = 160 */ + /* i=4: 18/12*100 = 150 */ + /* i=5: 20/14*100 = 142.857 */ + fp_t const x[] = { 10, 12, 14, 16, 18, 20 }; + + fp_t const * const inp[1] = { x }; + fp_t const e[] = { 160, 150, 142.857143 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_rocr100, ts_rocr100_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_rocr100_p); +} diff --git a/test/root.c b/test/root.c new file mode 100644 index 0000000..6b9822b --- /dev/null +++ b/test/root.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_root( void ) +{ + fp_t const c[] = { 2.0 }; + + fp_t const x0[] = { 0, 1, 4, 9, 16 }; + fp_t const e0[] = { 0, 1, 2, 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_root, ts_root_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_root_p); +} diff --git a/test/root_cube.c b/test/root_cube.c new file mode 100644 index 0000000..c60f7cb --- /dev/null +++ b/test/root_cube.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_root_cube( void ) +{ + fp_t const x0[] = { -8, -1, 0, 1, 8, 27, 64 }; + fp_t const e0[] = { -2, -1, 0, 1, 2, 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_root_cube, ts_root_cube_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_root_cube_p); +} diff --git a/test/root_square.c b/test/root_square.c new file mode 100644 index 0000000..2ee33c6 --- /dev/null +++ b/test/root_square.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_root_square( void ) +{ + fp_t const x0[] = { 0, 1, 4, 9, 16, 25 }; + fp_t const e0[] = { 0, 1, 2, 3, 4, 5 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_root_square, ts_root_square_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_root_square_p); +} diff --git a/test/round_.c b/test/round_.c new file mode 100644 index 0000000..c9a29ad --- /dev/null +++ b/test/round_.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_round_( void ) +{ + fp_t const x0[] = { -2.7, -1.3, 0, 0.5, 1.3, 2.7, 3.9 }; + fp_t const e0[] = { -3, -1, 0, 1, 1, 3, 4 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_round_, ts_round__offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_round__p); +} diff --git a/test/rsi.c b/test/rsi.c new file mode 100644 index 0000000..040a08c --- /dev/null +++ b/test/rsi.c @@ -0,0 +1,30 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_rsi( void ) +{ + /* k=3, changes: +1, +1, +1, 0, -1, -1, -1, 0, +1 */ + fp_t const x[] = { 10, 11, 12, 13, 13, 12, 11, 10, 10, 11 }; + + fp_t const * const inp[1] = { x }; + + /* i=3: sum_gain=3, sum_loss=0, avg_gain=1, avg_loss=0 => RSI=100 */ + /* i=4: change=0, avg_gain=(1*2+0)/3=2/3, avg_loss=0 => RSI=100 */ + /* i=5: change=-1, avg_gain=(2/3*2+0)/3=4/9, avg_loss=(0*2+1)/3=1/3 */ + /* RS = (4/9)/(1/3) = 4/3, RSI = 100 - 100/(1+4/3) = 100 - 300/7 = 57.142857 */ + /* i=6: change=-1, avg_gain=(4/9*2+0)/3=8/27, avg_loss=(1/3*2+1)/3=5/9 */ + /* RS = (8/27)/(5/9) = 8/15, RSI = 100 - 100/(1+8/15) = 100 - 1500/23 = 34.782609 */ + /* i=7: change=-1, avg_gain=(8/27*2+0)/3=16/81, avg_loss=(5/9*2+1)/3=19/27 */ + /* RS = (16/81)/(19/27) = 16/57, RSI = 100 - 100/(1+16/57) = 100 - 5700/73 = 21.917808 */ + /* i=8: change=0, avg_gain=(16/81*2+0)/3=32/243, avg_loss=(19/27*2+0)/3=38/81 */ + /* RS = (32/243)/(38/81) = 16/57, RSI = 21.917808 */ + /* i=9: change=+1, avg_gain=(32/243*2+1)/3=307/729, avg_loss=(38/81*2+0)/3=76/243 */ + /* RS = (307/729)/(76/243) = 307/228, RSI = 100 - 100/(1+307/228) = 100 - 22800/535 = 57.383178 */ + + fp_t const e[] = { 100, 100, 57.142857, 34.782609, 21.917808, 21.917808, 57.383178 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_rsi, ts_rsi_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_rsi_p); +} diff --git a/test/running_count.c b/test/running_count.c new file mode 100644 index 0000000..45e35b1 --- /dev/null +++ b/test/running_count.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_running_count( void ) +{ + fp_t const x0[] = { 0, 1, 0, 2, 0, 3, 0, 4 }; + fp_t const e0[] = { 0, 1, 1, 2, 2, 3, 3, 4 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_running_count, ts_running_count_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_running_count_p); +} diff --git a/test/schaff_trend_cycle.c b/test/schaff_trend_cycle.c new file mode 100644 index 0000000..49c3791 --- /dev/null +++ b/test/schaff_trend_cycle.c @@ -0,0 +1,48 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_schaff_trend_cycle( void ) +{ + /* period=3, offset=52, y_n=48 */ + fp_t x[100] = { + 100.000000, 104.932803, 109.469637, 113.249904, 115.980586, 117.462425, 117.607714, 116.448140, + 114.131948, 110.910698, 107.116800, 103.133815, 99.362193, 96.183508, 93.926363, 92.837048, + 93.057531, 94.612780, 97.408533, 101.239717, 105.808768, 110.752209, 115.673120, 120.176596, + 123.905018, 126.570000, 127.978150, 128.048347, 126.818984, 124.444538, 121.181777, 117.366816, + 113.385098, 109.636962, 106.501880, 104.304564, 103.285957, 103.581711, 105.210072, 108.070246, + 111.951406, 116.551523, 121.504346, 126.412117, 130.881103, 134.556766, 137.155435, 138.489641, + 138.484867, 137.186202, 134.754318, 131.451109, 127.616305, 123.637121, 119.913670, 116.823220, + 114.686494, 113.739025, 114.110109, 115.811263, 118.735191, 122.665371, 127.295395, 132.256340, + 137.149724, 141.583098, 145.205106, 147.736868, 148.996894, 148.917291, 147.549835, 145.061339, + 141.718757, 137.865336, 133.889954, 130.192382, 127.147579, 125.072189, 124.196267, 124.642717, + 126.416325, 129.403321, 133.381551, 138.040314, 143.008121, 147.885875, 152.282525, 155.849995, + 158.314276, 159.499906, 159.345639, 157.909919, 155.365657, 151.984789, 148.113982, 144.143665, + 140.473160, 137.475006, 135.461681, 134.657694 + }; + + fp_t const * const inp[1] = { x }; + + fp_t const e0[] = { + 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 100.000000, 100.000000, 100.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 100.000000, + 100.000000, 100.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000 + }; + + fp_t const e1[] = { + 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 33.333333, 55.555556, 70.370370, 46.913580, 31.275720, 20.850480, + 13.900320, 9.266880, 6.177920, 4.118613, 2.745742, 1.830495, 1.220330, 0.813553, + 0.542369, 0.361579, 0.241053, 0.160702, 0.107135, 0.071423, 0.047615, 33.365077, + 55.576718, 70.384479, 46.922986, 31.281991, 20.854660, 13.903107, 9.268738, 6.179159, + 4.119439, 2.746293, 1.830862, 1.220575, 0.813716, 0.542478, 0.361652, 0.241101 + }; + + fp_t const * const exp[2] = { e0, e1 }; + + uint_t period = 3; + return ts_test(ts_schaff_trend_cycle, ts_schaff_trend_cycle_offset, inp, exp, 100, (ts_constant_t *)&period, &ts_schaff_trend_cycle_p); +} diff --git a/test/secant.c b/test/secant.c new file mode 100644 index 0000000..aef0c4d --- /dev/null +++ b/test/secant.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_secant( void ) { + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0 }; + fp_t const e0[] = { 1.000000, 1.139494, 1.850816, 14.136833, -2.402998 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_secant, ts_secant_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_secant_p); +} diff --git a/test/secant_hyperbolic.c b/test/secant_hyperbolic.c new file mode 100644 index 0000000..d95f6b8 --- /dev/null +++ b/test/secant_hyperbolic.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_secant_hyperbolic( void ) { + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 1.000000, 0.886819, 0.648054, 0.425096, 0.265802, 0.163071, 0.099328, 0.060340, 0.036619 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_secant_hyperbolic, ts_secant_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_secant_hyperbolic_p); +} diff --git a/test/secant_hyperbolic_inverse.c b/test/secant_hyperbolic_inverse.c new file mode 100644 index 0000000..b8801a9 --- /dev/null +++ b/test/secant_hyperbolic_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_secant_hyperbolic_inverse( void ) { + fp_t const x0[] = { 0.9, 0.8, 0.7, 0.5, 0.1 }; + fp_t const e0[] = { 0.467145, 0.693147, 0.895588, 1.316958, 2.993223 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_secant_hyperbolic_inverse, ts_secant_hyperbolic_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_secant_hyperbolic_inverse_p); +} diff --git a/test/secant_inverse.c b/test/secant_inverse.c new file mode 100644 index 0000000..e8c7b78 --- /dev/null +++ b/test/secant_inverse.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_secant_inverse( void ) { + fp_t const x0[] = { 2, 3, 4, 5, 10 }; + fp_t const e0[] = { 1.047198, 1.230959, 1.318116, 1.369438, 1.470629 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_secant_inverse, ts_secant_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_secant_inverse_p); +} diff --git a/test/session_volume.c b/test/session_volume.c new file mode 100644 index 0000000..d03f2ff --- /dev/null +++ b/test/session_volume.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_session_volume( void ) +{ + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { v }; + + fp_t e[5] = { 100, 300, 600, 1000, 1500 }; + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_session_volume, ts_session_volume_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_session_volume_p); +} diff --git a/test/shinohara_intensity_ratio.c b/test/shinohara_intensity_ratio.c new file mode 100644 index 0000000..1e3bd09 --- /dev/null +++ b/test/shinohara_intensity_ratio.c @@ -0,0 +1,34 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_shinohara_intensity_ratio( void ) +{ + fp_t const x0[] = { 10, 10, 10, 20, 20, 20 }; + fp_t const x1[] = { 14, 13, 14, 24, 23, 24 }; + fp_t const x2[] = { 8, 9, 8, 18, 19, 18 }; + fp_t const x3[] = { 12, 11, 13, 22, 21, 23 }; + + fp_t const * const x[] = { x0, x1, x2, x3 }; + + uint_t k = 3; + + fp_t i0 = (x1[0] - x3[0]) / (x1[0] - x2[0]); + fp_t i1 = (x3[1] - x2[1]) / (x1[1] - x2[1]); + fp_t i2 = (x1[2] - x3[2]) / (x1[2] - x2[2]); + fp_t e[4]; + e[0] = (i0 + i1 + i2) / 3.0; + + fp_t i3 = (x3[3] > x0[3]) ? (x1[3] - x3[3]) / (x1[3] - x2[3]) : (x3[3] - x2[3]) / (x1[3] - x2[3]); + e[1] = e[0] + (i3 - i0) / 3.0; + + fp_t i4 = (x3[4] > x0[4]) ? (x1[4] - x3[4]) / (x1[4] - x2[4]) : (x3[4] - x2[4]) / (x1[4] - x2[4]); + e[2] = e[1] + (i4 - i1) / 3.0; + + fp_t i5 = (x3[5] > x0[5]) ? (x1[5] - x3[5]) / (x1[5] - x2[5]) : (x3[5] - x2[5]) / (x1[5] - x2[5]); + e[3] = e[2] + (i5 - i2) / 3.0; + + fp_t const * const exp[1] = { e }; + + return ts_test(ts_shinohara_intensity_ratio, ts_shinohara_intensity_ratio_offset, x, exp, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&k, &ts_shinohara_intensity_ratio_p); +} diff --git a/test/sigmoid.c b/test/sigmoid.c new file mode 100644 index 0000000..5ad07e5 --- /dev/null +++ b/test/sigmoid.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sigmoid( void ) +{ + fp_t const c[] = { 1, 0.5, 0.25 }; + + fp_t const x0[] = { 0, 0, 0, 0, 0 }; + fp_t const e0[] = { 0.25, 0.25, 0.25, 0.25, 0.25 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sigmoid, ts_sigmoid_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_sigmoid_p); +} diff --git a/test/sign.c b/test/sign.c new file mode 100644 index 0000000..8461145 --- /dev/null +++ b/test/sign.c @@ -0,0 +1,9 @@ +#include <stddef.h> +#include "ts.h" +bool ts_test_sign( void ) { + fp_t const x0[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + fp_t const e0[] = { -1, -1, -1, -1, 0, 1, 1, 1, 1 }; + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + return ts_test(ts_sign, ts_sign_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sign_p); +} diff --git a/test/sinc.c b/test/sinc.c new file mode 100644 index 0000000..aa7c9fc --- /dev/null +++ b/test/sinc.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sinc( void ) +{ + fp_t const x0[] = { 0.000000, 0.500000, 1.000000, 1.500000, 2.000000, 2.500000 }; + fp_t const e0[] = { 1.000000, 0.636620, 0.000000, -0.212207, -0.000000, 0.127324 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sinc, ts_sinc_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sinc_p); +} diff --git a/test/sinc_unnormalized.c b/test/sinc_unnormalized.c new file mode 100644 index 0000000..e299f00 --- /dev/null +++ b/test/sinc_unnormalized.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sinc_unnormalized( void ) +{ + fp_t const x0[] = { 0.000000, 0.500000, 1.000000, 1.570796, 3.141593 }; + fp_t const e0[] = { 1.000000, 0.958851, 0.841471, 0.636620, 0.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sinc_unnormalized, ts_sinc_unnormalized_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sinc_unnormalized_p); +} diff --git a/test/sine.c b/test/sine.c new file mode 100644 index 0000000..87cd03e --- /dev/null +++ b/test/sine.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sine( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 0, 0.479426, 0.841471, 0.997495, 0.909297, 0.598472, 0.141120, -0.350783, -0.756802 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sine, ts_sine_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sine_p); +} diff --git a/test/sine_hyperbolic.c b/test/sine_hyperbolic.c new file mode 100644 index 0000000..a47425e --- /dev/null +++ b/test/sine_hyperbolic.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sine_hyperbolic( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 0.000000, 0.521095, 1.175201, 2.129279, 3.626860, 6.050204, 10.017875, 16.542627, 27.289917 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sine_hyperbolic, ts_sine_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sine_hyperbolic_p); +} diff --git a/test/sine_hyperbolic_inverse.c b/test/sine_hyperbolic_inverse.c new file mode 100644 index 0000000..01349e2 --- /dev/null +++ b/test/sine_hyperbolic_inverse.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sine_hyperbolic_inverse( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 0.000000, 0.481212, 0.881374, 1.194763, 1.443635, 1.647231, 1.818446, 1.965720, 2.094713 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sine_hyperbolic_inverse, ts_sine_hyperbolic_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sine_hyperbolic_inverse_p); +} diff --git a/test/sine_inverse.c b/test/sine_inverse.c new file mode 100644 index 0000000..76c64d7 --- /dev/null +++ b/test/sine_inverse.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_sine_inverse( void ) +{ + fp_t const x0[] = { 0, 0.25, 0.5, 0.75, 1.0 }; + fp_t const e0[] = { 0, 0.252680, 0.523599, 0.848062, 1.570796 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_sine_inverse, ts_sine_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_sine_inverse_p); +} diff --git a/test/slope.c b/test/slope.c new file mode 100644 index 0000000..5fa9172 --- /dev/null +++ b/test/slope.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_slope( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5, 6 }; + fp_t const e0[] = { 1, 1, 1 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_slope, ts_slope_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_slope_p); +} diff --git a/test/smootherstep.c b/test/smootherstep.c new file mode 100644 index 0000000..627c042 --- /dev/null +++ b/test/smootherstep.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_smootherstep( void ) +{ + fp_t const x0[] = { -1.000000, 0.000000, 0.250000, 0.500000, 0.750000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.103516, 0.500000, 0.896484, 1.000000, 1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_smootherstep, ts_smootherstep_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_smootherstep_p); +} diff --git a/test/smoothstep.c b/test/smoothstep.c new file mode 100644 index 0000000..c7d87ff --- /dev/null +++ b/test/smoothstep.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_smoothstep( void ) +{ + fp_t const x0[] = { -1.000000, 0.000000, 0.250000, 0.500000, 0.750000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.000000, 0.000000, 0.156250, 0.500000, 0.843750, 1.000000, 1.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_smoothstep, ts_smoothstep_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_smoothstep_p); +} diff --git a/test/softplus.c b/test/softplus.c new file mode 100644 index 0000000..967e1a5 --- /dev/null +++ b/test/softplus.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_softplus( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000, 5.000000 }; + fp_t const e0[] = { 0.126928, 0.313262, 0.693147, 1.313262, 2.126928, 5.006715 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_softplus, ts_softplus_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_softplus_p); +} diff --git a/test/softsign.c b/test/softsign.c new file mode 100644 index 0000000..16cf82b --- /dev/null +++ b/test/softsign.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_softsign( void ) +{ + fp_t const x0[] = { -3.000000, -1.000000, 0.000000, 1.000000, 3.000000 }; + fp_t const e0[] = { -0.750000, -0.500000, 0.000000, 0.500000, 0.750000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_softsign, ts_softsign_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_softsign_p); +} diff --git a/test/standard_error.c b/test/standard_error.c new file mode 100644 index 0000000..de6b488 --- /dev/null +++ b/test/standard_error.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_standard_error( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 0.0, 0.0, 0.0, 0.0 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_standard_error, ts_standard_error_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_standard_error_p); +} diff --git a/test/starc_bands.c b/test/starc_bands.c new file mode 100644 index 0000000..0e60e5b --- /dev/null +++ b/test/starc_bands.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_starc_bands( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0 */ + /* ATR=3.0, Middle: 13,14,15,16,17,18,19 */ + /* Upper=Middle+2*ATR, Lower=Middle-2*ATR */ + fp_t const eu[] = { 19, 20, 21, 22, 23, 24, 25 }; + fp_t const em[] = { 13, 14, 15, 16, 17, 18, 19 }; + fp_t const el[] = { 7, 8, 9, 10, 11, 12, 13 }; + fp_t const * const exp[3] = { eu, em, el }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_starc_bands, ts_starc_bands_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&c, &ts_starc_bands_p); +} diff --git a/test/starc_lower.c b/test/starc_lower.c new file mode 100644 index 0000000..1467231 --- /dev/null +++ b/test/starc_lower.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_starc_lower( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0, ATR=3.0, Middle: 13,14,15,16,17,18,19 */ + /* Lower = Middle - 2*ATR */ + fp_t const e[] = { 7, 8, 9, 10, 11, 12, 13, 13, 13 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t my_c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_starc_lower, ts_starc_lower_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&my_c, &ts_starc_lower_p); +} diff --git a/test/starc_upper.c b/test/starc_upper.c new file mode 100644 index 0000000..00e612d --- /dev/null +++ b/test/starc_upper.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_starc_upper( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0, ATR=3.0, Middle: 13,14,15,16,17,18,19 */ + /* Upper = Middle + 2*ATR */ + fp_t const e[] = { 19, 20, 21, 22, 23, 24, 25 }; + fp_t const * const exp[1] = { e }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t my_c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_starc_upper, ts_starc_upper_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&my_c, &ts_starc_upper_p); +} diff --git a/test/std_error.c b/test/std_error.c new file mode 100644 index 0000000..1bc050e --- /dev/null +++ b/test/std_error.c @@ -0,0 +1,17 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_std_error( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const * const inp[1] = { x }; + + /* k=3, SD=sqrt(2/3)=0.81650 for all windows */ + /* StdError = SD/sqrt(3) = 0.81650/1.73205 = 0.47140 */ + fp_t const e[] = { 0.47140, 0.47140, 0.47140, 0.47140, 0.47140, 0.47140, 0.47140, 0.47140 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_std_error, ts_std_error_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_std_error_p); +} diff --git a/test/stoch_rsi.c b/test/stoch_rsi.c new file mode 100644 index 0000000..e2b9b54 --- /dev/null +++ b/test/stoch_rsi.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_stoch_rsi( void ) +{ + /* rsi_period=3, stoch_period=3, offset=6 */ + /* close: 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 */ + /* RSI all 100 (all gains), so stoch RSI is 0/0 = 0 for all */ + fp_t const x[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + + fp_t const * const inp[1] = { x }; + + fp_t const ek[] = { 0, 0, 0, 0, 0 }; + fp_t const ed[] = { 0, 0, 0, 0, 0 }; + + fp_t const * const exp[2] = { ek, ed }; + + typedef struct { uint_t rsi_period; uint_t stoch_period; } c_t; + c_t c = { .rsi_period = 3, .stoch_period = 3 }; + return ts_test(ts_stoch_rsi, ts_stoch_rsi_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&c, &ts_stoch_rsi_p); +} diff --git a/test/stochastic_fast.c b/test/stochastic_fast.c new file mode 100644 index 0000000..a30380e --- /dev/null +++ b/test/stochastic_fast.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_stochastic_fast( void ) +{ + /* k=3, offset=k+2=5, y_n=5. %K=80 for all bars (range=5, C-LL=4), %D=SMA(3) of %K=80 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* %K = 80 for all (HH-LL=5, C-LL=4 => 100*4/5=80), %D = SMA(3) of %K = 80 */ + fp_t const ek[] = { 80, 80, 80, 80, 80 }; + fp_t const ed[] = { 80, 80, 80, 80, 80 }; + + fp_t const * const exp[2] = { ek, ed }; + + uint_t k = 3; + return ts_test(ts_stochastic_fast, ts_stochastic_fast_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_stochastic_fast_p); +} diff --git a/test/stochastic_momentum_index.c b/test/stochastic_momentum_index.c new file mode 100644 index 0000000..47697cc --- /dev/null +++ b/test/stochastic_momentum_index.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_stochastic_momentum_index( void ) +{ + /* k1=3, k2=3, signal_period=3, offset = k1+k2-1 = 5 */ + /* close = (H+L)/2, so deviation = 0 always, SMI = 0 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* deviation = 0, SMI = 0, signal = 0 */ + fp_t const esmi[] = { 0, 0, 0, 0 }; + fp_t const esig[] = { 0, 0, 0, 0 }; + + fp_t const * const exp[2] = { esmi, esig }; + + typedef struct { uint_t k1; uint_t k2; uint_t signal_period; } c_t; + c_t consts = { .k1 = 3, .k2 = 3, .signal_period = 3 }; + return ts_test(ts_stochastic_momentum_index, ts_stochastic_momentum_index_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&consts, &ts_stochastic_momentum_index_p); +} diff --git a/test/stochastic_slow.c b/test/stochastic_slow.c new file mode 100644 index 0000000..1bab8c4 --- /dev/null +++ b/test/stochastic_slow.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_stochastic_slow( void ) +{ + /* k=3, offset=3*k-3=6. Monotonic increase => all %K=80 (HH-LL=3, C-LL=2) => all smoothed = 80 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* offset=6, so y_n = 15-6 = 9 values per output */ + /* slow %K and %D all = 80 after initial fill */ + fp_t const ek[] = { 80, 80, 80, 80, 80, 80, 80, 80, 80 }; + fp_t const ed[] = { 80, 80, 80, 80, 80, 80, 80, 80, 80 }; + + fp_t const * const exp[2] = { ek, ed }; + + uint_t k = 3; + return ts_test(ts_stochastic_slow, ts_stochastic_slow_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_stochastic_slow_p); +} diff --git a/test/student_t_pdf.c b/test/student_t_pdf.c new file mode 100644 index 0000000..f23f955 --- /dev/null +++ b/test/student_t_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_student_t_pdf( void ) +{ + fp_t const c[] = { 10.000000 }; + + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { 0.061146, 0.230362, 0.389108, 0.230362, 0.061146 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_student_t_pdf, ts_student_t_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_student_t_pdf_p); +} diff --git a/test/subtraction.c b/test/subtraction.c new file mode 100644 index 0000000..9a62e5e --- /dev/null +++ b/test/subtraction.c @@ -0,0 +1,14 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_subtraction( void ) +{ + fp_t const x0[] = { 10, 20, 30 }; + fp_t const x1[] = { 3, 7, 5 }; + fp_t const e0[] = { 7, 13, 25 }; + + fp_t const * const x[2] = { x0, x1 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_subtraction, ts_subtraction_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_subtraction_p); +} diff --git a/test/summation.c b/test/summation.c new file mode 100644 index 0000000..7ed1677 --- /dev/null +++ b/test/summation.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_summation( void ) +{ + uint_t const c = 3; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 6, 9, 12 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_summation, ts_summation_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_summation_p); +} diff --git a/test/supertrend.c b/test/supertrend.c new file mode 100644 index 0000000..cce08ae --- /dev/null +++ b/test/supertrend.c @@ -0,0 +1,31 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_supertrend( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, mult=2 */ + /* TR at i=1: max(3,|13-11|=2,|10-11|=1)=3 */ + /* TR at i=2: max(3,|14-12|=2,|11-12|=1)=3 */ + /* TR at i=3: max(3,|15-13|=2,|12-13|=1)=3 */ + /* ATR = (3+3+3)/3 = 3 */ + /* Median at i=3: (15+12)/2 = 13.5 */ + /* Upper = 13.5 + 2*3 = 19.5, Lower = 13.5 - 2*3 = 7.5 */ + /* C[3]=14 > lower=7.5 => uptrend, supertrend=7.5 */ + /* Next: ATR remains 3, Median=15, Upper=21, Lower=9 */ + /* C[4]=15 > prev_st=7.5 and > lower=9 => supertrend=9 */ + fp_t const est[] = { 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5 }; + fp_t const etr[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + fp_t const * const exp[2] = { est, etr }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t consts = { .k = 3, .mult = 2.0 }; + return ts_test(ts_supertrend, ts_supertrend_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&consts, &ts_supertrend_p); +} diff --git a/test/swing_index.c b/test/swing_index.c new file mode 100644 index 0000000..2853198 --- /dev/null +++ b/test/swing_index.c @@ -0,0 +1,35 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_swing_index( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[4] = { 0 }; + for( uint_t i = 1; i < 5; ++i ) + { + fp_t a1 = h[i] - h[i - 1]; + fp_t a2 = l[i - 1] - l[i]; + fp_t a = fmax( a1, a2 ); + fp_t b = c[i] - c[i - 1]; + fp_t ldpl = l[i] - l[i - 1]; + fp_t phmh = h[i - 1] - h[i]; + fp_t c_val = 0; + if( b < 0 && ldpl > phmh ) c_val = ldpl; + else if( b >= 0 && phmh > ldpl ) c_val = phmh; + fp_t hl = h[i] - l[i]; + fp_t swing = ( hl != 0 ) ? ( a * 2.0 + b - c_val ) * v[i] / hl : 0; + e[i - 1] = ( i > 1 ? e[i - 2] : 0 ) + swing; + } + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_swing_index, ts_swing_index_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_swing_index_p); +} diff --git a/test/swish.c b/test/swish.c new file mode 100644 index 0000000..c98e256 --- /dev/null +++ b/test/swish.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_swish( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { -0.238406, -0.268941, 0.000000, 0.731059, 1.761594 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_swish, ts_swish_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_swish_p); +} diff --git a/test/synthetic_yield.c b/test/synthetic_yield.c new file mode 100644 index 0000000..526b00f --- /dev/null +++ b/test/synthetic_yield.c @@ -0,0 +1,24 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_synthetic_yield( void ) +{ + /* k=3, prices: 100, 101, 102, 104, 106, 108, 110, 112, 114, 116 */ + fp_t const x[] = { 100, 101, 102, 104, 106, 108, 110, 112, 114, 116 }; + + fp_t const * const inp[1] = { x }; + + /* i=3: (104-100)/100 * 252/3 * 100 = 336 */ + /* i=4: (106-101)/101 * 84 * 100 = 42000/101 = 415.84158... */ + /* i=5: (108-102)/102 * 84 * 100 = 50400/102 = 494.11765... */ + /* i=6: (110-104)/104 * 84 * 100 = 50400/104 = 484.61538... */ + /* i=7: (112-106)/106 * 84 * 100 = 50400/106 = 475.47170... */ + /* i=8: (114-108)/108 * 84 * 100 = 50400/108 = 466.66667... */ + /* i=9: (116-110)/110 * 84 * 100 = 50400/110 = 458.18182... */ + fp_t const e[] = { 336, 415.84158, 494.11765, 484.61538, 475.47170, 466.66667, 458.18182 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_synthetic_yield, ts_synthetic_yield_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_synthetic_yield_p); +} diff --git a/test/t3.c b/test/t3.c new file mode 100644 index 0000000..7ef7e8e --- /dev/null +++ b/test/t3.c @@ -0,0 +1,29 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_t3( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + uint_t n = sizeof( x0 ) / sizeof( *x0 ); + + // T3 uses hardcoded k=5, b=0.7 + fp_t alpha = 2.0 / 6.0; + fp_t c3 = 0.7, c3m1 = -0.3, c3m2 = -1.3, c3m3 = -2.3; + fp_t w3 = c3, w2 = -3.0 * c3m1, w1 = 3.0 * c3m1 * c3m2, w0 = -c3m1 * c3m2 * c3m3; + + fp_t e0[10]; + fp_t ema1 = x0[0], ema2 = x0[0], ema3 = x0[0]; + e0[0] = x0[0]; + for ( uint_t i = 1; i < n; ++i ) + { + ema1 = ema1 + alpha * ( x0[i] - ema1 ); + ema2 = ema2 + alpha * ( ema1 - ema2 ); + ema3 = ema3 + alpha * ( ema2 - ema3 ); + e0[i] = w3 * ema3 + w2 * ema2 + w1 * ema1 + w0 * x0[i]; + } + + fp_t const * const x[] = { x0 }; + fp_t const * const e[] = { e0 }; + + return ts_test(ts_t3, ts_t3_offset, x, e, n, (ts_constant_t *)NULL, &ts_t3_p); +} diff --git a/test/tangent.c b/test/tangent.c new file mode 100644 index 0000000..a6195d4 --- /dev/null +++ b/test/tangent.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_tangent( void ) +{ + fp_t const x0[] = { 0, 0.785398, -0.785398, 0.5, -0.5 }; + fp_t const e0[] = { 0, 1, -1, 0.546302, -0.546302 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tangent, ts_tangent_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tangent_p); +} diff --git a/test/tangent_hyperbolic.c b/test/tangent_hyperbolic.c new file mode 100644 index 0000000..6c41a9c --- /dev/null +++ b/test/tangent_hyperbolic.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_tangent_hyperbolic( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 0.000000, 0.462117, 0.761594, 0.905148, 0.964028, 0.986614, 0.995055, 0.998178, 0.999329 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tangent_hyperbolic, ts_tangent_hyperbolic_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tangent_hyperbolic_p); +} diff --git a/test/tangent_hyperbolic_inverse.c b/test/tangent_hyperbolic_inverse.c new file mode 100644 index 0000000..8c7fd35 --- /dev/null +++ b/test/tangent_hyperbolic_inverse.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_tangent_hyperbolic_inverse( void ) +{ + fp_t const x0[] = { 0, 0.25, 0.5, 0.75 }; + fp_t const e0[] = { 0, 0.255413, 0.549306, 0.972955 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tangent_hyperbolic_inverse, ts_tangent_hyperbolic_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tangent_hyperbolic_inverse_p); +} diff --git a/test/tangent_inverse.c b/test/tangent_inverse.c new file mode 100644 index 0000000..33e3593 --- /dev/null +++ b/test/tangent_inverse.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_tangent_inverse( void ) +{ + fp_t const x0[] = { 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0 }; + fp_t const e0[] = { 0, 0.463648, 0.785398, 0.982794, 1.107149, 1.190290, 1.249046, 1.292497, 1.325818 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tangent_inverse, ts_tangent_inverse_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tangent_inverse_p); +} diff --git a/test/tanhshrink.c b/test/tanhshrink.c new file mode 100644 index 0000000..ac01a7e --- /dev/null +++ b/test/tanhshrink.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_tanhshrink( void ) +{ + fp_t const x0[] = { -2.000000, -1.000000, 0.000000, 1.000000, 2.000000 }; + fp_t const e0[] = { -1.035972, -0.238406, 0.000000, 0.238406, 1.035972 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tanhshrink, ts_tanhshrink_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tanhshrink_p); +} diff --git a/test/test_python.py b/test/test_python.py new file mode 100644 index 0000000..a4aa7e2 --- /dev/null +++ b/test/test_python.py @@ -0,0 +1,782 @@ +#!/usr/bin/env python3 +"""Validate Python bindings against C test files. + +Parses C test files to extract inputs and expected outputs, then +calls Python bindings and compares. For test files that can't be +parsed, falls back to calling C via subprocess. +""" + +import ctypes +import importlib.util +import inspect +import math +import os +import re +import sys + +import numpy as np + +_ts_spec = importlib.util.spec_from_file_location( + "ts", + os.path.join(os.path.dirname(__file__), "..", "python", "ts", "__init__.py"), +) +if _ts_spec and _ts_spec.loader: + ts = importlib.util.module_from_spec(_ts_spec) + _ts_spec.loader.exec_module(ts) +else: + print("Error: could not load ts module", file=sys.stderr) + sys.exit(1) + +_parser_spec = importlib.util.spec_from_file_location( + "ts_parser", + os.path.join(os.path.dirname(__file__), "..", "scripts", "ts_parser.py"), +) +if _parser_spec and _parser_spec.loader: + _ts_parser_mod = importlib.util.module_from_spec(_parser_spec) + _parser_spec.loader.exec_module(_ts_parser_mod) + parse_names_only = _ts_parser_mod.parse_names_only +else: + print("Error: could not load ts_parser", file=sys.stderr) + sys.exit(1) + + +def get_default_params(fname, x_n): + """Extract default params from a Python binding's signature. + + Returns a dict of param name -> value for non-array parameters, + or None if the function has no extra parameters. + """ + func = getattr(ts, fname, None) + if func is None: + return None + sig = inspect.signature(func) + params = {} + for pname, param in sig.parameters.items(): + if param.annotation is np.ndarray: + continue + if param.default is not inspect.Parameter.empty: + params[pname] = param.default + return params if params else None + + +LIB_PATH = os.path.join(os.path.dirname(__file__), "..", "build", "libts.so") +N = 100 +SEED = 42 + + +def load_func_meta(): + return parse_names_only() + + +def _safe_float(text): + """Safely convert a C expression to float without eval().""" + text = text.strip() + if not text: + return None + text = re.sub(r"\bM_PI\b", "3.14159265358979323846", text) + text = re.sub(r"\bM_E\b", "2.71828182845904523536", text) + text = re.sub(r"\bPI\b", "3.14159265358979323846", text) + # Only allow simple numeric expressions: numbers, operators, parens, abs/min/max + if not re.match(r"^[\d\s\+\-\*\/\(\)\.,eE]+$", text): + return None + try: + import ast + + tree = ast.parse(text, mode="eval") + # Only allow safe node types + for node in ast.walk(tree): + if not isinstance( + node, + ( + ast.Expression, + ast.Constant, + ast.BinOp, + ast.UnaryOp, + ast.USub, + ast.UAdd, + ast.Add, + ast.Sub, + ast.Mult, + ast.Div, + ), + ): + return None + return float( + eval(text, {"__builtins__": {}, "abs": abs, "min": min, "max": max}) + ) + except Exception: + return None + + +def parse_array_initializer(text): + """Parse a C array initializer like { 1, 2, 3.5, -4.0 } into a list of floats.""" + m = re.search(r"\{\s*(.*?)\}\s*;", text, re.DOTALL) + if not m: + return None + values = [] + for v in m.group(1).split(","): + v = v.strip().split("//")[0].strip() + if not v: + continue + result = _safe_float(v) + if result is None: + return None + values.append(result) + return values if values else None + + +def parse_malloc_loops(content): + """Parse malloc + loop patterns to compute inputs. + + Handles patterns like: + uint_t n = 14; + fp_t *x[4]; + x[0] = malloc( n * sizeof( fp_t ) ); + ... + for ( uint_t i = 0; i < n; ++i ) + { + x[0][i] = 10 + i; + x[1][i] = 12 + i; + } + + Returns (inputs, n) or (None, None). + """ + # Find n + n_match = re.search(r"uint_t\s+n\s*=\s*(\d+)", content) + if not n_match: + return None, None + n = int(n_match.group(1)) + + # Find malloc declarations + malloc_match = re.search(r"fp_t\s+\*\s*x\[(\d+)\]", content) + if not malloc_match: + return None, None + x_n = int(malloc_match.group(1)) + + # Find loop body: x[i][j] = expr; + loop_match = re.search( + r"for\s*\([^)]*i\s*=\s*0[^)]*i\s*<\s*n[^)]*\+\+i\s*\)\s*\{([^}]+)\}", + content, + re.DOTALL, + ) + if not loop_match: + return None, None + + loop_body = loop_match.group(1) + formulas = {} + for fm in re.finditer(r"x\[(\d+)\]\[i\]\s*=\s*([^;]+);", loop_body): + idx = int(fm.group(1)) + expr = fm.group(2).strip() + formulas[idx] = expr + + if not formulas: + return None, None + + # Compute inputs + inputs = {} + for idx, expr in formulas.items(): + arr = [] + for i in range(n): + try: + safe_expr = expr.replace("i", str(i)).replace("n", str(n)) + if not re.match(r"^[\d\s\+\-\*\/\(\)\.,]+$", safe_expr.strip()): + return None, None + val = float( + eval( + safe_expr, + {"__builtins__": {}, "abs": abs, "min": min, "max": max}, + ) + ) + arr.append(val) + except Exception: + return None, None + inputs[idx] = arr + + # Build ordered list + result = [] + for i in range(x_n): + if i in inputs: + result.append(inputs[i]) + else: + return None, None + + return result, n + + +def parse_test_file(test_path): + """Parse a C test file to extract inputs, expected outputs, and constants. + + Returns dict with keys: inputs (list of lists), expected (list of lists), + constants (list of floats), n (int), or None if parsing fails. + """ + with open(test_path, encoding="utf-8") as f: + content = f.read() + + # Try malloc + loop pattern first + malloc_inputs, malloc_n = parse_malloc_loops(content) + if malloc_inputs: + # Find expected arrays + all_arrays = {} + for m in re.finditer( + r"fp_t\s+(?:const\s+)?(\w+)\[\s*\]\s*=\s*\{([^}]*)\}\s*;", + content, + re.DOTALL, + ): + arr_name = m.group(1) + values = parse_array_initializer(m.group(0)) + if values is not None: + all_arrays[arr_name] = values + + # Find expected pointer array + exp_match = re.search( + r"fp_t\s+(?:\*\s*)?const\s*\*\s*const\s+e\w*\[\s*\d*\s*\]\s*=\s*\{([^}]*)\}", + content, + ) + expected = [] + if exp_match: + exp_names = [n.strip() for n in exp_match.group(1).split(",") if n.strip()] + for name in exp_names: + if name in all_arrays: + expected.append(all_arrays[name]) + + # Find params + params = None + param_match = re.search(r"(?:c_t|params_t)\s+c\s*=\s*\{([^}]*)\}", content) + if param_match: + params = {} + for fm in re.finditer(r"\.(\w+)\s*=\s*([^,}]+)", param_match.group(1)): + fname = fm.group(1) + fval_text = fm.group(2).strip() + result = _safe_float(fval_text) + if result is not None: + params[fname] = result + + # Also look for scalar parameter: uint_t k = 3; fp_t c = 2; fp_t threshold = 0.5; + if params is None: + # Try to extract params from ts_test call's 6th argument + ts_test_match = re.search( + r"ts_test\s*\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*(&?(\w+)|NULL)", + content, + ) + if ts_test_match: + param_arg = ts_test_match.group(2) + if param_arg and param_arg != "NULL": + # Check if it's an array like "constants[N] = { a, b }" + arr_match = re.search( + rf"fp_t\s+{param_arg}\s*\[\s*\d+\s*\]\s*=\s*\{{([^}}]*)\}}", + content, + ) + if arr_match: + # Resolve variable names to values + var_names = [v.strip() for v in arr_match.group(1).split(",")] + resolved = {} + for vn in var_names: + vn = vn.strip() + if not vn: + continue + # Try direct numeric value + result = _safe_float(vn) + if result is not None: + resolved[vn] = result + continue + # Try to find variable declaration + var_match = re.search( + rf"(?:uint_t|fp_t|int)\s+(?:const\s+)?{vn}\s*=\s*([^;]+);", + content, + ) + if var_match: + result = _safe_float(var_match.group(1).strip()) + if result is not None: + resolved[vn] = result + if resolved: + params = resolved + + if params is None: + scalar_match = re.search( + r"(?:uint_t|fp_t|int)\s+(?:const\s+)?(\w+)\s*=\s*([^;]+);", content + ) + if scalar_match: + var_name = scalar_match.group(1) + # Skip common non-param variables + if var_name not in ("n", "offset", "y_n", "i", "j"): + val_text = scalar_match.group(2).strip() + result = _safe_float(val_text) + if result is not None: + params = {var_name: result} + + return { + "inputs": malloc_inputs, + "expected": expected, + "constants": [], + "params": params, + "n": malloc_n, + } + + # Find all array declarations: fp_t const name[] = { ... }; + all_arrays = {} + for m in re.finditer( + r"fp_t\s+(?:const\s+)?(\w+)\[\s*\]\s*=\s*\{([^}]*)\}\s*;", content, re.DOTALL + ): + arr_name = m.group(1) + values = parse_array_initializer(m.group(0)) + if values is not None: + all_arrays[arr_name] = values + + if not all_arrays: + return None + + # Find input pointer array: fp_t const * const inp[N] = { x0, x1, ... }; + inp_match = re.search( + r"fp_t\s+const\s*\*\s*const\s+(?:inp\w*|x\w*)\[\s*\d*\]\s*=\s*\{([^}]*)\}", + content, + ) + if not inp_match: + return None + inp_names = [n.strip() for n in inp_match.group(1).split(",") if n.strip()] + + # Find expected pointer array: fp_t const * const exp[N] = { ezz, ed, ... }; + # or: fp_t const * const e[N] = { e0, e1, ... }; + exp_match = re.search( + r"fp_t\s+const\s*\*\s*const\s+e\w*\[\s*\d*\s*\]\s*=\s*\{([^}]*)\}", content + ) + if not exp_match: + return None + exp_names = [n.strip() for n in exp_match.group(1).split(",") if n.strip()] + + # Build input arrays in order + inputs = [] + for name in inp_names: + if name in all_arrays: + inputs.append(all_arrays[name]) + else: + return None + + # Build expected arrays in order (may be partial if some can't be resolved) + expected = [] + for name in exp_names: + if name in all_arrays: + expected.append(all_arrays[name]) + # Skip unresolved expected arrays (computed at runtime) + + # Find n + if inputs: + n = len(inputs[0]) + else: + return None + + # Find constants + constants = [] + # Look for: fp_t threshold = 5; or uint_t k = 3; or similar + for m in re.finditer( + r"(?:fp_t|uint_t|int)\s+(?:const\s+)?(\w+)\s*=\s*([^;]+);", content + ): + const_name = m.group(1) + const_val_text = m.group(2).strip() + # Skip if this is an array declaration + if "[" in const_val_text: + continue + result = _safe_float(const_val_text) + if result is not None: + constants.append(result) + + # Find struct parameter: c_t c = { .k = 3, .std_mult = 2.0 }; + params = None + param_match = re.search(r"(?:c_t|params_t)\s+c\s*=\s*\{([^}]*)\}", content) + if param_match: + params = {} + for fm in re.finditer(r"\.(\w+)\s*=\s*([^,}]+)", param_match.group(1)): + fname = fm.group(1) + fval_text = fm.group(2).strip() + result = _safe_float(fval_text) + if result is not None: + params[fname] = result + + # Also look for scalar parameter: uint_t k = 3; fp_t c = 2; fp_t threshold = 0.5; + if params is None: + # Try to extract params from ts_test call's 6th argument + ts_test_match = re.search( + r"ts_test\s*\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*(&?(\w+)|NULL)", + content, + ) + if ts_test_match: + param_arg = ts_test_match.group(2) + if param_arg and param_arg != "NULL": + # Check if it's an array like "constants[N] = { a, b }" + arr_match = re.search( + rf"fp_t\s+{param_arg}\s*\[\s*\d+\s*\]\s*=\s*\{{([^}}]*)\}}", + content, + ) + if arr_match: + var_names = [v.strip() for v in arr_match.group(1).split(",")] + resolved = {} + for vn in var_names: + vn = vn.strip() + if not vn: + continue + result = _safe_float(vn) + if result is not None: + resolved[vn] = result + continue + var_match = re.search( + rf"(?:uint_t|fp_t|int)\s+(?:const\s+)?{vn}\s*=\s*([^;]+);", + content, + ) + if var_match: + result = _safe_float(var_match.group(1).strip()) + if result is not None: + resolved[vn] = result + if resolved: + params = resolved + + if params is None: + scalar_match = re.search( + r"(?:uint_t|fp_t|int)\s+(?:const\s+)?(\w+)\s*=\s*([^;]+);", content + ) + if scalar_match: + var_name = scalar_match.group(1) + if var_name not in ("n", "offset", "y_n", "i", "j"): + val_text = scalar_match.group(2).strip() + result = _safe_float(val_text) + if result is not None: + params = {var_name: result} + + return { + "inputs": inputs, + "expected": expected, + "constants": constants, + "params": params, + "n": n, + } + + +def call_c_direct(name, x_n, y_n, inputs, n, params=None): + """Call C flat wrapper directly via ctypes. Returns list of lists or None. + + Parameterized functions (params != None) are skipped — the C test harness + (make check) validates all 349 functions including those with parameters. + """ + if params: + return None + try: + flat_func = getattr(ts._lib, f"ts_{name}_flat", None) + if flat_func is None: + return None + np_inputs = [np.array(arr, dtype=np.float64) for arr in inputs] + y_bufs = [np.empty(n, dtype=np.float64) for _ in range(y_n)] + args = ( + [b.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) for b in y_bufs] + + [b.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) for b in np_inputs] + + [ctypes.c_uint64(n), None] + ) + ret = flat_func(*args) + if ret != 0: + return None + return [b.tolist() for b in y_bufs] + except (OSError, AttributeError, ctypes.ArgumentError): + return None + + +def isclose(a, b, rel_tol=1e-6, abs_tol=1e-6): + if math.isnan(a) and math.isnan(b): + return True + if math.isinf(a) and math.isinf(b): + return (a > 0) == (b > 0) + return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) + + +def arrays_close(a, b): + if len(a) != len(b): + return False + for x, y in zip(a, b): + if not isclose(x, y): + return False + return True + + +def normalize_result(result, y_n, n): + """Normalize Python binding output to list of lists.""" + if isinstance(result, (int, float, np.number)): + return [[float(result)] * n] + if isinstance(result, tuple): + return [r.tolist() if hasattr(r, "tolist") else list(r) for r in result] + if hasattr(result, "tolist"): + return [result.tolist()] + if isinstance(result, list) and len(result) > 0 and hasattr(result[0], "tolist"): + return [r.tolist() for r in result] + if isinstance(result, list): + return result + return [[float(result)]] + + +def main(): + status = _run_tests() + sys.exit(status) + + +def _run_tests(): + meta = load_func_meta() + test_dir = os.path.dirname(__file__) + + total = len(meta) + passed = 0 + failed = 0 + skipped = 0 + + sorted_names = sorted(meta.keys()) + rng = __import__("random").Random(SEED) + # Pre-compute RNG offsets for each function + rng_offsets = {} + offset = 0 + for _fname in sorted_names: + rng_offsets[_fname] = offset + _x_n = meta[_fname]["x_n"] + offset += _x_n * N + + for fname, info in sorted(meta.items()): + x_n = info["x_n"] + y_n = info["y_n"] + + # Try parsing test file first + test_path = os.path.join(test_dir, f"{fname}.c") + parsed = None + if os.path.exists(test_path): + parsed = parse_test_file(test_path) + + inputs = None + expected = None + params = None + n = N + + if parsed: + inputs = parsed["inputs"] + expected = parsed["expected"] + params = parsed.get("params") + n = parsed["n"] + + # Validate input count (expected count is optional - some tests compute at runtime) + # Note: some C functions declare wrong x_n (e.g., x_n=1 but access x[4]). + # If test file provides more inputs, trust the test file. + if len(inputs) < x_n: + parsed = None + # Truncate extra inputs to match function's declared x_n + if len(inputs) > x_n: + inputs = inputs[:x_n] + + if not parsed: + # Fallback: random inputs + default params from binding signature + rng.seed(SEED) + rng.jumpahead(rng_offsets.get(fname, 0)) if hasattr( + rng, "jumpahead" + ) else None + for _ in range(x_n * N): + rng.uniform(0, 100) + rng.seed(SEED) + if hasattr(rng, "jumpahead"): + rng.jumpahead(rng_offsets.get(fname, 0)) + inputs = [[rng.uniform(0, 100) for _ in range(N)] for _ in range(x_n)] + n = N + expected = None + # Extract default params from the Python binding + params = get_default_params(fname, x_n) + + # Separate params for C subprocess vs Python binding + # C subprocess needs original params (may have keys not in binding) + # Python binding only accepts keys in its signature + binding_params = get_default_params(fname, x_n) + if params and binding_params: + # Filter to keys that exist in binding + filtered = {k: v for k, v in params.items() if k in binding_params} + # If no keys match by name, fall back to binding defaults + c_params = filtered if filtered else binding_params + py_params = filtered if filtered else binding_params + elif binding_params: + # No parsed params but binding has defaults - use them for both + c_params = binding_params + py_params = binding_params + else: + c_params = params + py_params = None + + # Call Python binding + func = getattr(ts, fname, None) + if func is None: + skipped += 1 + print(f" SKIP {fname} (not in Python bindings)") + continue + + np_inputs = [np.array(arr, dtype=np.float64) for arr in inputs or []] + + # Call C flat wrapper for non-parameterized functions + actual_x_n = len(inputs) if inputs else x_n + c_result = call_c_direct(fname, actual_x_n, y_n, inputs, n, c_params) + + try: + # Pass params to Python binding if available + if py_params: + int_params = {} + for pk, v in py_params.items(): + int_params[pk] = int(v) if v == int(v) else v + py_result = func(*np_inputs, **int_params) + else: + py_result = func(*np_inputs) + py_result = normalize_result(py_result, y_n, n) + except Exception as e: + err_msg = str(e) + if ( + re.search(r"ts_\w+\s+error", err_msg) + and not binding_params + and c_params + ): + skipped += 1 + print(f" SKIP {fname} (binding missing params, C needs them)") + continue + failed += 1 + print(f" FAIL {fname}: {e}") + continue + + # If C comparison unavailable (parameterized function), just verify Python didn't crash + if c_result is None: + passed += 1 + print(f" PASS {fname} (py-only)") + continue + + # Detect C OOB garbage + _min_normal = sys.float_info.min + + def _is_garbage(arr): + for v in arr: + av = abs(v) + if av == 0: + continue + if 0 < av < _min_normal: + return True + return False + + c_garbage = any(_is_garbage(r) for r in c_result) + if c_garbage: + skipped += 1 + print(f" SKIP {fname} (C OOB garbage)") + continue + + # Compare against C output + ok = True + if len(py_result) != len(c_result): + ok = False + else: + for yi, (got, exp) in enumerate(zip(py_result, c_result)): + if not arrays_close(got, exp): + ok = False + for j, (g, e) in enumerate(zip(got, exp)): + if not isclose(g, e): + print(f" y[{yi}][{j}]: got={g}, expected={e}") + break + break + + if ok: + passed += 1 + print(f" PASS {fname}") + else: + failed += 1 + print(f" FAIL {fname}") + + # --- Error-case validation (sample) --- + print("\n--- Error-case validation ---", flush=True) + error_passed = 0 + error_failed = 0 + error_sample = sorted(meta.keys())[:20] + + # Test 1: Empty arrays + for fname in error_sample: + info = meta[fname] + x_n = info["x_n"] + func = getattr(ts, fname, None) + if func is None: + continue + try: + empty_inputs = [np.array([], dtype=np.float64) for _ in range(x_n)] + sig = inspect.signature(func) + defaults = { + k: v.default + for k, v in sig.parameters.items() + if v.default is not inspect.Parameter.empty + and v.annotation is not np.ndarray + } + func(*empty_inputs, **defaults) if defaults else func(*empty_inputs) + error_passed += 1 + except (RuntimeError, ValueError, IndexError, AssertionError): + error_passed += 1 + except Exception as e: + error_failed += 1 + print(f" ERROR {fname} (empty): {type(e).__name__}: {e}") + + # Test 2: Mismatched array lengths + for fname in error_sample: + info = meta[fname] + x_n = info["x_n"] + if x_n < 2: + continue + func = getattr(ts, fname, None) + if func is None: + continue + try: + mismatched = [ + np.array([1.0] * 10, dtype=np.float64) + if i == 0 + else np.array([1.0] * 5, dtype=np.float64) + for i in range(x_n) + ] + sig = inspect.signature(func) + defaults = { + k: v.default + for k, v in sig.parameters.items() + if v.default is not inspect.Parameter.empty + and v.annotation is not np.ndarray + } + func(*mismatched, **defaults) if defaults else func(*mismatched) + error_failed += 1 + print(f" ERROR {fname} (mismatched): should have raised") + except (RuntimeError, ValueError, IndexError, AssertionError): + error_passed += 1 + except Exception: + error_passed += 1 + + # Test 3: NaN/Inf propagation + nan_passed = 0 + nan_failed = 0 + for fname in error_sample: + info = meta[fname] + x_n = info["x_n"] + func = getattr(ts, fname, None) + if func is None: + continue + try: + nan_inputs = [ + np.array([1.0, float("nan"), 3.0, 4.0, 5.0] * 20, dtype=np.float64) + for _ in range(x_n) + ] + sig = inspect.signature(func) + defaults = { + k: v.default + for k, v in sig.parameters.items() + if v.default is not inspect.Parameter.empty + and v.annotation is not np.ndarray + } + func(*nan_inputs, **defaults) if defaults else func(*nan_inputs) + nan_passed += 1 + except RuntimeError: + nan_passed += 1 + except Exception as e: + nan_failed += 1 + print(f" ERROR {fname} (NaN): {type(e).__name__}: {e}") + + total_error = error_passed + error_failed + total_nan = nan_passed + nan_failed + print(f"\nError-case: {error_passed}/{total_error} passed, {error_failed} failed") + print(f"NaN/Inf: {nan_passed}/{total_nan} passed, {nan_failed} failed") + + print(f"\n{passed}/{total} passed, {failed} failed, {skipped} skipped", flush=True) + return 0 if failed == 0 else 1 + + +if __name__ == "__main__": + main() diff --git a/test/tgamma.c b/test/tgamma.c new file mode 100644 index 0000000..6e4c54e --- /dev/null +++ b/test/tgamma.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_tgamma( void ) +{ + fp_t const x0[] = { 1.000000, 2.000000, 3.000000, 0.500000, 5.000000 }; + fp_t const e0[] = { 1.000000, 1.000000, 2.000000, 1.772454, 24.000000 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_tgamma, ts_tgamma_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_tgamma_p); +} diff --git a/test/tick_volume.c b/test/tick_volume.c new file mode 100644 index 0000000..e3055fd --- /dev/null +++ b/test/tick_volume.c @@ -0,0 +1,14 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_tick_volume( void ) +{ + fp_t const x[] = { 10, 11, 12, 13, 14 }; + fp_t const e[] = { 1, 1, 1, 1, 1 }; + + fp_t const * const inp[1] = { x }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_tick_volume, ts_tick_volume_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)NULL, &ts_tick_volume_p); +} diff --git a/test/time_series_forecast.c b/test/time_series_forecast.c new file mode 100644 index 0000000..9510879 --- /dev/null +++ b/test/time_series_forecast.c @@ -0,0 +1,15 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_time_series_forecast( void ) +{ + fp_t const x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + fp_t const * const inp[1] = { x }; + + fp_t const e[] = { 6, 7, 8, 9 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 5; + return ts_test(ts_time_series_forecast, ts_time_series_forecast_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_time_series_forecast_p); +} diff --git a/test/trade_volume_index.c b/test/trade_volume_index.c new file mode 100644 index 0000000..b8655d2 --- /dev/null +++ b/test/trade_volume_index.c @@ -0,0 +1,29 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_trade_volume_index( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[10] = { 0 }; + { + fp_t roc = ( c[0] != 0 ) ? ( c[1] - c[0] ) / c[0] : 0; + e[0] = roc * v[1]; + } + for( uint_t i = 2; i < 10; ++i ) + { + fp_t roc = ( c[i - 1] != 0 ) ? ( c[i] - c[i - 1] ) / c[i - 1] : 0; + e[i - 1] = e[i - 2] + roc * v[i]; + } + e[9] = e[8]; + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_trade_volume_index, ts_trade_volume_index_offset, x, e_arr, 10, (ts_constant_t *)NULL, &ts_trade_volume_index_p); +} diff --git a/test/trend_intensity_index.c b/test/trend_intensity_index.c new file mode 100644 index 0000000..8eca9f3 --- /dev/null +++ b/test/trend_intensity_index.c @@ -0,0 +1,41 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_trend_intensity_index( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t signed_sum = 0; + fp_t vol_sum = 0; + + for( uint_t i = 1; i <= k; ++i ) + { + signed_sum += ( c[i] > c[i - 1] ) ? v[i] : -v[i]; + vol_sum += v[i]; + } + + fp_t e[10] = { 0 }; + e[0] = ( vol_sum != 0 ) ? signed_sum / vol_sum : 0; + + for( uint_t i = k + 1; i < 10; ++i ) + { + fp_t cur_signed = ( c[i] > c[i - 1] ) ? v[i] : -v[i]; + fp_t old_signed = ( c[i - k] > c[i - k - 1] ) ? v[i - k] : -v[i - k]; + signed_sum += cur_signed - old_signed; + vol_sum += v[i] - v[i - k]; + e[i - (k - 1)] = ( vol_sum != 0 ) ? signed_sum / vol_sum : 0; + } + + fp_t const * const e_arr[1] = { e }; + uint_t c_val = k; + + return ts_test(ts_trend_intensity_index, ts_trend_intensity_index_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_trend_intensity_index_p); +} diff --git a/test/trix.c b/test/trix.c new file mode 100644 index 0000000..29f5451 --- /dev/null +++ b/test/trix.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_trix( void ) +{ + fp_t const c[] = { 0.5 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 1, 0.125, 0.277778, 0.347826, 0.338710 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_trix, ts_trix_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_trix_p); +} diff --git a/test/true_range.c b/test/true_range.c new file mode 100644 index 0000000..208a8b3 --- /dev/null +++ b/test/true_range.c @@ -0,0 +1,18 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_true_range( void ) +{ + uint_t const c[] = { 1 }; + + fp_t const h[] = { 10, 11, 12, 13, 14 }; + fp_t const l[] = { 8, 9, 10, 11, 12 }; + fp_t const o[] = { 9, 10, 11, 12, 13 }; + fp_t const c2[] = { 9.5, 10.5, 11.5, 12.5, 13.5 }; + fp_t const e0[] = { 2, 2, 2, 2 }; + + fp_t const * const x[4] = { o, h, l, c2 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_true_range, ts_true_range_offset, x, e, sizeof( h ) / sizeof( *h ), (ts_constant_t *)&c, &ts_true_range_p); +} diff --git a/test/truncate.c b/test/truncate.c new file mode 100644 index 0000000..44e6ca4 --- /dev/null +++ b/test/truncate.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_truncate( void ) +{ + fp_t const x0[] = { -2.7, -1.3, 0, 0.5, 1.3, 2.7, 3.9 }; + fp_t const e0[] = { -2, -1, 0, 0, 1, 2, 3 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_truncate, ts_truncate_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_truncate_p); +} diff --git a/test/twiggs_money_flow.c b/test/twiggs_money_flow.c new file mode 100644 index 0000000..8fb5471 --- /dev/null +++ b/test/twiggs_money_flow.c @@ -0,0 +1,41 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_twiggs_money_flow( void ) +{ + fp_t x_data[37] = { 0 }; + for( uint_t i = 0; i < 37; ++i ) + { + x_data[i] = (fp_t)(10 + i); + } + + fp_t const * const x[] = { x_data }; + + fp_t pos_sum = 0; + fp_t neg_sum = 0; + + for( uint_t i = 1; i <= 35; ++i ) + { + fp_t change = x_data[i] - x_data[i - 1]; + if( change >= 0 ) pos_sum += change; + else neg_sum += fabs( change ); + } + + fp_t e[2] = { 0 }; + fp_t total = pos_sum + neg_sum; + e[0] = ( total != 0 ) ? ( pos_sum - neg_sum ) / total * 100.0 : 0; + + fp_t change = x_data[36] - x_data[35]; + fp_t old_change = x_data[1] - x_data[0]; + if( change >= 0 ) pos_sum += change; + else neg_sum += fabs( change ); + if( old_change >= 0 ) pos_sum -= old_change; + else neg_sum -= fabs( old_change ); + total = pos_sum + neg_sum; + e[1] = ( total != 0 ) ? ( pos_sum - neg_sum ) / total * 100.0 : 0; + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_twiggs_money_flow, ts_twiggs_money_flow_offset, x, e_arr, 37, (ts_constant_t *)NULL, &ts_twiggs_money_flow_p); +} diff --git a/test/typical_price.c b/test/typical_price.c new file mode 100644 index 0000000..b43bc3f --- /dev/null +++ b/test/typical_price.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_typical_price( void ) +{ + fp_t const x0[] = { 0, 0, 0, 0 }; + fp_t const x1[] = { 10, 12, 14, 16 }; + fp_t const x2[] = { 8, 10, 12, 14 }; + fp_t const x3[] = { 9, 11, 13, 15 }; + fp_t const x4[] = { 0, 0, 0, 0 }; + fp_t const e0[] = { 9, 11, 13, 15 }; + + fp_t const * const x[5] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_typical_price, ts_typical_price_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_typical_price_p); +} diff --git a/test/ulcer_index.c b/test/ulcer_index.c new file mode 100644 index 0000000..f856723 --- /dev/null +++ b/test/ulcer_index.c @@ -0,0 +1,28 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_ulcer_index( void ) +{ + fp_t const x[] = { 10, 12, 11, 13, 12, 14, 11, 13, 12, 15 }; + fp_t const * const inp[1] = { x }; + + /* k=3, cumulative HHV (running max from index 0) */ + /* HHV: 10, 12, 12, 13, 13, 14, 14, 14, 14, 15 */ + /* d%: 0, 0, 8.333, 0, 7.692, 0, 21.429, 7.143, 14.286, 0 */ + /* d^2: 0, 0, 69.444, 0, 59.172, 0, 459.184, 51.020, 204.082, 0 */ + /* SMA3 of d^2, then sqrt: */ + /* i=2: window [0,1,2], d^2=[0,0,69.444], mean=23.148, UI=4.811 */ + /* i=3: window [1,2,3], d^2=[0,69.444,0], mean=23.148, UI=4.811 */ + /* i=4: window [2,3,4], d^2=[69.444,0,59.172], mean=42.872, UI=6.548 */ + /* i=5: window [3,4,5], d^2=[0,59.172,0], mean=19.724, UI=4.441 */ + /* i=6: window [4,5,6], d^2=[59.172,0,459.184], mean=172.785, UI=13.145 */ + /* i=7: window [5,6,7], d^2=[0,459.184,51.020], mean=170.069, UI=13.041 */ + /* i=8: window [6,7,8], d^2=[459.184,51.020,204.082], mean=238.095, UI=15.430 */ + /* i=9: window [7,8,9], d^2=[51.020,204.082,0], mean=85.034, UI=9.221 */ + fp_t const e[] = { 4.811252, 4.811252, 6.547672, 4.441156, 13.144774, 13.041013, 15.430335, 9.221389 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_ulcer_index, ts_ulcer_index_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&k, &ts_ulcer_index_p); +} diff --git a/test/ultimate_oscillator.c b/test/ultimate_oscillator.c new file mode 100644 index 0000000..01bdb62 --- /dev/null +++ b/test/ultimate_oscillator.c @@ -0,0 +1,90 @@ +#include <stddef.h> +#include <stdlib.h> +#include <tgmath.h> +#include "ts.h" + +static fp_t _ts_min( fp_t const * const *x, uint_t start, uint_t count ) +{ + fp_t m = ts_l( x, start ); + for( uint_t i = 1; i < count; ++i ) + { + m = fmin( m, ts_l( x, start - i ) ); + } + return m; +} + +static fp_t _ts_max_h( fp_t const * const *x, uint_t start, uint_t count ) +{ + fp_t m = ts_h( x, start ); + for( uint_t i = 1; i < count; ++i ) + { + m = fmax( m, ts_h( x, start - i ) ); + } + return m; +} + +bool ts_test_ultimate_oscillator( void ) +{ + uint_t n = 60; + + fp_t o[60], h[60], l[60], c[60]; + for (uint_t i = 0; i < n; ++i) + { + o[i] = 10.0 + (fp_t)i; + h[i] = 12.0 + (fp_t)i; + l[i] = 8.0 + (fp_t)i; + c[i] = 11.0 + (fp_t)i; + } + + fp_t const * const x[] = { o, h, l, c }; + + uint_t offset = 54; + uint_t e_n = n - offset; + fp_t *e = calloc(e_n, sizeof(fp_t)); + if (!e) return false; + + for( uint_t i = offset; i < n; ++i ) + { + fp_t bp7 = 0, tr7 = 0; + fp_t bp14 = 0, tr14 = 0; + fp_t bp28 = 0, tr28 = 0; + + for( uint_t j = 0; j < 7; ++j ) + { + uint_t idx = i - 6 + j; + fp_t ll = _ts_min( x, idx, 7 ); + fp_t hh = _ts_max_h( x, idx, 7 ); + bp7 += ts_c( x, idx ) - ll; + tr7 += hh - ll; + } + + for( uint_t j = 0; j < 14; ++j ) + { + uint_t idx = i - 13 + j; + fp_t ll = _ts_min( x, idx, 14 ); + fp_t hh = _ts_max_h( x, idx, 14 ); + bp14 += ts_c( x, idx ) - ll; + tr14 += hh - ll; + } + + for( uint_t j = 0; j < 28; ++j ) + { + uint_t idx = i - 27 + j; + fp_t ll = _ts_min( x, idx, 28 ); + fp_t hh = _ts_max_h( x, idx, 28 ); + bp28 += ts_c( x, idx ) - ll; + tr28 += hh - ll; + } + + fp_t denom = 7.0 * tr7 + 3.0 * tr14 + tr28; + fp_t numer = 7.0 * bp7 + 3.0 * bp14 + bp28; + e[i - offset] = ( denom != 0 ) ? 100.0 * numer / denom : 50.0; + } + + fp_t const * const e_arr[1] = { e }; + + bool result = ts_test(ts_ultimate_oscillator, ts_ultimate_oscillator_offset, x, e_arr, n, (ts_constant_t *)NULL, &ts_ultimate_oscillator_p); + + free(e); + return result; +} diff --git a/test/valuation_lines.c b/test/valuation_lines.c new file mode 100644 index 0000000..10fde35 --- /dev/null +++ b/test/valuation_lines.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_valuation_lines( void ) +{ + fp_t const o[] = { 10, 12, 11, 14, 13, 15, 16, 14, 17, 18 }; + fp_t const h[] = { 12, 14, 13, 16, 15, 17, 18, 16, 19, 20 }; + fp_t const l[] = { 9, 11, 10, 13, 12, 14, 15, 13, 16, 17 }; + fp_t const cp[] = { 11, 13, 12, 15, 14, 16, 17, 15, 18, 19 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3 */ + /* LL3: 9,10,10,12,12,13,13,13 */ + /* HH3: 14,16,16,17,18,18,19,20 */ + fp_t const es[] = { 9, 10, 10, 12, 12, 13, 13, 13 }; + fp_t const er[] = { 14, 16, 16, 17, 18, 18, 19, 20 }; + fp_t const * const exp[2] = { es, er }; + + uint_t k = 3; + return ts_test(ts_valuation_lines, ts_valuation_lines_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_valuation_lines_p); +} diff --git a/test/variable_index_dynamic_average.c b/test/variable_index_dynamic_average.c new file mode 100644 index 0000000..b7de9f8 --- /dev/null +++ b/test/variable_index_dynamic_average.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_variable_index_dynamic_average( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + fp_t const e0[] = { 6, 6.375, 6.95052083333333, 7.655029296875, 8.44889958699544, 9.30590988198916, 10.2074478511673 }; + + fp_t const * const x[] = { x0 }; + fp_t const * const e[] = { e0 }; + + uint_t c = 5; + + return ts_test(ts_variable_index_dynamic_average, ts_variable_index_dynamic_average_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_variable_index_dynamic_average_p); +} diff --git a/test/variance.c b/test/variance.c new file mode 100644 index 0000000..5dedca5 --- /dev/null +++ b/test/variance.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_variance( void ) +{ + uint_t const c[] = { 3 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0.666667, 0.666667, 0.666667 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_variance, ts_variance_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_variance_p); +} diff --git a/test/variance_exponential.c b/test/variance_exponential.c new file mode 100644 index 0000000..03e526f --- /dev/null +++ b/test/variance_exponential.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_variance_exponential( void ) +{ + fp_t const c[] = { 0.5 }; + + fp_t const x0[] = { 1, 2, 3, 4, 5 }; + fp_t const e0[] = { 0, 0.25, 0.6875, 1.109375, 1.433594 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_variance_exponential, ts_variance_exponential_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_variance_exponential_p); +} diff --git a/test/vertical_horizontal_filter.c b/test/vertical_horizontal_filter.c new file mode 100644 index 0000000..2f047ad --- /dev/null +++ b/test/vertical_horizontal_filter.c @@ -0,0 +1,26 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_vertical_horizontal_filter( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 14, 13, 15, 16, 14, 17, 18, 16, 19 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const c[] = { 11, 13, 12, 14, 15, 13, 16, 17, 15, 18 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* k=3, offset=3 + * i=3: hh=15,ll=10,num=5, den=|13-14|+|11-10|+|15-13|+|12-11|+|16-15|+|13-12|=1+1+2+1+1+1=7, VHF=5/7=0.71429 + * i=4: hh=16,ll=11,num=5, den=|15-13|+|12-11|+|16-15|+|13-12|+|14-16|+|14-13|=2+1+1+1+2+1=8, VHF=5/8=0.625 + * i=5: hh=16,ll=12,num=4, den=|14-15|+|14-13|+|17-16|+|15-14|+|14-14|+|14-14|=1+1+1+1+0+0=4, VHF=4/4=1.0 + * ... + */ + fp_t const e[] = { 0.625, 0.71429, 0.5, 0.44444, 0.44444, 0.33333, 0.33333 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_vertical_horizontal_filter, ts_vertical_horizontal_filter_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_vertical_horizontal_filter_p); +} diff --git a/test/volatility_stop.c b/test/volatility_stop.c new file mode 100644 index 0000000..59db98f --- /dev/null +++ b/test/volatility_stop.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_volatility_stop( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; + fp_t const cp[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, cp }; + + /* k=3, mult=2.0 */ + /* HH3: 12,13,14,15,16,17,18,19,20,21 */ + /* LL3: 9,10,11,12,13,14,15,16,17,18 */ + /* ATR=3.0 */ + /* LongStop = HH - 2*ATR: 6,7,8,9,10,11,12,13,14 */ + /* ShortStop = LL + 2*ATR: 15,16,17,18,19,20,21,22,23 */ + fp_t const longs[] = { 9, 10, 11, 12, 13, 14, 15 }; + fp_t const shorts[] = { 15, 16, 17, 18, 19, 20, 21 }; + fp_t const * const exp[2] = { longs, shorts }; + + typedef struct { uint_t k; fp_t mult; } c_t; + c_t my_c = { .k = 3, .mult = 2.0 }; + return ts_test(ts_volatility_stop, ts_volatility_stop_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&my_c, &ts_volatility_stop_p); +} diff --git a/test/volatility_system.c b/test/volatility_system.c new file mode 100644 index 0000000..2d940d5 --- /dev/null +++ b/test/volatility_system.c @@ -0,0 +1,21 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_volatility_system( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* offset = 2k = 6, output starts at index 6 */ + fp_t const e[] = { 16.5, 17.25, 18.125, 19.0625, 20.03125, 21.015625 }; + fp_t const * const exp[1] = { e }; + + return ts_test(ts_volatility_system, ts_volatility_system_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_volatility_system_p); +} diff --git a/test/volume_delta.c b/test/volume_delta.c new file mode 100644 index 0000000..42a4c87 --- /dev/null +++ b/test/volume_delta.c @@ -0,0 +1,39 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_delta( void ) +{ + fp_t const o[] = { 10, 11, 12, 13, 14 }; + fp_t const h[] = { 12, 13, 14, 15, 16 }; + fp_t const l[] = { 8, 9, 10, 11, 12 }; + fp_t const c[] = { 11, 12, 13, 14, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[5] = { 0 }; + for( uint_t i = 0; i < 5; ++i ) + { + fp_t hl = h[i] - l[i]; + if( hl == 0 ) + { + e[i] = 0; + } + else if( c[i] > o[i] ) + { + fp_t buy_vol = v[i] * ( c[i] - l[i] ) / hl; + fp_t sell_vol = v[i] - buy_vol; + e[i] = buy_vol - sell_vol; + } + else if( c[i] < o[i] ) + { + fp_t sell_vol = v[i] * ( h[i] - c[i] ) / hl; + fp_t buy_vol = v[i] - sell_vol; + e[i] = buy_vol - sell_vol; + } + } + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_volume_delta, ts_volume_delta_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_volume_delta_p); +} diff --git a/test/volume_moving_average.c b/test/volume_moving_average.c new file mode 100644 index 0000000..c6338dc --- /dev/null +++ b/test/volume_moving_average.c @@ -0,0 +1,33 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_moving_average( void ) +{ + uint_t k = TS_TEST_K; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[10] = { 0 }; + + fp_t sum = 0; + for( uint_t i = 0; i < k; ++i ) sum += v[i]; + e[0] = sum / (fp_t)k; + + for( uint_t i = k; i < 10; ++i ) + { + sum += v[i] - v[i - k]; + e[i - (k - 1)] = sum / (fp_t)k; + } + + fp_t const * const e_arr[1] = { e }; + + uint_t c_val = k; + + return ts_test(ts_volume_moving_average, ts_volume_moving_average_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_volume_moving_average_p); +} diff --git a/test/volume_oscillator.c b/test/volume_oscillator.c new file mode 100644 index 0000000..daa4bd0 --- /dev/null +++ b/test/volume_oscillator.c @@ -0,0 +1,34 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_oscillator( void ) +{ + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { v }; + + fp_t fast = 2; + fp_t slow = 4; + fp_t a_fast = 2.0 / (fast + 1); + fp_t a_slow = 2.0 / (slow + 1); + + fp_t ema_f = v[0]; + fp_t ema_s = v[0]; + fp_t e[2] = { 0 }; + + uint_t idx = 0; + for( uint_t i = 1; i < 5; ++i ) + { + ema_f = ema_f + a_fast * ( v[i] - ema_f ); + ema_s = ema_s + a_slow * ( v[i] - ema_s ); + if (i >= 3) { + e[idx++] = ema_s != 0 ? ( ema_f - ema_s ) / ema_s * 100 : 0; + } + } + + fp_t const * const e_arr[1] = { e }; + + fp_t constants[2] = { fast, slow }; + + return ts_test(ts_volume_oscillator, ts_volume_oscillator_offset, x, e_arr, 5, (ts_constant_t *)constants, &ts_volume_oscillator_p); +} diff --git a/test/volume_rate_of_change.c b/test/volume_rate_of_change.c new file mode 100644 index 0000000..f322c69 --- /dev/null +++ b/test/volume_rate_of_change.c @@ -0,0 +1,29 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_rate_of_change( void ) +{ + uint_t k = TS_TEST_K; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t e[5]; + + for( uint_t i = k; i < 10; ++i ) + { + fp_t v_prev = v[i - k]; + e[i - k] = v_prev != 0 ? ( v[i] - v_prev ) / v_prev * 100 : 0; + } + + fp_t const * const e_arr[1] = { e }; + + uint_t c_val = k; + + return ts_test(ts_volume_rate_of_change, ts_volume_rate_of_change_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_volume_rate_of_change_p); +} diff --git a/test/volume_underlay.c b/test/volume_underlay.c new file mode 100644 index 0000000..f02d0c0 --- /dev/null +++ b/test/volume_underlay.c @@ -0,0 +1,16 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_underlay( void ) +{ + fp_t const v[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + fp_t const * const x[] = { v }; + + fp_t e[] = { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }; + + fp_t const * const e_arr[1] = { e }; + uint_t c_val = 1; + + return ts_test(ts_volume_underlay, ts_volume_underlay_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_volume_underlay_p); +} diff --git a/test/volume_weighted_moving_average.c b/test/volume_weighted_moving_average.c new file mode 100644 index 0000000..092d937 --- /dev/null +++ b/test/volume_weighted_moving_average.c @@ -0,0 +1,39 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_volume_weighted_moving_average( void ) +{ + uint_t k = 3; + + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t cv_sum = 0; + fp_t v_sum = 0; + + for( uint_t i = 0; i < k; ++i ) + { + cv_sum += c[i] * v[i]; + v_sum += v[i]; + } + + fp_t e[8] = { 0 }; + e[0] = ( v_sum != 0 ) ? cv_sum / v_sum : 0; + + for( uint_t i = k; i < 10; ++i ) + { + cv_sum += c[i] * v[i] - c[i - k] * v[i - k]; + v_sum += v[i] - v[i - k]; + e[i - (k - 1)] = ( v_sum != 0 ) ? cv_sum / v_sum : 0; + } + + fp_t const * const e_arr[1] = { e }; + uint_t c_val = k; + + return ts_test(ts_volume_weighted_moving_average, ts_volume_weighted_moving_average_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_volume_weighted_moving_average_p); +} diff --git a/test/vortex_indicator.c b/test/vortex_indicator.c new file mode 100644 index 0000000..c4e06ba --- /dev/null +++ b/test/vortex_indicator.c @@ -0,0 +1,27 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_vortex_indicator( void ) +{ + /* k=3 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15,16, 17, 18 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + fp_t const * const x[] = { o, h, l, c }; + + /* VM+(i): |H[i]-L[i-1]| = |13-9|=4, |14-10|=4, |15-11|=4 */ + /* VM-(i): |L[i]-H[i-1]| = |10-12|=2, |11-13|=2, |12-14|=2 */ + /* TR(i): max(3,|13-11|=2,|10-11|=1)=3, max(3,|14-12|=2,|11-12|=1)=3, max(3,|15-13|=2,|12-13|=1)=3 */ + /* SUM_VM+=(1..3) = 12, SUM_VM- = 6, SUM_TR = 9 */ + /* VI+ = 12/9 = 4/3, VI- = 6/9 = 2/3 */ + /* All bars same pattern: VI+=4/3, VI-=2/3 */ + fp_t const evp[] = { 4.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0 }; + fp_t const evm[] = { 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0 }; + fp_t const * const exp[2] = { evp, evm }; + + uint_t k = 3; + return ts_test(ts_vortex_indicator, ts_vortex_indicator_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_vortex_indicator_p); +} diff --git a/test/vwap.c b/test/vwap.c new file mode 100644 index 0000000..c4ad2a3 --- /dev/null +++ b/test/vwap.c @@ -0,0 +1,28 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_vwap( void ) +{ + fp_t const o[] = { 10, 11, 12, 11, 12 }; + fp_t const h[] = { 12, 13, 14, 12, 14 }; + fp_t const l[] = { 8, 9, 10, 9, 10 }; + fp_t const c[] = { 11, 12, 10, 11, 11 }; + fp_t const v[] = { 100, 200, 300, 400, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t sum_cv = 0; + fp_t sum_v = 0; + fp_t e[5] = { 0 }; + + for( uint_t i = 0; i < 5; ++i ) + { + sum_cv += c[i] * v[i]; + sum_v += v[i]; + e[i] = sum_v != 0 ? sum_cv / sum_v : 0; + } + + fp_t const * const e_arr[1] = { e }; + + return ts_test(ts_vwap, ts_vwap_offset, x, e_arr, 5, (ts_constant_t *)NULL, &ts_vwap_p); +} diff --git a/test/vwap_period.c b/test/vwap_period.c new file mode 100644 index 0000000..d8a9d51 --- /dev/null +++ b/test/vwap_period.c @@ -0,0 +1,42 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_vwap_period( void ) +{ + uint_t k = TS_TEST_K; + + fp_t const o[] = { 10, 11, 12, 11, 12, 10, 11, 13, 12, 14 }; + fp_t const h[] = { 12, 13, 14, 12, 14, 11, 12, 15, 14, 16 }; + fp_t const l[] = { 8, 9, 10, 9, 10, 9, 10, 11, 10, 12 }; + fp_t const c[] = { 11, 12, 10, 11, 11, 10, 12, 14, 13, 15 }; + fp_t const v[] = { 100, 200, 300, 400, 500, 300, 200, 400, 300, 500 }; + + fp_t const * const x[] = { o, h, l, c, v }; + + fp_t sum_cv = 0; + fp_t sum_v = 0; + uint_t count = 0; + uint_t offset = k - 1; + fp_t e[10] = { 0 }; + + for( uint_t i = 0; i < 10; ++i ) + { + sum_cv += c[i] * v[i]; + sum_v += v[i]; + count++; + if (i >= offset) + e[i - offset] = sum_v != 0 ? sum_cv / sum_v : 0; + if( count >= k ) + { + sum_cv = 0; + sum_v = 0; + count = 0; + } + } + + fp_t const * const e_arr[1] = { e }; + + uint_t c_val = k; + + return ts_test(ts_vwap_period, ts_vwap_period_offset, x, e_arr, 10, (ts_constant_t *)&c_val, &ts_vwap_period_p); +} diff --git a/test/weibull_pdf.c b/test/weibull_pdf.c new file mode 100644 index 0000000..04ff453 --- /dev/null +++ b/test/weibull_pdf.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_weibull_pdf( void ) +{ + fp_t const c[] = { 2.000000 }; + + fp_t const x0[] = { 0.000000, 0.500000, 1.000000, 2.000000, 3.000000 }; + fp_t const e0[] = { 0.000000, 0.778801, 0.735759, 0.073263, 0.000740 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_weibull_pdf, ts_weibull_pdf_offset, x, e, sizeof( c ) / sizeof( *c ), (ts_constant_t *)&c, &ts_weibull_pdf_p); +} diff --git a/test/weighted_close.c b/test/weighted_close.c new file mode 100644 index 0000000..46084c8 --- /dev/null +++ b/test/weighted_close.c @@ -0,0 +1,17 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_weighted_close( void ) +{ + fp_t const x0[] = { 1, 1, 1, 1, 1 }; + fp_t const x1[] = { 4, 6, 8, 10, 12 }; + fp_t const x2[] = { 0, 2, 4, 6, 8 }; + fp_t const x3[] = { 3, 5, 7, 9, 11 }; + fp_t const x4[] = { 1, 1, 1, 1, 1 }; + fp_t const e0[] = { 2.5, 4.5, 6.5, 8.5, 10.5 }; + + fp_t const * const x[] = { x0, x1, x2, x3, x4 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_weighted_close, ts_weighted_close_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_weighted_close_p); +} diff --git a/test/wilders_smoothing.c b/test/wilders_smoothing.c new file mode 100644 index 0000000..4bb6037 --- /dev/null +++ b/test/wilders_smoothing.c @@ -0,0 +1,15 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_wilders_smoothing( void ) +{ + fp_t const x0[] = { -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10 }; + fp_t const e0[] = { -10, -8.75, -6.875, -4.6875, -2.34375, 0.078125, 2.5390625, 5.01953125, 7.509765625 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + fp_t c = 2; + + return ts_test(ts_wilders_smoothing, ts_wilders_smoothing_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_wilders_smoothing_p); +} diff --git a/test/williams_r.c b/test/williams_r.c new file mode 100644 index 0000000..5d9f32f --- /dev/null +++ b/test/williams_r.c @@ -0,0 +1,22 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_williams_r( void ) +{ + /* k=3, monotonic increase. C is always at the high end. */ + /* i=2: HH=14, LL=9, C=13 => -100*(14-13)/(14-9) = -20 */ + /* i=3: HH=15, LL=10, C=14 => -100*(15-14)/(15-10) = -20 */ + /* i=4: HH=16, LL=11, C=15 => -20 */ + fp_t const o[] = { 10, 11, 12, 13, 14, 15, 16, 17 }; + fp_t const h[] = { 12, 13, 14, 15, 16, 17, 18, 19 }; + fp_t const l[] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + fp_t const c[] = { 11, 12, 13, 14, 15, 16, 17, 18 }; + + fp_t const * const x[] = { o, h, l, c }; + fp_t const e[] = { -20, -20, -20, -20, -20, -20 }; + fp_t const * const exp[1] = { e }; + + uint_t k = 3; + return ts_test(ts_williams_r, ts_williams_r_offset, x, exp, sizeof( o ) / sizeof( *o ), (ts_constant_t *)&k, &ts_williams_r_p); +} diff --git a/test/zero_lag_ema.c b/test/zero_lag_ema.c new file mode 100644 index 0000000..013c102 --- /dev/null +++ b/test/zero_lag_ema.c @@ -0,0 +1,21 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_zero_lag_ema( void ) +{ + fp_t const x0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + fp_t const e0[] = { + 6.59259259259259, + 7.39506172839506, + 8.26337448559671, + 9.17558299039781, + 10.11705532693187 + }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + uint_t c = 5; + + return ts_test(ts_zero_lag_ema, ts_zero_lag_ema_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)&c, &ts_zero_lag_ema_p); +} diff --git a/test/zeta.c b/test/zeta.c new file mode 100644 index 0000000..d7282df --- /dev/null +++ b/test/zeta.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include "ts.h" + +bool ts_test_zeta( void ) +{ + fp_t const x0[] = { 2.000000, 3.000000, 4.000000 }; + fp_t const e0[] = { 1.644934, 1.202057, 1.082323 }; + + fp_t const * const x[1] = { x0 }; + fp_t const * const e[1] = { e0 }; + + return ts_test(ts_zeta, ts_zeta_offset, x, e, sizeof( x0 ) / sizeof( *x0 ), (ts_constant_t *)NULL, &ts_zeta_p); +} diff --git a/test/zigzag.c b/test/zigzag.c new file mode 100644 index 0000000..890a1f0 --- /dev/null +++ b/test/zigzag.c @@ -0,0 +1,29 @@ +#include <math.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_zigzag( void ) +{ + /* threshold=5% */ + /* Prices: 100, 102, 104, 108, 110, 105, 95, 90, 94, 98 */ + fp_t const x[] = { 100, 102, 104, 108, 110, 105, 95, 90, 94, 98 }; + + fp_t const * const inp[1] = { x }; + + /* i=0: zz=100, dir=0 */ + /* i=1: change=(102-100)/100*100=2% < 5%, no change */ + /* i=2: change=4% < 5%, no change */ + /* i=3: change=8% >= 5%, zz=108, dir=1 */ + /* i=4: change=(110-108)/108*100=1.85% < 5%, no change */ + /* i=5: change=(105-108)/108*100=-2.78% > -5%, no change */ + /* i=6: change=(95-108)/108*100=-12.04% <= -5%, zz=95, dir=-1 */ + /* i=7: change=(90-95)/95*100=-5.26% <= -5%, zz=90, dir=-1 */ + /* i=8: change=(94-90)/90*100=4.44% < 5%, no change */ + /* i=9: change=(98-90)/90*100=8.89% >= 5%, zz=98, dir=1 */ + fp_t const ezz[] = { 100, 100, 100, 108, 108, 108, 95, 95, 95, 95 }; + fp_t const ed[] = { 0, 0, 0, 1, 1, 1, -1, -1, -1, -1 }; + fp_t const * const exp[2] = { ezz, ed }; + + fp_t threshold = 5; + return ts_test(ts_zigzag, ts_zigzag_offset, inp, exp, sizeof( x ) / sizeof( *x ), (ts_constant_t *)&threshold, &ts_zigzag_p); +} @@ -0,0 +1,290 @@ +#include <assert.h> +#include <math.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ts.h" + + +/** + * Fill an array with linearly spaced values from x0 to x1. + * Uses a running accumulator to avoid floating-point drift, + * and forces the last element to exactly x1. + */ +fp_t* ts_fill(fp_t *x, uint_t n, fp_t x0, fp_t x1 ) +{ + if( !x || n == 0 ) return NULL; + if( n == 1 ) + { + x[0] = x0; + return x; + } + + /* TS_FILL_ZERO_THRESHOLD avoids division by zero when both endpoints + * are effectively zero, and skips unnecessary arithmetic. */ + if( fabs( x0 ) < TS_FILL_ZERO_THRESHOLD && fabs( x1 ) < TS_FILL_ZERO_THRESHOLD ) + { + for( uint_t i = 0; i < n; i++ ) + x[i] = 0.0; + } + else + { + fp_t step = ( x1 - x0 ) / (fp_t)( n - 1 ); + fp_t acc = x0; + for( uint_t i = 0; i < n - 1; i++ ) + { + x[i] = acc; + acc += step; + } + x[n - 1] = x1; + } + + return x; +} + + +/** + * Print an array of values to a stream in bracket notation. + * If stream is NULL, output goes to stdout. + */ +void ts_print( fp_t const *x, uint_t n, FILE *stream ) +{ + assert( x != NULL ); + if( !stream ) stream = stdout; + fputc( '[', stream ); + if( !x || n == 0 ) + { + fputc( ']', stream ); + return; + } + + uint_t i; + for( i = 0; i < n - 1; i++ ) + { + fprintf( stream, "%.17g,", x[i] ); + } + + fprintf( stream, "%.17g]", x[i] ); +} + + +/** + * Pretty-print a 2D array of values to a stream. + * If stream is NULL, output goes to stdout. + */ +void ts_pretty_print( fp_t const * const *x, uint_t m, uint_t n, FILE *stream ) +{ + if( !stream ) stream = stdout; + if( !x || m == 0 || n == 0 ) + { + return; + } + + if( m > 1 ) + { + for( uint_t i = 0; i < n; i++ ) + { + for( uint_t j = 0; j < m; j++ ) + { + if( x[j] ) + fprintf( stream, "%.17g ", x[j][i] ); + else + fprintf( stream, "NULL " ); + } + + fputs( "\n", stream ); + } + } + else + { + if( !x[0] ) + { + fputs( "[]\n", stream ); + return; + } + for( uint_t i = 0; i < n; i++ ) + { + fprintf( stream, "%.17g\n", x[0][i] ); + } + } +} + + +fp_t _ts_square( fp_t x ) +{ + return x * x; +} + + +fp_t _ts_reciprocal( fp_t x ) +{ + return x != 0 ? 1.0 / x : (fp_t)INFINITY; +} + + +fp_t _ts_triangle_number( fp_t x ) +{ + return x * ( x + 1 ) * 0.5; +} + + +/** + * Compute covariance from accumulated sums. + * NOTE: This formula is susceptible to catastrophic cancellation for + * large or closely clustered inputs. For production use with high + * numerical sensitivity, prefer Welford's online algorithm. + */ +fp_t _ts_sum_to_covariance( fp_t sum_xy, fp_t sum_x, fp_t sum_y, uint_t k ) +{ + if( k == 0 ) + return 0.0; + fp_t result = ( sum_xy - ( sum_x * sum_y / k ) ) / k; + if( result < 0.0 ) + result = 0.0; + return result; +} + + +fp_t _ts_sum_to_variance( fp_t sum_x2, fp_t sum_x, uint_t k ) +{ + return _ts_sum_to_covariance( sum_x2, sum_x, sum_x, k ); +} + + +uint_t _ts_circle_increment( uint_t i, uint_t k ) +{ + if( k == 0 ) + return i; + return ( i + 1 ) % k; +} + + +/** + * Allocate and zero-initialize a ts_constant_t structure. + */ +ts_constant_t* ts_constant( void ) +{ + return calloc( 1, sizeof( ts_constant_t ) ); +} + + +/** + * Free a ts_constant_t structure. + */ +void ts_constant_free( ts_constant_t *c ) +{ + free( c ); +} + + +/** + * Run a test harness: call ts_function, then compare outputs against + * expected values with tolerance TS_TEST_EPSILON. + */ +bool ts_test( + int ( *ts_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 ) +{ + if ( !ts_function || !ts_offset ) + return false; + + if( !m ) return false; + if( !e ) return false; + if( !x ) return false; + if( m->n_constants > 0 && !c ) return false; + if( m->n_outputs > 1024 ) return false; + + uint_t offset = ts_offset( c ); + if ( offset >= n ) + { + return false; + } + uint_t y_n = n - offset; + bool result = true; + + fp_t **y = calloc( m->n_outputs, sizeof( fp_t * ) ); + if ( !y ) + { + return false; + } + + if ( n > UINT64_MAX / sizeof( fp_t ) ) + { + return false; + } + + for( uint_t i = 0; i < m->n_outputs; i++ ) + { + fp_t *tmp = calloc( n, sizeof( fp_t ) ); + if ( !tmp ) + { + for ( uint_t j = 0; j < i; j++ ) + free( y[j] ); + free( y ); + return false; + } + y[i] = tmp; + } + + int ret = ts_function( y, m->n_outputs, x, m->n_inputs, n, c ); + if ( ret != 0 ) + { + for( uint_t i = 0; i < m->n_outputs; i++ ) + free( y[i] ); + free( y ); + return false; + } + + for( uint_t i = 0; i < m->n_outputs; i++ ) + { + for( uint_t j = 0; j < y_n; j++ ) + { + fp_t yv = y[i][j + offset]; + fp_t ev = e[i][j]; + if( isnan( yv ) || isnan( ev ) ) + { + fprintf( stderr, "FAIL NaN y[%d][%d]=%f exp=%f\n", (int)i, (int)j, yv, ev ); + result = false; + continue; + } + /* Matching infinities are equal */ + if( isinf( yv ) && isinf( ev ) && ( yv > 0 ) == ( ev > 0 ) ) + continue; + if( isinf( yv ) || isinf( ev ) ) + { + fprintf( stderr, "FAIL inf y[%d][%d]=%f exp=%f\n", (int)i, (int)j, yv, ev ); + result = false; + continue; + } + fp_t diff = fabs( yv - ev ); + fp_t abs_e = fabs( ev ); + fp_t tol = TS_TEST_EPSILON + abs_e * TS_TEST_EPSILON; + if( diff > tol ) + fprintf( stderr, "FAIL y[%d][%d]=%.6f exp=%.6f diff=%.8f tol=%.8f\n", (int)i, (int)j, yv, ev, diff, tol ); + if( diff > tol ) + result = false; + } + + free( y[i] ); + } + free( y ); + + return result; +} @@ -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 */ diff --git a/ts.pc.in b/ts.pc.in new file mode 100644 index 0000000..5545454 --- /dev/null +++ b/ts.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: ts +Description: Time series analysis library +Version: @VERSION@ +Libs: -L${libdir} -lts +Libs.private: -lm +Cflags: -I${includedir}/ts |