mod engine;
use engine::*;
pub fn ht_dcperiod(price: &[f64]) -> Vec<f64> {
let n = price.len();
if n <= LB_PERIOD {
return vec![f64::NAN; n];
}
let mut out = Vec::with_capacity(n);
out.resize(LB_PERIOD, f64::NAN);
let mut core = HtCoreState::seed(price);
for i in CORE_START..LB_PERIOD {
let _ = core.step(price, i);
}
for i in LB_PERIOD..n {
out.push(core.step(price, i).smooth_period);
}
out
}
pub fn ht_phasor(price: &[f64]) -> (Vec<f64>, Vec<f64>) {
let n = price.len();
let mut inphase = vec![f64::NAN; n];
let mut quad = vec![f64::NAN; n];
if n <= CORE_START {
return (inphase, quad);
}
let mut core = HtCoreState::seed(price);
for i in CORE_START..n {
let b = core.step(price, i);
if i >= LB_PERIOD {
inphase[i] = b.i1;
quad[i] = b.q1;
}
}
(inphase, quad)
}
pub fn ht_phasor_line(price: &[f64], quadrature: bool) -> Vec<f64> {
let n = price.len();
let mut out = vec![f64::NAN; n];
if n <= CORE_START {
return out;
}
let mut core = HtCoreState::seed(price);
#[allow(clippy::needless_range_loop)] for i in CORE_START..n {
let b = core.step(price, i);
if i >= LB_PERIOD {
out[i] = if quadrature { b.q1 } else { b.i1 };
}
}
out
}
pub fn ht_dcphase(price: &[f64]) -> Vec<f64> {
let core = ht_core(price);
let phase = dcphase_all(&core);
let mut out = vec![f64::NAN; price.len()];
if price.len() > LB_PHASE {
out[LB_PHASE..].copy_from_slice(&phase[LB_PHASE..price.len()]);
}
out
}
pub fn ht_sine(price: &[f64]) -> (Vec<f64>, Vec<f64>) {
let core = ht_core(price);
let phase = dcphase_all(&core);
let n = price.len();
let mut sine = vec![f64::NAN; n];
let mut lead = vec![f64::NAN; n];
for i in LB_PHASE..n {
sine[i] = (phase[i] * DEG2RAD).sin();
lead[i] = ((phase[i] + 45.0) * DEG2RAD).sin();
}
(sine, lead)
}
pub fn ht_trendline(price: &[f64]) -> Vec<f64> {
let n = price.len();
let mut out = vec![f64::NAN; n];
if n <= CORE_START {
return out;
}
let mut prefix = Vec::with_capacity(n + 1);
prefix.push(0.0);
for &p in price {
prefix.push(prefix[prefix.len() - 1] + p);
}
let mut core = HtCoreState::seed(price);
let (mut it1, mut it2, mut it3) = (0.0f64, 0.0f64, 0.0f64);
for today in CORE_START..n {
let b = core.step(price, today);
let dc = (b.smooth_period + 0.5) as usize;
let lo = (today + 1).saturating_sub(dc);
let avg = (prefix[today + 1] - prefix[lo]) / dc as f64;
if today >= LB_PHASE {
out[today] = (4.0 * avg + 3.0 * it1 + 2.0 * it2 + it3) / 10.0;
}
it3 = it2;
it2 = it1;
it1 = avg;
}
out
}
pub fn ht_trendmode(price: &[f64]) -> Vec<f64> {
let core = ht_core(price);
let phase = dcphase_all(&core);
let trend_line = trendline_all(&core, price);
let n = price.len();
let mut out = vec![f64::NAN; n];
let mut days_in_trend: i32 = 0;
for i in CORE_START..n {
let trend = trendmode_decide(
phase[i],
phase[i - 1],
core[i].smooth_period,
core[i].smoothed,
trend_line[i],
&mut days_in_trend,
);
if i >= LB_PHASE {
out[i] = trend as f64;
}
}
out
}
pub fn mama(price: &[f64], fast_limit: f64, slow_limit: f64) -> (Vec<f64>, Vec<f64>) {
let n = price.len();
let mut mama_out = vec![f64::NAN; n];
let mut fama_out = vec![f64::NAN; n];
if n <= CORE_START {
return (mama_out, fama_out);
}
let mut mama = 0.0f64;
let mut fama = 0.0f64;
let mut prev_phase = 0.0f64;
let mut core = HtCoreState::seed(price);
for i in CORE_START..n {
let b = core.step(price, i);
mama_step(
&b,
price[i],
fast_limit,
slow_limit,
&mut mama,
&mut fama,
&mut prev_phase,
);
if i >= LB_PERIOD {
mama_out[i] = mama;
fama_out[i] = fama;
}
}
(mama_out, fama_out)
}
pub fn mama_line(price: &[f64], fast_limit: f64, slow_limit: f64, want_fama: bool) -> Vec<f64> {
let n = price.len();
let mut out = vec![f64::NAN; n];
if n <= CORE_START {
return out;
}
let mut mama = 0.0f64;
let mut fama = 0.0f64;
let mut prev_phase = 0.0f64;
let mut core = HtCoreState::seed(price);
for i in CORE_START..n {
let b = core.step(price, i);
mama_step(
&b,
price[i],
fast_limit,
slow_limit,
&mut mama,
&mut fama,
&mut prev_phase,
);
if i >= LB_PERIOD {
out[i] = if want_fama { fama } else { mama };
}
}
out
}
fn ht_core_final_state(price: &[f64]) -> Option<Vec<f64>> {
let n = price.len();
if n <= CORE_START {
return None;
}
let mut core = HtCoreState::seed(price);
for today in CORE_START..n {
let _ = core.step(price, today);
}
Some(core.serialize())
}
fn ht_core_resume_setup(state: &[f64], from: usize) -> Option<HtCoreState> {
if from <= CORE_START {
return None;
}
HtCoreState::deserialize(state)
}
pub fn ht_core_state(price: &[f64]) -> Option<Vec<f64>> {
ht_core_final_state(price)
}
pub fn ht_dcperiod_resume(
price: &[f64],
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let mut core = ht_core_resume_setup(state, from)?;
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
out.push(core.step(price, today).smooth_period);
}
Some((out, core.serialize()))
}
pub fn ht_phasor_resume(
price: &[f64],
quad: bool,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let mut core = ht_core_resume_setup(state, from)?;
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
out.push(if quad { b.q1 } else { b.i1 });
}
Some((out, core.serialize()))
}
fn ht_dcphase_final_state(price: &[f64]) -> Option<Vec<f64>> {
let n = price.len();
if n <= CORE_START {
return None;
}
let mut core = HtCoreState::seed(price);
let mut dc = DcPhase::new();
let mut v = Vec::with_capacity(CORE_STATE_LEN + DCPHASE_STATE_LEN);
for today in CORE_START..n {
let b = core.step(price, today);
let _ = dc.push(b.smoothed, b.smooth_period);
}
v.extend(core.serialize());
dc.push_state(&mut v);
Some(v)
}
pub fn ht_dcphase_state(price: &[f64]) -> Option<Vec<f64>> {
ht_dcphase_final_state(price)
}
pub fn ht_dcphase_resume(
price: &[f64],
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let mut core = ht_core_resume_setup(state, from)?;
let mut off = CORE_STATE_LEN;
if state.len() < CORE_STATE_LEN + DCPHASE_STATE_LEN {
return None;
}
let mut dc = DcPhase::from_state(state, &mut off);
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
out.push(dc.push(b.smoothed, b.smooth_period));
}
let mut v = core.serialize();
dc.push_state(&mut v);
Some((out, v))
}
pub fn ht_sine_state(price: &[f64]) -> Option<Vec<f64>> {
ht_dcphase_final_state(price)
}
pub fn ht_sine_resume(
price: &[f64],
lead: bool,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let mut core = ht_core_resume_setup(state, from)?;
let mut off = CORE_STATE_LEN;
if state.len() < CORE_STATE_LEN + DCPHASE_STATE_LEN {
return None;
}
let mut dc = DcPhase::from_state(state, &mut off);
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
let phase = dc.push(b.smoothed, b.smooth_period);
out.push(if lead {
((phase + 45.0) * DEG2RAD).sin()
} else {
(phase * DEG2RAD).sin()
});
}
let mut v = core.serialize();
dc.push_state(&mut v);
Some((out, v))
}
fn ht_trendline_final_state(price: &[f64]) -> Option<Vec<f64>> {
let n = price.len();
if n <= CORE_START {
return None;
}
let mut core = HtCoreState::seed(price);
let (mut it1, mut it2, mut it3) = (0.0f64, 0.0f64, 0.0f64);
for today in CORE_START..n {
let b = core.step(price, today);
let avg = trendline_avg(price, today, b.smooth_period);
it3 = it2;
it2 = it1;
it1 = avg;
}
let mut v = core.serialize();
v.push(it1);
v.push(it2);
v.push(it3);
Some(v)
}
pub fn ht_trendline_state(price: &[f64]) -> Option<Vec<f64>> {
ht_trendline_final_state(price)
}
#[inline]
fn trendline_avg(price: &[f64], today: usize, smooth_period: f64) -> f64 {
let dc_period_int = (smooth_period + 0.5) as usize;
let mut sum = 0.0f64;
for j in 0..dc_period_int {
if today >= j {
sum += price[today - j];
}
}
if dc_period_int > 0 {
sum / dc_period_int as f64
} else {
0.0
}
}
pub fn ht_trendline_resume(
price: &[f64],
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from < SMOOTH_PRICE_SIZE {
return None;
}
let mut core = ht_core_resume_setup(state, from)?;
if state.len() < CORE_STATE_LEN + 3 {
return None;
}
let (mut it1, mut it2, mut it3) = (
state[CORE_STATE_LEN],
state[CORE_STATE_LEN + 1],
state[CORE_STATE_LEN + 2],
);
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
let avg = trendline_avg(price, today, b.smooth_period);
out.push((4.0 * avg + 3.0 * it1 + 2.0 * it2 + it3) / 10.0);
it3 = it2;
it2 = it1;
it1 = avg;
}
let mut v = core.serialize();
v.push(it1);
v.push(it2);
v.push(it3);
Some((out, v))
}
#[inline]
fn trendmode_decide(
dc: f64,
prev_dc: f64,
smooth_period: f64,
smoothed: f64,
trend_line: f64,
days_in_trend: &mut i32,
) -> i32 {
let sine = (dc * DEG2RAD).sin();
let lead = ((dc + 45.0) * DEG2RAD).sin();
let prev_sine = (prev_dc * DEG2RAD).sin();
let prev_lead = ((prev_dc + 45.0) * DEG2RAD).sin();
let sp = smooth_period;
let mut trend = 1i32;
if (sine > lead && prev_sine <= prev_lead) || (sine < lead && prev_sine >= prev_lead) {
*days_in_trend = 0;
trend = 0;
}
*days_in_trend += 1;
if (*days_in_trend as f64) < 0.5 * sp {
trend = 0;
}
let dphase = dc - prev_dc;
if sp != 0.0 && dphase > 0.67 * 360.0 / sp && dphase < 1.5 * 360.0 / sp {
trend = 0;
}
if trend_line != 0.0 && ((smoothed - trend_line) / trend_line).abs() >= 0.015 {
trend = 1;
}
trend
}
fn ht_trendmode_final_state(price: &[f64]) -> Option<Vec<f64>> {
let n = price.len();
if n <= CORE_START {
return None;
}
let mut core = HtCoreState::seed(price);
let mut dc = DcPhase::new();
let (mut it1, mut it2, mut it3) = (0.0f64, 0.0f64, 0.0f64);
let mut days_in_trend: i32 = 0;
let mut prev_phase = 0.0f64;
for today in CORE_START..n {
let b = core.step(price, today);
let phase = dc.push(b.smoothed, b.smooth_period);
let avg = trendline_avg(price, today, b.smooth_period);
let trend_line = (4.0 * avg + 3.0 * it1 + 2.0 * it2 + it3) / 10.0;
let _ = trendmode_decide(
phase,
prev_phase,
b.smooth_period,
b.smoothed,
trend_line,
&mut days_in_trend,
);
it3 = it2;
it2 = it1;
it1 = avg;
prev_phase = phase;
}
let mut v = core.serialize();
dc.push_state(&mut v);
v.push(it1);
v.push(it2);
v.push(it3);
v.push(days_in_trend as f64);
v.push(prev_phase);
Some(v)
}
pub fn ht_trendmode_state(price: &[f64]) -> Option<Vec<f64>> {
ht_trendmode_final_state(price)
}
pub fn ht_trendmode_resume(
price: &[f64],
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
if from < SMOOTH_PRICE_SIZE {
return None;
}
let mut core = ht_core_resume_setup(state, from)?;
let tail = CORE_STATE_LEN + DCPHASE_STATE_LEN;
if state.len() < tail + 5 {
return None;
}
let mut off = CORE_STATE_LEN;
let mut dc = DcPhase::from_state(state, &mut off);
let (mut it1, mut it2, mut it3) = (state[tail], state[tail + 1], state[tail + 2]);
let mut days_in_trend = state[tail + 3] as i32;
let mut prev_phase = state[tail + 4];
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
let phase = dc.push(b.smoothed, b.smooth_period);
let avg = trendline_avg(price, today, b.smooth_period);
let trend_line = (4.0 * avg + 3.0 * it1 + 2.0 * it2 + it3) / 10.0;
let trend = trendmode_decide(
phase,
prev_phase,
b.smooth_period,
b.smoothed,
trend_line,
&mut days_in_trend,
);
out.push(trend as f64);
it3 = it2;
it2 = it1;
it1 = avg;
prev_phase = phase;
}
let mut v = core.serialize();
dc.push_state(&mut v);
v.push(it1);
v.push(it2);
v.push(it3);
v.push(days_in_trend as f64);
v.push(prev_phase);
Some((out, v))
}
fn mama_final_state(price: &[f64], fast_limit: f64, slow_limit: f64) -> Option<Vec<f64>> {
let n = price.len();
if n <= CORE_START {
return None;
}
let mut core = HtCoreState::seed(price);
let (mut mama, mut fama, mut prev_phase) = (0.0f64, 0.0f64, 0.0f64);
for today in CORE_START..n {
let b = core.step(price, today);
mama_step(
&b,
price[today],
fast_limit,
slow_limit,
&mut mama,
&mut fama,
&mut prev_phase,
);
}
let mut v = core.serialize();
v.push(mama);
v.push(fama);
v.push(prev_phase);
Some(v)
}
pub fn mama_state(price: &[f64], fast_limit: f64, slow_limit: f64) -> Option<Vec<f64>> {
mama_final_state(price, fast_limit, slow_limit)
}
#[inline]
fn mama_step(
b: &HtBar,
price_today: f64,
fast_limit: f64,
slow_limit: f64,
mama: &mut f64,
fama: &mut f64,
prev_phase: &mut f64,
) {
let phase = if b.i1 != 0.0 {
(b.q1 / b.i1).atan() * RAD2DEG
} else {
0.0
};
let mut delta = *prev_phase - phase;
*prev_phase = phase;
if delta < 1.0 {
delta = 1.0;
}
let alpha = if delta > 1.0 {
(fast_limit / delta).max(slow_limit)
} else {
fast_limit
};
*mama = alpha * price_today + (1.0 - alpha) * *mama;
let half = 0.5 * alpha;
*fama = half * *mama + (1.0 - half) * *fama;
}
pub fn mama_resume(
price: &[f64],
fast_limit: f64,
slow_limit: f64,
want_fama: bool,
from: usize,
state: &[f64],
) -> Option<(Vec<f64>, Vec<f64>)> {
let mut core = ht_core_resume_setup(state, from)?;
if state.len() < CORE_STATE_LEN + 3 {
return None;
}
let (mut mama, mut fama, mut prev_phase) = (
state[CORE_STATE_LEN],
state[CORE_STATE_LEN + 1],
state[CORE_STATE_LEN + 2],
);
let n = price.len();
let mut out = Vec::with_capacity(n.saturating_sub(from));
for today in from..n {
let b = core.step(price, today);
mama_step(
&b,
price[today],
fast_limit,
slow_limit,
&mut mama,
&mut fama,
&mut prev_phase,
);
out.push(if want_fama { fama } else { mama });
}
let mut v = core.serialize();
v.push(mama);
v.push(fama);
v.push(prev_phase);
Some((out, v))
}
#[cfg(test)]
#[path = "hilbert_tests.rs"]
mod hilbert_tests;