examples: add multi-timeframe demos for Rust and Node
Python's examples/python/multi_timeframe.py had no Rust or Node sibling. Add both — the Rust version uses wickra-data's `Resampler` / `resample_all` (the canonical path; no manual roll-up), the Node version mirrors the Python one's inline aggregation because wickra-data's resampler is currently Rust-only. * examples/rust/src/bin/multi_timeframe.rs — reads the bundled 1m CSV via `CandleReader`, resamples to 5m / 15m / 1h / 4h / 1d via `resample_all`, prints last RSI(14), MACD(12,26,9) histogram and ADX(14) per timeframe. * examples/node/multi_timeframe.js — same outputs from a hand-rolled bucket aggregator; reuses the new examples/data/ default path. * examples/README.md gains the new rows. Run side by side: the Rust and Node summaries are bit-identical at every timeframe (50000 / 10000 / 3334 / 834 / 209 / 35 bars; same RSI, MACD histogram and ADX to two decimals) — confirming both the Rust resampler and the inline Node aggregator produce the same OHLC buckets.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
//! Multi-timeframe indicators with the Wickra Rust stack.
|
||||
//!
|
||||
//! Reads the bundled 1-minute BTCUSDT CSV (or a path passed on the command
|
||||
//! line), resamples it via [`wickra_data::resample::resample_all`] to 5m /
|
||||
//! 15m / 1h / 4h / 1d, and prints the last RSI(14), MACD(12,26,9) histogram
|
||||
//! and ADX(14) at each timeframe. The Rust counterpart of
|
||||
//! `examples/python/multi_timeframe.py`.
|
||||
//!
|
||||
//! Run with:
|
||||
//! ```text
|
||||
//! cargo run --release -p wickra-examples --bin multi_timeframe
|
||||
//! ```
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use wickra::{Adx, Candle, Indicator, MacdIndicator, Rsi};
|
||||
use wickra_data::aggregator::Timeframe;
|
||||
use wickra_data::csv::CandleReader;
|
||||
use wickra_data::resample::resample_all;
|
||||
|
||||
const ONE_MINUTE_MS: i64 = 60_000;
|
||||
|
||||
fn default_csv() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("..")
|
||||
.join("data")
|
||||
.join("btcusdt-1m.csv")
|
||||
}
|
||||
|
||||
fn summarize(label: &str, candles: &[Candle]) {
|
||||
if candles.is_empty() {
|
||||
println!(" {label:<5} (empty)");
|
||||
return;
|
||||
}
|
||||
let mut rsi = Rsi::new(14).expect("RSI(14) constructor cannot fail");
|
||||
let mut macd = MacdIndicator::new(12, 26, 9).expect("MACD(12,26,9) constructor cannot fail");
|
||||
let mut adx = Adx::new(14).expect("ADX(14) constructor cannot fail");
|
||||
|
||||
let mut last_rsi: Option<f64> = None;
|
||||
let mut last_hist: Option<f64> = None;
|
||||
let mut last_adx: Option<f64> = None;
|
||||
for c in candles {
|
||||
if let Some(v) = rsi.update(c.close) {
|
||||
last_rsi = Some(v);
|
||||
}
|
||||
if let Some(m) = macd.update(c.close) {
|
||||
last_hist = Some(m.histogram);
|
||||
}
|
||||
if let Some(a) = adx.update(*c) {
|
||||
last_adx = Some(a.adx);
|
||||
}
|
||||
}
|
||||
let last_close = candles.last().expect("non-empty").close;
|
||||
println!(
|
||||
" {label:<5} bars={:>5} last_close={last_close:>10.2} rsi={} macd_hist={} adx={}",
|
||||
candles.len(),
|
||||
last_rsi.map_or_else(|| " --".to_string(), |v| format!("{v:>6.2}")),
|
||||
last_hist.map_or_else(|| " -- ".to_string(), |v| format!("{v:+6.2}")),
|
||||
last_adx.map_or_else(|| " --".to_string(), |v| format!("{v:>6.2}")),
|
||||
);
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = env::args().nth(1).map_or_else(default_csv, PathBuf::from);
|
||||
|
||||
println!("Multi-timeframe view of {}", path.display());
|
||||
|
||||
let mut reader = CandleReader::open(&path)?;
|
||||
let ones: Vec<Candle> = reader.read_all()?;
|
||||
summarize("1m", &ones);
|
||||
|
||||
for &(label, minutes) in &[
|
||||
("5m", 5_i64),
|
||||
("15m", 15),
|
||||
("1h", 60),
|
||||
("4h", 240),
|
||||
("1d", 1440),
|
||||
] {
|
||||
let tf = Timeframe::millis(minutes * ONE_MINUTE_MS)?;
|
||||
let resampled = resample_all(tf, ones.iter().copied().map(Ok))?;
|
||||
summarize(label, &resampled);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user