Skip to content

Commit de6e182

Browse files
authored
add fractal adaptive ma (#109)
1 parent cd0dfec commit de6e182

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

benches/traquer.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,15 @@ fn criterion_benchmark(c: &mut Criterion) {
584584
c.bench_function("ma-t3", |b| {
585585
b.iter(|| black_box(smooth::t3(&stats.close, 6, None).collect::<Vec<f64>>()))
586586
});
587+
c.bench_function("ma-frama", |b| {
588+
b.iter(|| {
589+
black_box(
590+
smooth::frama(&stats.close, 16)
591+
.unwrap()
592+
.collect::<Vec<f64>>(),
593+
)
594+
})
595+
});
587596

588597
c.bench_function("correlation-pcc", |b| {
589598
b.iter(|| black_box(correlation::pcc(&stats.open, &stats.close, 16).collect::<Vec<_>>()))

src/smooth.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,3 +921,57 @@ pub fn t3<T: ToPrimitive>(
921921
.map(move |(e3, e4, e5, e6)| c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3),
922922
)
923923
}
924+
925+
/// Fractal Adaptive Moving Average
926+
///
927+
/// Based on argument that market prices are fractal, this algorithm considers fractal shapes as
928+
/// self-similar because they tend to have the same roughness and sparseness regardless of the
929+
/// magnification used to view them.
930+
///
931+
/// ## Sources
932+
///
933+
/// [[1]](https://www.mesasoftware.com/papers/FRAMA.pdf)
934+
///
935+
/// # Examples
936+
///
937+
/// ```
938+
/// use traquer::smooth;
939+
///
940+
/// match smooth::frama(&[1.0,2.0,3.0,4.0,5.0,2.0,3.0,4.0,2.0,3.0,4.0,2.0,3.0,4.0], 3){
941+
/// Ok(iter) => {
942+
/// let ma = iter.collect::<Vec<f64>>();
943+
/// },
944+
/// Err(e) => println!("Error: {}", e),
945+
/// }
946+
/// ```
947+
pub fn frama<T: ToPrimitive>(
948+
data: &[T],
949+
window: usize,
950+
) -> Result<impl Iterator<Item = f64> + '_, &'static str> {
951+
if window % 2 != 0 {
952+
return Err("Window must be an even number");
953+
}
954+
let mid = window / 2;
955+
Ok(iter::repeat(f64::NAN)
956+
.take(window - 1)
957+
.chain(data.windows(window).scan(0., move |state, w| {
958+
let (hh1, ll1, hh2, ll2) = w.iter().take(mid).zip(w.iter().rev().take(mid)).fold(
959+
(f64::MIN, f64::MAX, f64::MIN, f64::MAX),
960+
|(hh1, ll1, hh2, ll2), (x1, x2)| {
961+
(
962+
hh1.max(x1.to_f64().unwrap()),
963+
ll1.min(x1.to_f64().unwrap()),
964+
hh2.max(x2.to_f64().unwrap()),
965+
ll2.min(x2.to_f64().unwrap()),
966+
)
967+
},
968+
);
969+
let n1 = (hh1 - ll1) / mid as f64;
970+
let n2 = (hh2 - ll2) / mid as f64;
971+
let n3 = (hh1.max(hh2) - ll1.min(ll2)) / window as f64;
972+
let d = ((n1 + n2).ln() - n3.ln()) / 2_f64.ln();
973+
let alpha = (-4.6 * (d - 1.)).exp().clamp(0.01, 1.);
974+
*state = alpha * w.last().unwrap().to_f64().unwrap() + (1. - alpha) * *state;
975+
Some(*state)
976+
})))
977+
}

tests/ma_test.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,3 +1618,44 @@ fn test_t3() {
16181618
result[12..]
16191619
);
16201620
}
1621+
1622+
#[test]
1623+
fn test_frama() {
1624+
let stats = common::test_data();
1625+
assert!(smooth::frama(&stats.close, 3).is_err());
1626+
1627+
let result = smooth::frama(&stats.close, 8).unwrap().collect::<Vec<_>>();
1628+
assert_eq!(stats.close.len(), result.len());
1629+
assert_eq!(
1630+
vec![
1631+
11.735817449990835,
1632+
35.08640357543414,
1633+
38.21403178926078,
1634+
41.68248606395869,
1635+
42.28922749250002,
1636+
42.23389147160019,
1637+
43.956450640092505,
1638+
42.27000045776367,
1639+
40.0,
1640+
40.17812608700349,
1641+
39.32672748964177,
1642+
41.720001220703125,
1643+
41.5875219933315,
1644+
41.640739963598065,
1645+
41.7792881667438,
1646+
42.163913483292994,
1647+
42.45000076293945,
1648+
45.43000030517578,
1649+
45.97753225132534,
1650+
45.2928023379573,
1651+
45.880001068115234,
1652+
46.458327365816054,
1653+
46.483063104549856,
1654+
47.6326315798041,
1655+
49.400001525878906,
1656+
50.180656289232715,
1657+
49.29499816894531
1658+
],
1659+
result[8 - 1..]
1660+
);
1661+
}

0 commit comments

Comments
 (0)