// Kvo // // kvo // // Klinger Volume Oscillator // // Volume-weighted price direction oscillator // // Inputs: 5 // Outputs: 2 // Constants: 2 #include #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 const *)c)->k1 > 10 ? ((ts_kvo_params_t const *)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 const *)c)->k1; fp_t f0_raw = ((ts_kvo_params_t const *)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; }