12681e4b1b
Stacked on #222 (base `feat/c-abi-hub`), so the diff is just the additions on top of the hub foundation — no merge of #222 required. ## What this adds **Examples — full parity with rust/python/node (`examples/c/`)** - `streaming.c` upgraded to the multi-indicator (SMA/EMA/RSI/MACD + signals) demo - `backtest.c`, `multi_timeframe.c` (manual time-bucket resampling), `parallel_assets.c` (serial vs OpenMP fan-out, one handle per asset) - three educational strategies: `strategy_rsi_mean_reversion.c`, `strategy_macd_adx.c`, `strategy_bollinger_squeeze.c` - two network examples shelling out to `curl`: `fetch_btcusdt.c`, `live_binance.c` (REST poll) - two header-only helpers (`wickra_csv.h`, `wickra_strategy.h`) since the C ABI ships no IO layer - CMake builds all 11; the 9 offline ones run under `ctest` on 3 OS; the network two are built-only **Docs & metadata — surface the C ABI everywhere it was missing** - ARCHITECTURE diagram + crate table, SECURITY + THREAT_MODEL (the C ABI as the sole `unsafe` FFI surface), the three binding package READMEs, issue/PR templates, CHANGELOG, and the GitHub About template (live About + org description updated too) **Cleanup** - removed all references to the private generator tooling from public files (`bindings/c/src/lib.rs` header, `CONTRIBUTING.md`, `sync-about.yml`) Verified locally: `cargo build -p wickra-c --release`, `cmake + ctest` (9/9 pass), and `-Wall -Wextra -Wpedantic` clean on gcc 13.
111 lines
3.9 KiB
C
111 lines
3.9 KiB
C
/* Streaming indicators with the Wickra C ABI.
|
|
*
|
|
* Feeds a synthetic price series through several indicators tick by tick — the
|
|
* same O(1)-per-update model a live trading bot would use — and prints a status
|
|
* line once every indicator has warmed up. The C counterpart of
|
|
* `examples/rust/src/bin/streaming.rs`, `examples/python/streaming.py` and
|
|
* `examples/node/streaming.js`, using the same seeded LCG so a side-by-side run
|
|
* produces visibly comparable streams.
|
|
*
|
|
* Build (after `cargo build -p wickra-c --release`):
|
|
* cc examples/c/streaming.c -I bindings/c/include -L target/release -lwickra -lm -o streaming
|
|
*/
|
|
|
|
#include "wickra.h"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define DEFAULT_TICKS 120
|
|
|
|
/* Deterministic synthetic series matching the sibling examples' seeded LCG. */
|
|
static void make_series(double *prices, size_t n) {
|
|
uint64_t seed = 1234567u;
|
|
for (size_t t = 0; t < n; ++t) {
|
|
seed = (seed * 1103515245u + 12345u) & 0x7FFFFFFFu;
|
|
double rnd = (double)seed / (double)0x7FFFFFFFu;
|
|
double tf = (double)t;
|
|
prices[t] = 100.0 + tf * 0.05 + sin(tf * 0.07) * 8.0 + cos(tf * 0.21) * 3.0 +
|
|
(rnd - 0.5);
|
|
}
|
|
}
|
|
|
|
/* Format an indicator value, rendering warmup (NaN) as a dashed placeholder. */
|
|
static void fmt(char *buf, size_t buflen, double v) {
|
|
if (isfinite(v)) {
|
|
snprintf(buf, buflen, "%7.2f", v);
|
|
} else {
|
|
snprintf(buf, buflen, " -- ");
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
size_t ticks = DEFAULT_TICKS;
|
|
if (argc == 3 && strcmp(argv[1], "--ticks") == 0) {
|
|
long parsed = strtol(argv[2], NULL, 10);
|
|
if (parsed <= 0) {
|
|
fprintf(stderr, "--ticks must be positive\n");
|
|
return 1;
|
|
}
|
|
ticks = (size_t)parsed;
|
|
} else if (argc != 1) {
|
|
fprintf(stderr, "usage: streaming [--ticks N]\n");
|
|
return 1;
|
|
}
|
|
|
|
struct Sma *sma = wickra_sma_new(20);
|
|
struct Ema *ema = wickra_ema_new(20);
|
|
struct Rsi *rsi = wickra_rsi_new(14);
|
|
struct MacdIndicator *macd = wickra_macd_indicator_new(12, 26, 9);
|
|
double *prices = (double *)malloc(ticks * sizeof(*prices));
|
|
if (sma == NULL || ema == NULL || rsi == NULL || macd == NULL || prices == NULL) {
|
|
fprintf(stderr, "allocation failed\n");
|
|
return 1;
|
|
}
|
|
make_series(prices, ticks);
|
|
|
|
printf("Wickra streaming indicator demo (C)\n\n");
|
|
|
|
size_t signals = 0;
|
|
for (size_t t = 0; t < ticks; ++t) {
|
|
double price = prices[t];
|
|
double sv = wickra_sma_update(sma, price);
|
|
double ev = wickra_ema_update(ema, price);
|
|
double rv = wickra_rsi_update(rsi, price);
|
|
WickraMacdOutput m;
|
|
int macd_ready = wickra_macd_indicator_update(macd, price, &m);
|
|
|
|
/* Only act once every indicator has produced a value. */
|
|
if (!isfinite(sv) || !isfinite(ev) || !isfinite(rv) || !macd_ready) {
|
|
continue;
|
|
}
|
|
|
|
int overbought = rv > 70.0 && m.histogram < 0.0;
|
|
int oversold = rv < 30.0 && m.histogram > 0.0;
|
|
const char *tag = overbought ? "SELL?" : (oversold ? "BUY? " : " ");
|
|
if (overbought || oversold) {
|
|
signals++;
|
|
}
|
|
|
|
char b_price[16], b_sma[16], b_ema[16], b_rsi[16], b_hist[16];
|
|
fmt(b_price, sizeof(b_price), price);
|
|
fmt(b_sma, sizeof(b_sma), sv);
|
|
fmt(b_ema, sizeof(b_ema), ev);
|
|
fmt(b_rsi, sizeof(b_rsi), rv);
|
|
fmt(b_hist, sizeof(b_hist), m.histogram);
|
|
printf("t=%3llu price=%s sma=%s ema=%s rsi=%s macd_hist=%s %s\n",
|
|
(unsigned long long)t, b_price, b_sma, b_ema, b_rsi, b_hist, tag);
|
|
}
|
|
|
|
printf("\nDone — %llu candidate signal(s) over %llu ticks.\n",
|
|
(unsigned long long)signals, (unsigned long long)ticks);
|
|
|
|
wickra_sma_free(sma);
|
|
wickra_ema_free(ema);
|
|
wickra_rsi_free(rsi);
|
|
wickra_macd_indicator_free(macd);
|
|
free(prices);
|
|
return 0;
|
|
}
|