use crate::bar_indicators::indicator_value::IndicatorValue;
use crate::bar_indicators::momentum::rsi::Rsi;
#[derive(Clone)]
pub struct AndGate {
rsi_short: Rsi,
rsi_long: Rsi,
pub v: bool,
}
impl Default for AndGate {
fn default() -> Self {
Self::new()
}
}
impl AndGate {
pub fn new() -> Self {
Self::with_periods(7, 21)
}
pub fn with_periods(short_period: usize, long_period: usize) -> Self {
Self {
rsi_short: Rsi::new(short_period),
rsi_long: Rsi::new(long_period),
v: false,
}
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> bool {
self.rsi_short.update_bar(open, high, low, close, volume);
self.rsi_long.update_bar(open, high, low, close, volume);
if self.is_ready() {
let short_val = self.rsi_short.value().main();
let long_val = self.rsi_long.value().main();
self.v = (short_val > 70.0 && long_val > 70.0) || (short_val < 30.0 && long_val < 30.0);
}
self.v
}
pub fn update(&mut self, a: bool, b: bool) -> bool {
self.v = a && b;
self.v
}
pub fn value(&self) -> bool {
self.v
}
#[inline]
pub fn is_ready(&self) -> bool {
self.rsi_short.is_ready() && self.rsi_long.is_ready()
}
pub fn reset(&mut self) {
self.rsi_short.reset();
self.rsi_long.reset();
self.v = false;
}
}
#[derive(Clone)]
pub struct OrGate {
rsi_short: Rsi,
rsi_long: Rsi,
pub v: bool,
}
impl Default for OrGate {
fn default() -> Self {
Self::new()
}
}
impl OrGate {
pub fn new() -> Self {
Self::with_periods(7, 21)
}
pub fn with_periods(short_period: usize, long_period: usize) -> Self {
Self {
rsi_short: Rsi::new(short_period),
rsi_long: Rsi::new(long_period),
v: false,
}
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> bool {
self.rsi_short.update_bar(open, high, low, close, volume);
self.rsi_long.update_bar(open, high, low, close, volume);
if self.is_ready() {
let short_val = self.rsi_short.value().main();
let long_val = self.rsi_long.value().main();
self.v = !(30.0..=70.0).contains(&short_val) || !(30.0..=70.0).contains(&long_val);
}
self.v
}
pub fn update(&mut self, a: bool, b: bool) -> bool {
self.v = a || b;
self.v
}
pub fn value(&self) -> bool {
self.v
}
#[inline]
pub fn is_ready(&self) -> bool {
self.rsi_short.is_ready() && self.rsi_long.is_ready()
}
pub fn reset(&mut self) {
self.rsi_short.reset();
self.rsi_long.reset();
self.v = false;
}
}
#[derive(Clone)]
pub struct XorGate {
rsi_short: Rsi,
rsi_long: Rsi,
pub v: bool,
}
impl Default for XorGate {
fn default() -> Self {
Self::new()
}
}
impl XorGate {
pub fn new() -> Self {
Self::with_periods(7, 21)
}
pub fn with_periods(short_period: usize, long_period: usize) -> Self {
Self {
rsi_short: Rsi::new(short_period),
rsi_long: Rsi::new(long_period),
v: false,
}
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> bool {
self.rsi_short.update_bar(open, high, low, close, volume);
self.rsi_long.update_bar(open, high, low, close, volume);
if self.is_ready() {
let short_val = self.rsi_short.value().main();
let long_val = self.rsi_long.value().main();
let short_extreme = !(30.0..=70.0).contains(&short_val);
let long_extreme = !(30.0..=70.0).contains(&long_val);
self.v = short_extreme ^ long_extreme;
}
self.v
}
pub fn update(&mut self, a: bool, b: bool) -> bool {
self.v = a ^ b;
self.v
}
pub fn value(&self) -> bool {
self.v
}
#[inline]
pub fn is_ready(&self) -> bool {
self.rsi_short.is_ready() && self.rsi_long.is_ready()
}
pub fn reset(&mut self) {
self.rsi_short.reset();
self.rsi_long.reset();
self.v = false;
}
}
#[derive(Clone)]
pub struct SignCombiner {
rsi_short: Rsi,
rsi_long: Rsi,
pub s: i8,
}
impl Default for SignCombiner {
fn default() -> Self {
Self::new()
}
}
impl SignCombiner {
pub fn new() -> Self {
Self::with_periods(7, 21)
}
pub fn with_periods(short_period: usize, long_period: usize) -> Self {
Self {
rsi_short: Rsi::new(short_period),
rsi_long: Rsi::new(long_period),
s: 0,
}
}
pub fn update_bar(&mut self, open: f64, high: f64, low: f64, close: f64, volume: f64) -> i8 {
self.rsi_short.update_bar(open, high, low, close, volume);
self.rsi_long.update_bar(open, high, low, close, volume);
if self.is_ready() {
let short_val = self.rsi_short.value().main();
let long_val = self.rsi_long.value().main();
let short_sig: i8 = if short_val < 30.0 { 1 } else if short_val > 70.0 { -1 } else { 0 };
let long_sig: i8 = if long_val < 30.0 { 1 } else if long_val > 70.0 { -1 } else { 0 };
self.s = (short_sig + long_sig).clamp(-1, 1);
}
self.s
}
pub fn update(&mut self, a: i8, b: i8) -> i8 {
self.s = a.saturating_add(b).clamp(-1, 1);
self.s
}
pub fn value(&self) -> IndicatorValue {
IndicatorValue::Signal(self.s)
}
#[inline]
pub fn is_ready(&self) -> bool {
self.rsi_short.is_ready() && self.rsi_long.is_ready()
}
pub fn reset(&mut self) {
self.rsi_short.reset();
self.rsi_long.reset();
self.s = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logic_gates_with_periods() {
let mut and = AndGate::with_periods(5, 14);
let mut or = OrGate::with_periods(5, 14);
let mut xor = XorGate::with_periods(5, 14);
let mut sc = SignCombiner::with_periods(5, 14);
let mut price = 100.0;
for _ in 0..30 {
price += 1.5;
and.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
or.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
xor.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
sc.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
}
assert!(and.is_ready());
assert!(or.is_ready());
assert!(xor.is_ready());
assert!(sc.is_ready());
assert!(sc.value().as_signal().unwrap().abs() <= 1);
}
#[test]
fn test_and_gate_legacy() {
let mut gate = AndGate::new();
assert!(!gate.update(true, false));
assert!(!gate.update(false, true));
assert!(gate.update(true, true));
assert!(!gate.update(false, false));
gate.reset();
assert!(!gate.value());
}
#[test]
fn test_and_gate_with_data() {
let mut gate = AndGate::new();
assert!(!gate.is_ready());
let mut price = 100.0;
for _ in 0..30 {
price += 0.5;
gate.update_bar(price - 0.2, price + 0.3, price - 0.3, price, 1000.0);
}
assert!(gate.is_ready());
}
#[test]
fn test_or_gate_legacy() {
let mut gate = OrGate::new();
assert!(gate.update(true, false));
assert!(gate.update(false, true));
assert!(gate.update(true, true));
assert!(!gate.update(false, false));
gate.reset();
assert!(!gate.value());
}
#[test]
fn test_or_gate_with_data() {
let mut gate = OrGate::new();
let mut price = 100.0;
for _ in 0..30 {
price += 0.5;
gate.update_bar(price - 0.2, price + 0.3, price - 0.3, price, 1000.0);
}
assert!(gate.is_ready());
}
#[test]
fn test_xor_gate_legacy() {
let mut gate = XorGate::new();
assert!(gate.update(true, false));
assert!(gate.update(false, true));
assert!(!gate.update(true, true));
assert!(!gate.update(false, false));
gate.reset();
assert!(!gate.value());
}
#[test]
fn test_xor_gate_with_divergence() {
let mut rsi_short = Rsi::new(7);
let mut rsi_long = Rsi::new(21);
let mut price = 100.0;
for i in 0..30 {
if i % 2 == 0 {
price += 1.0;
} else {
price -= 1.0;
}
rsi_short.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
rsi_long.update_bar(price - 0.5, price + 0.5, price - 0.5, price, 1000.0);
}
eprintln!("After warmup: short={:.1}, long={:.1}",
rsi_short.value().main(), rsi_long.value().main());
let mut any_divergence = false;
for i in 0..10 {
price += 3.0; rsi_short.update_bar(price - 1.0, price + 1.0, price - 1.5, price, 1000.0);
rsi_long.update_bar(price - 1.0, price + 1.0, price - 1.5, price, 1000.0);
let short_val = rsi_short.value().main();
let long_val = rsi_long.value().main();
let short_extreme = short_val > 70.0 || short_val < 30.0;
let long_extreme = long_val > 70.0 || long_val < 30.0;
eprintln!("Bar {}: short={:.1}, long={:.1}, xor={}",
i, short_val, long_val, short_extreme ^ long_extreme);
if short_extreme ^ long_extreme {
any_divergence = true;
}
}
assert!(any_divergence, "XOR should detect divergence during sharp spike");
}
#[test]
fn test_sign_combiner_legacy() {
let mut sc = SignCombiner::new();
assert_eq!(sc.update(1, 0), 1);
assert_eq!(sc.update(-1, 0), -1);
assert_eq!(sc.update(1, 1), 1); assert_eq!(sc.update(-1, -1), -1); assert_eq!(sc.update(1, -1), 0);
sc.reset();
assert_eq!(sc.value().as_signal(), Some(0));
}
}