templar_proxy_oracle_kernel/proxy/circuit_breaker/rule/
windowed_change_delta.rs1#[cfg(feature = "schemars")]
2use alloc::borrow::ToOwned;
3#[cfg(any(feature = "borsh", feature = "schemars"))]
4use alloc::string::ToString;
5use templar_primitives::Decimal;
6
7use crate::proxy::circuit_breaker::{
8 math::relative_sum_change_exceeds, CircuitBreakerRule, Observation, RingBuffer,
9};
10
11serialize! {
12 #[derive(Debug, Clone, PartialEq, Eq)]
13 pub struct WindowedChangeDelta {
15 pub window_len: u32,
17 pub lookback_windows: u32,
23 pub max_relative_mean_change: Decimal,
25 }
26}
27
28impl CircuitBreakerRule for WindowedChangeDelta {
29 fn should_trip(&self, history: &RingBuffer<Observation>) -> bool {
30 let window_len = self.window_len as usize;
31 let lookback_windows = self.lookback_windows as usize;
32 if window_len < 2 || lookback_windows == 0 {
33 return false;
34 }
35
36 let Some(current_start) = history.len().checked_sub(window_len) else {
37 return false;
38 };
39 let Some(lookback_offset) = lookback_windows.checked_mul(window_len) else {
40 return false;
41 };
42 let Some(previous_start) = current_start.checked_sub(lookback_offset) else {
43 return false;
44 };
45 let previous = history
46 .iter()
47 .skip(previous_start)
48 .take(window_len)
49 .map(|observation| &observation.price);
50 let current = history
51 .iter()
52 .skip(current_start)
53 .take(window_len)
54 .map(|observation| &observation.price);
55
56 relative_sum_change_exceeds(previous, current, self.max_relative_mean_change)
57 }
58}