use crate::{Float, Result, TalibError};
#[derive(Debug, Clone, Copy)]
pub struct CandleInput<'a> {
pub open: &'a [Float],
pub high: &'a [Float],
pub low: &'a [Float],
pub close: &'a [Float],
}
impl CandleInput<'_> {
#[inline]
pub const fn len(&self) -> usize {
self.open.len()
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.open.is_empty()
}
#[inline]
pub(crate) fn max_len(&self) -> usize {
self.open
.len()
.max(self.high.len())
.max(self.low.len())
.max(self.close.len())
}
#[inline]
pub(crate) fn candle(&self, index: usize) -> Candle {
Candle {
open: self.open[index],
high: self.high[index],
low: self.low[index],
close: self.close[index],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Candle {
pub open: Float,
pub high: Float,
pub low: Float,
pub close: Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternDirection {
Bullish,
Bearish,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternStrength {
Partial,
Standard,
Confirmed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PatternSignal {
NoMatch,
Match {
direction: PatternDirection,
strength: PatternStrength,
},
}
impl PatternSignal {
#[inline]
pub(crate) const fn standard(direction: PatternDirection) -> Self {
Self::Match {
direction,
strength: PatternStrength::Standard,
}
}
#[inline]
pub const fn to_talib_code(self) -> i32 {
let (direction, strength) = match self {
Self::NoMatch => return 0,
Self::Match {
direction,
strength,
} => (direction, strength),
};
let magnitude = match strength {
PatternStrength::Partial => 80,
PatternStrength::Standard => 100,
PatternStrength::Confirmed => 200,
};
match direction {
PatternDirection::Bullish => magnitude,
PatternDirection::Bearish => -magnitude,
}
}
pub fn from_talib_code(code: i32) -> Result<Self> {
let (direction, strength) = match code {
0 => return Ok(Self::NoMatch),
80 => (PatternDirection::Bullish, PatternStrength::Partial),
-80 => (PatternDirection::Bearish, PatternStrength::Partial),
100 => (PatternDirection::Bullish, PatternStrength::Standard),
-100 => (PatternDirection::Bearish, PatternStrength::Standard),
200 => (PatternDirection::Bullish, PatternStrength::Confirmed),
-200 => (PatternDirection::Bearish, PatternStrength::Confirmed),
_ => {
return Err(TalibError::invalid_input(
"Pattern Signal code must be exactly 0, ±80, ±100, or ±200",
))
}
};
Ok(Self::Match {
direction,
strength,
})
}
}
impl TryFrom<i32> for PatternSignal {
type Error = TalibError;
fn try_from(value: i32) -> core::result::Result<Self, Self::Error> {
Self::from_talib_code(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CandleRangeKind {
RealBody,
HighLow,
Shadows,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CandleSetting {
range_kind: CandleRangeKind,
average_period: usize,
factor: Float,
}
impl CandleSetting {
pub fn new(range_kind: CandleRangeKind, average_period: usize, factor: Float) -> Result<Self> {
if average_period > 100_000 {
return Err(TalibError::invalid_period(
average_period,
"Candle Setting Average Period must not exceed 100000",
));
}
if !factor.is_finite() || factor < 0.0 as Float {
return Err(TalibError::invalid_input(
"Candle Setting Factor must be finite and nonnegative",
));
}
Ok(Self {
range_kind,
average_period,
factor,
})
}
const fn default_value(
range_kind: CandleRangeKind,
average_period: usize,
factor: Float,
) -> Self {
Self {
range_kind,
average_period,
factor,
}
}
#[inline]
pub const fn range_kind(self) -> CandleRangeKind {
self.range_kind
}
#[inline]
pub const fn average_period(self) -> usize {
self.average_period
}
#[inline]
pub const fn factor(self) -> Float {
self.factor
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(usize)]
pub enum CandleSettingType {
BodyLong = 0,
BodyVeryLong = 1,
BodyShort = 2,
BodyDoji = 3,
ShadowLong = 4,
ShadowVeryLong = 5,
ShadowShort = 6,
ShadowVeryShort = 7,
Near = 8,
Far = 9,
Equal = 10,
}
impl CandleSettingType {
pub const ALL: [Self; 11] = [
Self::BodyLong,
Self::BodyVeryLong,
Self::BodyShort,
Self::BodyDoji,
Self::ShadowLong,
Self::ShadowVeryLong,
Self::ShadowShort,
Self::ShadowVeryShort,
Self::Near,
Self::Far,
Self::Equal,
];
#[inline]
pub(crate) const fn index(self) -> usize {
self as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CandleSettings {
settings: [CandleSetting; 11],
}
impl CandleSettings {
#[inline]
pub const fn setting(self, setting_type: CandleSettingType) -> CandleSetting {
self.settings[setting_type.index()]
}
#[inline]
pub fn with_setting(mut self, setting_type: CandleSettingType, setting: CandleSetting) -> Self {
self.settings[setting_type.index()] = setting;
self
}
}
impl Default for CandleSettings {
fn default() -> Self {
use CandleRangeKind::{HighLow, RealBody, Shadows};
Self {
settings: [
CandleSetting::default_value(RealBody, 10, 1.0 as Float),
CandleSetting::default_value(RealBody, 10, 3.0 as Float),
CandleSetting::default_value(RealBody, 10, 1.0 as Float),
CandleSetting::default_value(HighLow, 10, 0.1 as Float),
CandleSetting::default_value(RealBody, 0, 1.0 as Float),
CandleSetting::default_value(RealBody, 0, 2.0 as Float),
CandleSetting::default_value(Shadows, 10, 1.0 as Float),
CandleSetting::default_value(HighLow, 10, 0.1 as Float),
CandleSetting::default_value(HighLow, 5, 0.2 as Float),
CandleSetting::default_value(HighLow, 5, 0.6 as Float),
CandleSetting::default_value(HighLow, 5, 0.05 as Float),
],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Penetration(Float);
impl Penetration {
pub fn new(value: Float) -> Result<Self> {
if !value.is_finite() || value < 0.0 as Float {
return Err(TalibError::invalid_input(
"Penetration must be finite and nonnegative",
));
}
Ok(Self(value))
}
#[inline]
pub const fn value(self) -> Float {
self.0
}
#[inline]
pub(crate) fn wide_value(self) -> f64 {
#[cfg(feature = "f32")]
{
self.0 as f64
}
#[cfg(not(feature = "f32"))]
{
self.0
}
}
}