use crate::enums::bot::TimeFrameKind;
use chrono::{Datelike, NaiveDateTime, Timelike};
use polars::prelude::{BooleanChunked, IntoSeries, Series};
use std::fmt;
pub trait InInterval {
fn in_time_interval(&self, val: &Series, granularity: &TimeFrameKind) -> Series;
}
#[derive(Debug, Clone, Copy)]
pub struct TimeInterval {
pub start_day: chrono::Weekday,
pub start_h: u32,
pub end_day: chrono::Weekday,
pub end_h: u32,
}
impl InInterval for TimeInterval {
fn in_time_interval(&self, val: &Series, time_frame: &TimeFrameKind) -> Series {
val.i64()
.unwrap()
.into_iter()
.map(|o: Option<i64>| {
o.map(|ts: i64| match time_frame {
TimeFrameKind::Weekly => self.in_weekly_time_interval(ts),
TimeFrameKind::Daily => self.in_daily_time_interval(ts),
})
})
.collect::<BooleanChunked>()
.into_series()
}
}
impl TimeInterval {
fn in_weekly_time_interval(&self, utc_ts_in_milliseconds: i64) -> bool {
let ts = NaiveDateTime::from_timestamp_opt(utc_ts_in_milliseconds / 1000, 0).unwrap();
let weekend = ts.weekday() == chrono::Weekday::Sat || ts.weekday() == chrono::Weekday::Sun;
let too_early = ts.hour() < self.start_h
&& ts.weekday().number_from_monday() <= self.start_day.number_from_monday();
let too_late = ts.hour() >= self.end_h
&& ts.weekday().number_from_monday() >= self.end_day.number_from_monday();
!(weekend || too_early || too_late)
}
fn in_daily_time_interval(&self, utc_ts_in_milliseconds: i64) -> bool {
let ts = NaiveDateTime::from_timestamp_opt(utc_ts_in_milliseconds / 1000, 0).unwrap();
let weekend = ts.weekday() == chrono::Weekday::Sat || ts.weekday() == chrono::Weekday::Sun;
let too_early = ts.hour() < self.start_h;
let too_late = ts.hour() >= self.end_h;
!(weekend || too_early || too_late)
}
}
impl fmt::Display for TimeInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:?}{}h{}m-{:?}{}h{}m",
self.start_day, self.start_h, 0, self.end_day, self.end_h, 0
)
}
}
pub fn timestamp_in_milli_to_string(ts: i64) -> String {
NaiveDateTime::from_timestamp_opt(ts / 1000, 0)
.unwrap()
.format("%Y-%m-%d %H:%M:%S")
.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lazy_frame_operations::closures::{get_cw_from_ts, get_weekday_from_ts};
use polars::prelude::{df, DataFrame, NamedFrom};
#[tokio::test]
async fn test_get_cw_from_ts() {
let df: polars::prelude::PolarsResult<DataFrame> = df!(
"atid" => &[0, 1],
"px" => &[10.00, 20.00],
"qx" => &[1.00, 1.00],
"ftid" => &[0, 0],
"ltid" => &[0, 0],
"ts" => &[1645300600000_i64, 1645400600000],
"bm" => &[false, true],
"btpm" => &[true, true],
"cw" => &[1645300600000_i64, 1645400600000],
);
let target_df: polars::prelude::PolarsResult<DataFrame> = df!(
"atid" => &[0, 1],
"px" => &[10.00, 20.00],
"qx" => &[1.00, 1.00],
"ftid" => &[0, 0],
"ltid" => &[0, 0],
"ts" => &[1645300600000_i64, 1645400600000],
"bm" => &[false, true],
"btpm" => &[true, true],
"cw" => &[7_i64, 7],
);
assert_eq!(
df.unwrap()
.apply("cw", get_cw_from_ts)
.unwrap()
.frame_equal(&target_df.unwrap()),
true
);
}
#[tokio::test]
async fn test_get_weekday_from_ts() {
let df: polars::prelude::PolarsResult<DataFrame> = df!(
"atid" => &[0, 1],
"px" => &[10.00, 20.00],
"qx" => &[1.00, 1.00],
"ftid" => &[0, 0],
"ltid" => &[0, 0],
"ts" => &[1645300600000_i64, 1645400600000],
"bm" => &[false, true],
"btpm" => &[true, true],
"weekday" => &[1645300600000_i64, 1645400600000],
);
let target_df: polars::prelude::PolarsResult<DataFrame> = df!(
"atid" => &[0, 1],
"px" => &[10.00, 20.00],
"qx" => &[1.00, 1.00],
"ftid" => &[0, 0],
"ltid" => &[0, 0],
"ts" => &[1645300600000_i64, 1645400600000],
"bm" => &[false, true],
"btpm" => &[true, true],
"weekday" => &[6_i64, 7],
);
assert_eq!(
df.unwrap()
.apply("weekday", get_weekday_from_ts)
.unwrap()
.frame_equal(&target_df.unwrap()),
true
);
}
#[test]
fn test_in_time_interval() {
let time_interval_config = TimeInterval {
start_day: chrono::Weekday::Mon,
start_h: 1,
end_day: chrono::Weekday::Fri,
end_h: 23,
};
assert_eq!(
time_interval_config.in_weekly_time_interval(1661128200000),
false
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661129999000),
false
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661130000000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661169600000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661209140000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661209200000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661301000000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661302799000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661302800000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661342400000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661381999000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661382000000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661473800000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661475599000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661475600000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661515200000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661554799000),
true
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661554800000),
false
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661601600000),
false
);
assert_eq!(
time_interval_config.in_weekly_time_interval(1661688000000),
false
);
}
}