use super::errors::AnalyzeErorr;
use crate::objects::bar::RawBarBuilder;
use crate::objects::{
bar::{NewBar, NewBarBuilder, RawBar},
bi::{BI, BIBuilder},
direction::Direction,
freq::Freq,
fx::{FX, FXBuilder},
mark::Mark,
};
use anyhow::Context;
use chrono::DateTime;
use chrono::Utc;
use polars::frame::DataFrame;
use polars::prelude::TimeUnit;
pub fn remove_include(
k1: &NewBar,
k2: &NewBar,
k3: RawBar,
) -> Result<(bool, NewBar), AnalyzeErorr> {
let direction = if k1.high < k2.high {
Direction::Up
} else if k1.high > k2.high {
Direction::Down
} else {
return Ok((false, NewBar::new_from_raw(&k3)));
};
let has_inclusion =
(k2.high <= k3.high && k2.low >= k3.low) || (k2.high >= k3.high && k2.low <= k3.low);
if !has_inclusion {
return Ok((false, NewBar::new_from_raw(&k3)));
}
let (high, low, dt) = match direction {
Direction::Up => {
let high = k2.high.max(k3.high);
let low = k2.low.max(k3.low);
let dt = if k2.high > k3.high { k2.dt } else { k3.dt };
(high, low, dt)
}
Direction::Down => {
let high = k2.high.min(k3.high);
let low = k2.low.min(k3.low);
let dt = if k2.low < k3.low { k2.dt } else { k3.dt };
(high, low, dt)
}
};
let (open_, close) = if k3.open > k3.close {
(high, low)
} else {
(low, high)
};
let k4 = {
let k3_dt = k3.dt;
NewBarBuilder::default()
.symbol(k2.symbol.clone())
.id(k2.id)
.freq(k2.freq)
.dt(dt)
.open(open_)
.close(close)
.high(high)
.low(low)
.vol(k2.vol + k3.vol)
.amount(k2.amount + k3.amount)
.elements(
k2.elements
.iter()
.take(100)
.filter(|x| x.dt != k3_dt)
.cloned()
.chain(std::iter::once(k3))
.collect::<Vec<RawBar>>(),
)
.build()
.context("Failed to new NewBar")?
};
Ok((true, k4))
}
pub fn check_fxs<B: AsRef<NewBar>>(bars: &[B]) -> Vec<FX> {
let mut fxs: Vec<FX> = Vec::new();
for window in bars[0..bars.len()].windows(3) {
if let [k1, k2, k3] = window
&& let Some(fx1) = check_fx(k1.as_ref(), k2.as_ref(), k3.as_ref())
{
if fxs.len() >= 2 && fx1.mark == fxs.last().unwrap().mark {
eprintln!(
"check_fxs错误: {},{:?},{:?}",
k2.as_ref().dt,
fx1.mark,
fxs.last().unwrap().mark
);
} else {
fxs.push(fx1);
}
}
}
fxs
}
pub fn check_fx(k1: &NewBar, k2: &NewBar, k3: &NewBar) -> Option<FX> {
if k1.high < k2.high && k2.high > k3.high && k1.low < k2.low && k2.low > k3.low {
return Some(
FXBuilder::default()
.symbol(k1.symbol.clone())
.dt(k2.dt)
.mark(Mark::G)
.high(k2.high)
.low(k2.low)
.fx(k2.high)
.elements(vec![k1.clone(), k2.clone(), k3.clone()])
.build()
.unwrap(),
);
}
if k1.low > k2.low && k2.low < k3.low && k1.high > k2.high && k2.high < k3.high {
return Some(
FXBuilder::default()
.symbol(k1.symbol.clone())
.dt(k2.dt)
.mark(Mark::D)
.high(k2.high)
.low(k2.low)
.fx(k2.low)
.elements(vec![k1.clone(), k2.clone(), k3.clone()])
.build()
.unwrap(),
);
}
None
}
pub fn check_bi<B>(bars: &[B]) -> (Option<BI>, &[B])
where
B: AsRef<NewBar>,
{
let fxs = check_fxs(bars);
if fxs.len() < 2 {
return (None, bars);
}
let fx_a = &fxs[0];
let (direction, fx_b) = match fx_a.mark {
Mark::D => {
let mut fx_b: Option<&FX> = None;
for x in fxs
.iter()
.filter(|x| x.mark == Mark::G && x.dt > fx_a.dt && x.fx > fx_a.fx)
{
match fx_b {
None => fx_b = Some(x),
Some(best) if x.high > best.high => fx_b = Some(x),
_ => {}
}
}
let fx_b = fx_b.cloned();
(Direction::Up, fx_b)
}
Mark::G => {
let mut fx_b: Option<&FX> = None;
for x in fxs
.iter()
.filter(|x| x.mark == Mark::D && x.dt > fx_a.dt && x.fx < fx_a.fx)
{
match fx_b {
None => fx_b = Some(x),
Some(best) if x.low < best.low => fx_b = Some(x),
_ => {}
}
}
let fx_b = fx_b.cloned();
(Direction::Down, fx_b)
}
};
let fx_b = match fx_b {
Some(fx) => fx,
None => return (None, bars),
};
let start_dt = fx_a.elements[0].dt;
let end_dt = fx_b.elements[2].dt;
let start_idx = bars.partition_point(|bar| bar.as_ref().dt < start_dt);
let end_idx = bars.partition_point(|bar| bar.as_ref().dt <= end_dt);
if start_idx >= end_idx {
return (None, bars);
}
let bars_a = &bars[start_idx..end_idx];
let new_start_dt = fx_b.elements[0].dt;
let new_start_idx = bars.partition_point(|bar| bar.as_ref().dt < new_start_dt);
let bars_b = &bars[new_start_idx..];
let ab_include = (fx_a.high > fx_b.high && fx_a.low < fx_b.low)
|| (fx_a.high < fx_b.high && fx_a.low > fx_b.low);
let min_bi_len = 6;
if !ab_include && bars_a.len() >= min_bi_len {
let fxs_filtered: Vec<_> = fxs
.iter()
.filter(|x| x.dt >= start_dt && x.dt <= end_dt)
.cloned()
.collect();
let bi = BIBuilder::default()
.symbol(fx_a.symbol.clone())
.fx_a(fx_a.clone())
.fx_b(fx_b.clone())
.fxs(fxs_filtered)
.direction(direction)
.bars(
bars_a
.iter()
.map(|b| b.as_ref().to_owned())
.collect::<Vec<NewBar>>(),
)
.build()
.unwrap();
(Some(bi), bars_b)
} else {
(None, bars)
}
}
pub fn format_standard_kline(df: DataFrame, freq: Freq) -> Result<Vec<RawBar>, AnalyzeErorr> {
let symbol_col = df.column("symbol")?.str()?;
let dt_col = df.column("dt")?.datetime()?;
let open_col = df.column("open")?.f64()?;
let close_col = df.column("close")?.f64()?;
let high_col = df.column("high")?.f64()?;
let low_col = df.column("low")?.f64()?;
let vol_col = df.column("vol")?.f64()?;
let amount_col = df.column("amount")?.f64()?;
let time_unit = dt_col.time_unit();
let len = df.height();
let mut bars = Vec::with_capacity(len);
for i in 0..len {
let ts = dt_col.get(i).unwrap();
let ns = match time_unit {
TimeUnit::Milliseconds => ts * 1_000_000,
TimeUnit::Microseconds => ts * 1_000,
TimeUnit::Nanoseconds => ts,
};
let dt_utc = DateTime::<Utc>::from_timestamp_nanos(ns);
let bar = RawBarBuilder::default()
.symbol(symbol_col.get(i).unwrap_or(""))
.id(i as i32)
.dt(dt_utc)
.freq(freq)
.open(open_col.get(i).unwrap())
.close(close_col.get(i).unwrap())
.high(high_col.get(i).unwrap())
.low(low_col.get(i).unwrap())
.vol(vol_col.get(i).unwrap())
.amount(amount_col.get(i).unwrap())
.build()
.context("Failed to create raw bar")?;
bars.push(bar);
}
Ok(bars)
}