qust-io 0.1.5

a part of crate qust
Documentation
#![allow(async_fn_in_trait)]
use csv::StringRecord;
use qust::prelude::{dt, Di, KlineData, PriceOri, TickData, Tri};


trait ReadRecord {
    type Output;
    fn read_record(&self, record: &StringRecord) -> Self::Output;
}

pub trait ReadCsv {
    type Output;
    async fn read_csv(&self, path: &str) -> Self::Output;
}

pub struct DiReader<T> {
    pub t: T,
    pub o: T,
    pub h: T,
    pub l: T,
    pub c: T,
    pub v: T,
    pub t_format: Option<&'static str>,
    pub has_header: bool,
}

struct DiReaderRecord {
    t: usize,
    o: usize,
    h: usize,
    l: usize,
    c: usize,
    v: usize,
    t_format: &'static str,
}


impl ReadRecord for DiReaderRecord {
    type Output = KlineData;
    fn read_record(&self, record: &StringRecord) -> Self::Output {
        KlineData {
            t: {
                let t = record[self.t].trim();
                dt::parse_from_str(t, self.t_format)
                    .unwrap_or_else(|_| panic!("failed to parse time, provided: {}, format: {}", t, self.t_format))
            },
            o: record[self.o].trim().parse().unwrap(),
            h: record[self.h].trim().parse().unwrap(),
            l: record[self.l].trim().parse().unwrap(),
            c: record[self.c].trim().parse().unwrap(),
            v: record[self.v].trim().parse().unwrap(),
            ki: Default::default(),
        }
    }
}


impl ReadCsv for DiReader<usize> {
    type Output = PriceOri;
    async fn read_csv(&self, path: &str) -> Self::Output {
        let di_reader_record = DiReaderRecord {
            t: self.t,
            o: self.o,
            h: self.h,
            l: self.l,
            c: self.c,
            v: self.v,
            t_format: self.t_format.unwrap_or("%Y-%m-%dT%H:%M:%S%.f"),
        };
        let skip_n = if self.has_header { 1 } else { 0 };
        let mut price_ori = PriceOri::with_capacity(100_000);
        if path.contains("https") {
            let response = reqwest::get(path).await.unwrap().text().await.unwrap();
            let mut reader = csv::ReaderBuilder::new()
                .has_headers(self.has_header)
                .from_reader(response.as_bytes());
            for record in reader.records().skip(skip_n) {
                let record = record.unwrap();
                let kline_data = di_reader_record.read_record(&record);
                price_ori.update(&kline_data);
            }
        } else {
            let mut reader = csv::Reader::from_path(path).unwrap();
            for record in reader.records().skip(skip_n) {
                let record = record.unwrap();
                let kline_data = di_reader_record.read_record(&record);
                price_ori.update(&kline_data);
            }
        }
        price_ori.shrink_to_fit();
        price_ori
    }
}

pub struct TickReader<T> {
    pub t: T,
    pub c: T,
    pub v: T,
    pub ask1: T,
    pub bid1: T,
    pub ask1_v: T,
    pub bid1_v: T,
    pub t_format: Option<&'static str>,
    pub has_header: bool,
}

struct TickReaderRecord {
    t: usize,
    c: usize,
    v: usize,
    ask1: usize,
    bid1: usize,
    ask1_v: usize,
    bid1_v: usize,
    t_format: &'static str,
}

impl ReadRecord for TickReaderRecord {
    type Output = TickData;
    fn read_record(&self, record: &StringRecord) -> Self::Output {
        TickData {
            t: {
                let t = record[self.t].trim();
                dt::parse_from_str(t, self.t_format)
                    .unwrap_or_else(|_| panic!("failed to parse time, provided: {}, format: {}", t, self.t_format))
            },
            c: record[self.c].trim().parse().unwrap(),
            v: record[self.v].trim().parse().unwrap(),
            ask1: record[self.ask1].trim().parse().unwrap(),
            bid1: record[self.bid1].trim().parse().unwrap(),
            ask1_v: record[self.ask1_v].trim().parse().unwrap(),
            bid1_v: record[self.bid1_v].trim().parse().unwrap(),
            ct: 1,
        }

    }
}


impl ReadCsv for TickReader<usize> {
    type Output = Vec<TickData>;
    async fn read_csv(&self, path: &str) -> Self::Output {
        let tick_reader_record = TickReaderRecord {
            t: self.t,
            c: self.c,
            v: self.v,
            ask1: self.ask1,
            bid1: self.bid1,
            ask1_v: self.ask1_v,
            bid1_v: self.bid1_v,
            t_format: self.t_format.unwrap_or("%Y-%m-%dT%H:%M:%S%.f"),
        };
        let mut res = Vec::with_capacity(100000);
        if path.contains("https") {
            let response = reqwest::get(path).await.unwrap().text().await.unwrap();
            let mut reader = csv::ReaderBuilder::new()
                .has_headers(self.has_header)
                .from_reader(response.as_bytes());
            for record in reader.records() {
                let record = record.unwrap();
                let tick_data = tick_reader_record.read_record(&record);
                res.push(tick_data);
            }
        } else {
            let mut reader = csv::Reader::from_path(path).unwrap();
            for record in reader.records() {
                let record = record.unwrap();
                let tick_data = tick_reader_record.read_record(&record);
                res.push(tick_data);
            }
        }
        res.shrink_to_fit();
        res
    }
}

const remote_kline_url: &str = "https://raw.githubusercontent.com/baiguoname/qust/refs/heads/main/examples/git_test/kline_data.csv";
const remote_tick_url: &str = "https://raw.githubusercontent.com/baiguoname/qust/refs/heads/main/examples/git_test/tick_data.csv"; 

pub async fn read_remote_kline_data() -> Di {
    let di_reader: DiReader<usize> = DiReader {
        t: 0,
        o: 1,
        h: 2,
        l: 3,
        c: 4,
        v: 5,
        t_format: None,
        has_header: true,
    };
    di_reader.read_csv(remote_kline_url).await.to_di(qust::prelude::aler, qust::prelude::rl5m.tri_box())
}

pub async fn read_remote_tick_data() -> Vec<TickData> {
    let tick_reader = TickReader {
        t: 0,
        c: 1,
        v: 2,
        ask1: 3,
        bid1: 4,
        ask1_v: 5,
        bid1_v: 6,
        t_format: None,
        has_header:true,
    };
    tick_reader
        .read_csv(remote_tick_url)
        .await
}