summaryrefslogtreecommitdiffstats
path: root/lib/schaff_trend_cycle.c
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
committerEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
commit07e18043145f3d91c8ea7ec3ad41b3c905585277 (patch)
tree2bdeae46973f4bdfce263679a4008ac768011f28 /lib/schaff_trend_cycle.c
initial commit: ts time series analysis library v0.1.0
- 354 financial and mathematical time-series indicators - Uniform C11 API with consistent function signatures - 349 passing tests with -Werror - Static and shared library builds - pkg-config integration - Python bindings - Fuzzing and sanitizer support - Security hardening flags
Diffstat (limited to 'lib/schaff_trend_cycle.c')
-rw-r--r--lib/schaff_trend_cycle.c151
1 files changed, 151 insertions, 0 deletions
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;
+}
+