Skip to main content

kestrel_chartkit/analytics/
position.rs

1//! Position-to-signal alignment and distance, without broker fields or presentation text.
2use crate::{Agreement, SignalDirection};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[cfg_attr(
6    feature = "serde",
7    derive(serde::Serialize),
8    serde(rename_all = "snake_case")
9)]
10pub enum PositionAlignment {
11    Aligned,
12    Opposed,
13    Neutral,
14    Conflict,
15    Unconfigured,
16}
17
18pub fn position_alignment(
19    position: SignalDirection,
20    recommendation: Option<&Agreement>,
21) -> PositionAlignment {
22    let Some(recommendation) = recommendation else {
23        return PositionAlignment::Unconfigured;
24    };
25    if recommendation.conflict {
26        PositionAlignment::Conflict
27    } else if recommendation.direction == SignalDirection::Neutral
28        || position == SignalDirection::Neutral
29    {
30        PositionAlignment::Neutral
31    } else if recommendation.direction == position {
32        PositionAlignment::Aligned
33    } else {
34        PositionAlignment::Opposed
35    }
36}
37
38/// Unsigned price distance in ATR units; unavailable for a non-positive ATR.
39pub fn atr_distance(entry: f64, current: f64, atr: f64) -> Option<f64> {
40    (atr > 0.0).then_some((current - entry).abs() / atr)
41}