use concat_idents::concat_idents;
use std::mem::{transmute, MaybeUninit};
use ta_lib_sys as ta;
mod macros;
use macros::*;
#[derive(Debug, Clone)]
pub struct Error(String);
define_high_low_close_period_fn!(
=>
average_directional_movement_index,
ADX
);
define_high_low_close_period_fn!(
=>
average_true_range,
ATR
);
define_high_low_close_period_fn!(
=>
normalized_average_true_range,
NATR
);
define_high_low_close_period_fn!(
=>
negative_directional_indicator,
MINUS_DI
);
define_high_low_close_period_fn!(
=>
positive_directional_indicator,
PLUS_DI
);
define_high_low_close_fn!(
=>
true_range,
TRANGE
);
define_values_period_fn!(
=>
exponential_moving_average,
EMA
);
define_values_period_fn!(
=>
simple_moving_average,
SMA
);
#[repr(C)]
pub enum MovingAverageType {
SimpleMovingAverage = ta::MAType::MAType_SMA as _,
ExponentialMovingAverage = ta::MAType::MAType_EMA as _,
WeightedMovingAverage = ta::MAType::MAType_WMA as _,
DoubleExponentialMovingAverage = ta::MAType::MAType_DEMA as _,
TripleExponentialMovingAverage = ta::MAType::MAType_TEMA as _,
TriangularMovingAverage = ta::MAType::MAType_TRIMA as _,
KaufmanAdaptiveMovingAverage = ta::MAType::MAType_KAMA as _,
MesaAdaptiveMovingAverage = ta::MAType::MAType_MAMA as _,
TripleGeneralizedDoubleExponentialMovingAverage = ta::MAType::MAType_T3 as _,
}
pub fn bollinger_bands(
input: &[f64],
period: Option<usize>,
num_std_deviations_up: Option<f64>,
num_std_deviations_down: Option<f64>,
moving_average_type: Option<MovingAverageType>,
) -> Result<(Vec<f64>, Vec<f64>, Vec<f64>, usize), Error> {
assert!(!input.is_empty());
let mut out_begin = MaybeUninit::<i32>::uninit();
let mut out_size = MaybeUninit::<i32>::uninit();
let mut out_upper_band: Vec<f64> = Vec::with_capacity(input.len());
let mut out_middle_band: Vec<f64> = Vec::with_capacity(input.len());
let mut out_lower_band: Vec<f64> = Vec::with_capacity(input.len());
unsafe {
let ret_code = ta::BBANDS(
0,
(input.len() - 1) as _,
input.as_ptr(),
if let Some(period) = period {
period as _
} else {
i32::MIN
},
num_std_deviations_up.unwrap_or(ta::REAL_DEFAULT),
num_std_deviations_down.unwrap_or(ta::REAL_DEFAULT),
transmute(moving_average_type.unwrap_or(MovingAverageType::ExponentialMovingAverage)),
out_begin.as_mut_ptr(),
out_size.as_mut_ptr(),
out_upper_band.as_mut_ptr(),
out_middle_band.as_mut_ptr(),
out_lower_band.as_mut_ptr(),
);
match ret_code {
ta::RetCode::SUCCESS => {
out_upper_band.set_len(out_size.assume_init() as _);
out_middle_band.set_len(out_size.assume_init() as _);
out_lower_band.set_len(out_size.assume_init() as _);
Ok((
out_upper_band,
out_middle_band,
out_lower_band,
out_begin.assume_init() as _,
))
}
_ => Err(Error(format!(
"Could not compute OBV; error: {:?}",
ret_code
))),
}
}
}
pub fn on_balance_volume(close: &[f64], volume: &[f64]) -> Result<(Vec<f64>, usize), Error> {
assert!(!close.is_empty());
assert!(close.len() <= volume.len());
let mut out: Vec<f64> = Vec::with_capacity(close.len());
let mut out_begin = MaybeUninit::<i32>::uninit();
let mut out_size = MaybeUninit::<i32>::uninit();
unsafe {
let ret_code = ta::OBV(
0,
(close.len() - 1) as _,
close.as_ptr(),
volume.as_ptr(),
out_begin.as_mut_ptr(),
out_size.as_mut_ptr(),
out.as_mut_ptr(),
);
match ret_code {
ta::RetCode::SUCCESS => {
out.set_len(out_size.assume_init() as _);
Ok((out, out_begin.assume_init() as _))
}
_ => Err(Error(format!(
"Could not compute OBV; error: {:?}",
ret_code
))),
}
}
}
#[test]
fn test_on_balance_volume() {
println!(
"{:?}",
on_balance_volume(&[1.0, 2.0, 3.0, 4.0], &[1.0, 2.0, 3.0, 4.0]).unwrap()
);
}
#[test]
fn test_sma() {
let close_prices = [
1.087010, 1.087120, 1.087080, 1.087170, 1.087110, 1.087010, 1.087100, 1.087120, 1.087110,
1.087080, 1.087000, 1.086630, 1.086630, 1.086610, 1.086630, 1.086640, 1.086650, 1.086650,
1.086670, 1.086630,
];
let (sma_values, begin) = simple_moving_average(&close_prices, Some(10)).unwrap();
for (index, value) in sma_values.iter().enumerate() {
println!("Close index {} = {}", begin + index + 1, value);
}
}