use super::{
BlockRecord, CompanyInfoCategory, CompanyInfoCategoryItem, CompanyInfoContent, FinanceInfoData,
HistoryMinuteTime, HistoryTransaction, IndexKline, Kline, MinuteTimeData, QuoteData,
SecurityListData, TransactionData, Xdxr,
};
use crate::tcp::helper::DateTime;
use crate::tcp::{Tcp, TcpConfig, Tdx};
#[derive(Debug)]
pub struct Client {
pub tcp: Tcp,
}
impl Client {
pub fn new() -> std::io::Result<Self> {
Ok(Self { tcp: Tcp::new()? })
}
pub fn with_config(config: &TcpConfig) -> std::io::Result<Self> {
Ok(Self {
tcp: Tcp::with_config(config)?,
})
}
pub fn reconnect(&mut self) -> std::io::Result<()> {
self.tcp.reconnect()
}
pub fn heartbeat(&mut self) -> std::io::Result<u16> {
self.tcp.heartbeat()
}
pub fn retry<T>(
&mut self,
f: impl FnMut(&mut Tcp) -> std::io::Result<T>,
attempts: usize,
) -> std::io::Result<T> {
self.tcp.retry(f, attempts)
}
pub fn quotes(&mut self, stocks: &[(u16, &str)]) -> std::io::Result<Vec<QuoteData>> {
let mut quotes = SecurityQuotesRef::new(stocks.to_vec());
quotes.recv_parsed(&mut self.tcp)?;
Ok(quotes.result().to_vec())
}
pub fn bars<'a>(
&mut self,
market: u16,
code: &'a str,
category: u16,
start: u16,
count: u16,
) -> std::io::Result<Vec<super::KlineData<'a>>> {
let mut kline = Kline::new(market, code, category, start, count);
kline.recv_parsed(&mut self.tcp)?;
Ok(kline.result().to_vec())
}
pub fn index_bars<'a>(
&mut self,
market: u16,
code: &'a str,
category: u16,
start: u16,
count: u16,
) -> std::io::Result<Vec<super::IndexKlineData<'a>>> {
let mut kline = IndexKline::new(market, code, category, start, count);
kline.recv_parsed(&mut self.tcp)?;
Ok(kline.result().to_vec())
}
pub fn k<'a>(
&mut self,
market: u16,
code: &'a str,
begin: Option<u32>,
end: Option<u32>,
) -> std::io::Result<Vec<super::KlineData<'a>>> {
const PAGE: u16 = 800;
const CATEGORY_DAY: u16 = 9;
let mut all = Vec::new();
let mut start = 0u16;
loop {
let bars = self.bars(market, code, CATEGORY_DAY, start, PAGE)?;
let n = bars.len();
all.extend(bars);
if n < PAGE as usize {
break; }
if let Some(begin) = begin {
if let Some(oldest) = all
.iter()
.map(|b| DateTime::to_u32(b.dt.clone()))
.min()
&& oldest < begin
{
break;
}
}
start = start.saturating_add(PAGE);
if start == 0 {
break; }
}
all.retain(|bar| {
let d = DateTime::to_u32(bar.dt.clone());
begin.is_none_or(|b| d >= b) && end.is_none_or(|e| d <= e)
});
all.sort_by_key(|bar| DateTime::to_u32(bar.dt.clone()));
Ok(all)
}
pub fn minute(&mut self, market: u16, code: &str) -> std::io::Result<Vec<MinuteTimeData>> {
let mut mt = super::MinuteTime::new(market, code);
mt.recv_parsed(&mut self.tcp)?;
Ok(mt.result().to_vec())
}
pub fn history_minute(
&mut self,
market: u16,
code: &str,
date: u32,
) -> std::io::Result<Vec<MinuteTimeData>> {
let mut mt = HistoryMinuteTime::new(market, code, date);
mt.recv_parsed(&mut self.tcp)?;
Ok(mt.result().to_vec())
}
pub fn transaction(
&mut self,
market: u16,
code: &str,
start: u16,
count: u16,
) -> std::io::Result<Vec<TransactionData>> {
let mut tx = super::Transaction::new(market, code, start, count);
tx.recv_parsed(&mut self.tcp)?;
Ok(tx.result().to_vec())
}
pub fn history_transaction(
&mut self,
market: u16,
code: &str,
start: u16,
count: u16,
date: u32,
) -> std::io::Result<Vec<TransactionData>> {
let mut tx = HistoryTransaction::new(market, code, start, count, date);
tx.recv_parsed(&mut self.tcp)?;
Ok(tx.result().to_vec())
}
pub fn finance(&mut self, market: u16, code: &str) -> std::io::Result<FinanceInfoData> {
let mut fin = super::FinanceInfo::new(market as u8, code);
fin.recv_parsed(&mut self.tcp)?;
Ok(fin.result().first().cloned().unwrap_or_default())
}
pub fn xdxr(&mut self, market: u16, code: &str) -> std::io::Result<Vec<super::XdxrData>> {
let mut xdxr = Xdxr::new(market, code);
xdxr.recv_parsed(&mut self.tcp)?;
Ok(xdxr.result().to_vec())
}
pub fn block(&mut self, block_file: &str) -> crate::Result<Vec<BlockRecord>> {
super::get_block_info(&mut self.tcp, block_file)
}
pub fn stock_count(&mut self, market: u16) -> std::io::Result<u16> {
let mut sc = super::super::basic::SecurityCount::new(market);
Ok(*sc.recv_parsed(&mut self.tcp)?)
}
pub fn stocks(&mut self, market: u16) -> crate::Result<Vec<SecurityListData>> {
super::stocks(&mut self.tcp, market)
}
pub fn f10(&mut self, market: u16, code: &str) -> std::io::Result<Vec<(String, String)>> {
let mut cat = CompanyInfoCategory::new(market, code);
cat.recv_parsed(&mut self.tcp)?;
let mut result = Vec::with_capacity(cat.result().len());
for item in cat.result() {
let mut content =
CompanyInfoContent::new(market, code, &item.filename, item.start, item.length);
content.recv_parsed(&mut self.tcp)?;
result.push((item.name.clone(), std::mem::take(&mut content.data)));
}
Ok(result)
}
pub fn f10_categories(
&mut self,
market: u16,
code: &str,
) -> std::io::Result<Vec<CompanyInfoCategoryItem>> {
let mut cat = CompanyInfoCategory::new(market, code);
cat.recv_parsed(&mut self.tcp)?;
Ok(cat.result().to_vec())
}
}
use super::SecurityQuotes as SecurityQuotesRef;
#[cfg(test)]
mod tests {
#[test]
fn k_range_retain_logic() {
use crate::tcp::helper::DateTime;
let d = DateTime {
year: 2026,
month: 9,
day: 1,
hour: 15,
minute: 0,
};
assert_eq!(d.to_u32(), 20260901);
}
}