tdx_rs/tdx_model/builder/
day_line_builder.rs

1use std::{
2    fs::File,
3    io::{Read, Seek, SeekFrom},
4    path::Path,
5};
6
7use byteorder::ByteOrder;
8
9use crate::tdx_model::{data::DailyData, DayLine};
10
11/// for each day, there are 8 numbers, 4 bytes each
12pub const DAY_SIZE: usize = 32;
13
14#[derive(Debug)]
15pub struct DayLineBuilder {
16    pub file: File,
17    pub max_days: u64,
18}
19
20impl DayLineBuilder {
21    pub fn from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
22        let file = File::open(path.as_ref())?;
23        let max_days = std::fs::metadata(path.as_ref())?.len() / (DAY_SIZE as u64);
24
25        Ok(Self { file, max_days })
26    }
27
28    pub fn query_days(mut self, days: u64) -> Self {
29        if self.max_days > days {
30            // take the smaller one between the two values
31            self.max_days = days;
32        }
33
34        let pos_offset = (self.max_days * DAY_SIZE as u64) as i64;
35        self.file.seek(SeekFrom::End(0 - pos_offset)).unwrap();
36
37        self
38    }
39
40    pub fn build(mut self) -> DayLine {
41        let mut buffer = [0u8; DAY_SIZE];
42        let mut day_line = Vec::with_capacity(self.max_days as usize);
43
44        for _ in 0..self.max_days {
45            self.file.read_exact(buffer.as_mut()).unwrap();
46            let date = byteorder::LE::read_u32(&buffer[0..4]);
47            let open = byteorder::LE::read_u32(&buffer[4..8]) as f64 / 100f64;
48            let high = byteorder::LE::read_u32(&buffer[8..12]) as f64 / 100f64;
49            let low = byteorder::LE::read_u32(&buffer[12..16]) as f64 / 100f64;
50            let close = byteorder::LE::read_u32(&buffer[16..20]) as f64 / 100f64;
51            // last 12 bytes consists of 4 bytes of turnover, 4 bytes of volume, and last 4 bytes reserved
52            // those bytes are not needed now, so we drop these bytes
53            let daily_data = DailyData::new(date, open, high, low, close);
54            day_line.push(daily_data);
55        }
56
57        DayLine::new(day_line)
58    }
59}
60
61#[test]
62fn builder_test() -> anyhow::Result<()> {
63    let builder = DayLineBuilder::from_path("../data/shlday/sh600000.day")?.query_days(400);
64    let day_line = builder.build();
65    dbg!(day_line.data.len());
66    Ok(())
67}