use crate::tcp::Tdx;
#[derive(Debug, Clone)]
pub struct IndexKline<'d> {
pub send: Box<[u8]>,
pub market: u16,
pub code: &'d str,
pub category: u16,
pub start: u16,
pub count: u16,
pub response: Vec<u8>,
pub data: Vec<IndexKlineData<'d>>,
}
impl<'d> IndexKline<'d> {
pub fn new(market: u16, code: &'d str, category: u16, start: u16, count: u16) -> Self {
assert_eq!(code.len(), 6, "股票代码必须是6位");
let mut send = [0u8; Self::LEN];
send.copy_from_slice(Self::SEND);
send[12..14].copy_from_slice(&market.to_le_bytes());
send[14..20].copy_from_slice(code.as_bytes());
send[20..22].copy_from_slice(&category.to_le_bytes());
send[22..24].copy_from_slice(&1u16.to_le_bytes());
send[24..26].copy_from_slice(&start.to_le_bytes());
send[26..28].copy_from_slice(&count.to_le_bytes());
Self {
send: send.into(),
market,
code,
category,
start,
count,
response: Vec::new(),
data: vec![IndexKlineData::default(); count as usize],
}
}
}
impl<'a> Tdx for IndexKline<'a> {
type Item = [IndexKlineData<'a>];
const SEND: &'static [u8] = &[
0x0c, 0x01, 0x08, 0x64, 0x01, 0x01, 0x1c, 0x00, 0x1c, 0x00, 0x2d, 0x05, 0x00, 0x00, 0x30,
0x30, 0x30, 0x30, 0x30, 0x31, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
const TAG: &'static str = "指数K线";
fn send(&mut self) -> &[u8] {
&self.send
}
fn parse(&mut self, v: Vec<u8>) {
use crate::bytes_helper::{u16_from_le_bytes, u32_from_le_bytes};
use crate::tcp::helper::{datetime, price, vol_amount};
if v.len() < 2 {
self.response = v;
return;
}
let (count, mut pos, mut base) = (u16_from_le_bytes(&v, 0), 2usize, 0i32);
let n = (count as usize).min(self.data.len());
for item in self.data.iter_mut().take(n) {
if v.len() - pos < 4 + 4 + 8 + 4 {
break;
}
let dt = datetime(&v[pos..pos + 4], self.category);
pos += 4;
let open = price(&v, &mut pos);
let close = price(&v, &mut pos);
*item = IndexKlineData {
dt,
code: self.code,
open: {
base += open;
base as f64 / 1000.
},
close: real_price(close, base),
high: real_price(price(&v, &mut pos), base),
low: real_price(price(&v, &mut pos), base),
vol: {
pos += 4;
vol_amount(u32_from_le_bytes(&v, pos - 4) as i32)
},
amount: {
pos += 4;
vol_amount(u32_from_le_bytes(&v, pos - 4) as i32)
},
up_count: u16_from_le_bytes(&v, pos),
down_count: u16_from_le_bytes(&v, pos + 2),
};
pos += 4;
base += close;
}
self.response = v;
}
fn result(&self) -> &Self::Item {
&self.data
}
}
#[inline]
fn real_price(p: i32, base: i32) -> f64 {
(p + base) as f64 / 1000.
}
#[derive(Debug, Default, Clone, serde::Serialize)]
pub struct IndexKlineData<'d> {
pub dt: crate::tcp::helper::DateTime,
pub code: &'d str,
pub open: f64,
pub close: f64,
pub high: f64,
pub low: f64,
pub vol: f64,
pub amount: f64,
pub up_count: u16,
pub down_count: u16,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_send_bytes() {
let kline = IndexKline::new(1, "000001", 9, 0, 5);
assert_eq!(kline.send.len(), 38);
assert_eq!(kline.market, 1);
assert_eq!(&kline.send[12..14], &[1, 0]);
assert_eq!(&kline.send[14..20], b"000001");
assert_eq!(&kline.send[20..22], &[9, 0]); assert_eq!(&kline.send[24..26], &[0, 0]); assert_eq!(&kline.send[26..28], &[5, 0]); }
#[test]
#[should_panic(expected = "股票代码必须是6位")]
fn test_invalid_code() {
IndexKline::new(1, "00000", 9, 0, 5);
}
#[test]
fn parse_truncated_no_panic() {
let v = vec![0x02, 0x00, 0x3f, 0xb3, 0x00, 0x00];
let mut kline = IndexKline::new(1, "000001", 9, 0, 2);
kline.parse(v); assert_eq!(kline.response.len(), 6);
}
}