use crate::{
errors::MarketDataError,
models::futopt::{ContractType, FutOptTicker, FutOptType},
rest::client::RestClient,
};
pub struct TickersRequestBuilder<'a> {
client: &'a RestClient,
typ: Option<FutOptType>,
exchange: Option<String>,
session: Option<String>,
contract_type: Option<ContractType>,
}
impl<'a> TickersRequestBuilder<'a> {
pub(crate) fn new(client: &'a RestClient) -> Self {
Self {
client,
typ: None,
exchange: None,
session: None,
contract_type: None,
}
}
pub fn typ(mut self, typ: FutOptType) -> Self {
self.typ = Some(typ);
self
}
pub fn exchange(mut self, exchange: &str) -> Self {
self.exchange = Some(exchange.to_string());
self
}
pub fn after_hours(mut self) -> Self {
self.session = Some("afterhours".to_string());
self
}
pub fn contract_type(mut self, contract_type: ContractType) -> Self {
self.contract_type = Some(contract_type);
self
}
pub fn send(self) -> Result<Vec<FutOptTicker>, MarketDataError> {
let typ = self.typ.ok_or_else(|| MarketDataError::ConfigError(
"type parameter is required for tickers endpoint".to_string(),
))?;
let mut query_params = Vec::new();
query_params.push(format!("type={}", typ.as_str()));
if let Some(exchange) = &self.exchange {
query_params.push(format!("exchange={}", exchange));
}
if let Some(session) = &self.session {
query_params.push(format!("session={}", session));
}
if let Some(contract_type) = &self.contract_type {
query_params.push(format!("contractType={}", contract_type.as_code()));
}
let url = format!(
"{}/futopt/intraday/tickers?{}",
self.client.get_base_url(),
query_params.join("&")
);
let request = self.client.agent().get(&url);
let request = self.client.auth().apply_to_request(request);
let response = self.client.execute(request)?;
let tickers: Vec<FutOptTicker> = response
.into_json()
.map_err(|e| MarketDataError::Other(e.into()))?;
Ok(tickers)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rest::Auth;
#[test]
fn test_tickers_builder_requires_type() {
let client = RestClient::new(Auth::SdkToken("test".to_string()));
let builder = TickersRequestBuilder::new(&client);
let result = builder.send();
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), MarketDataError::ConfigError(_)));
}
#[test]
fn test_tickers_builder_with_type() {
let client = RestClient::new(Auth::SdkToken("test".to_string()));
let builder = TickersRequestBuilder::new(&client).typ(FutOptType::Future);
assert_eq!(builder.typ, Some(FutOptType::Future));
}
#[test]
fn test_tickers_builder_full_params() {
let client = RestClient::new(Auth::SdkToken("test".to_string()));
let builder = TickersRequestBuilder::new(&client)
.typ(FutOptType::Option)
.exchange("TAIFEX")
.after_hours()
.contract_type(ContractType::Index);
assert_eq!(builder.typ, Some(FutOptType::Option));
assert_eq!(builder.exchange, Some("TAIFEX".to_string()));
assert_eq!(builder.session, Some("afterhours".to_string()));
assert_eq!(builder.contract_type, Some(ContractType::Index));
}
#[test]
fn test_tickers_builder_chaining() {
let client = RestClient::new(Auth::SdkToken("test".to_string()));
let builder = TickersRequestBuilder::new(&client)
.typ(FutOptType::Future)
.exchange("TAIFEX")
.contract_type(ContractType::Stock);
assert!(builder.typ.is_some());
assert!(builder.exchange.is_some());
assert!(builder.contract_type.is_some());
}
}