Skip to main content

tradingview/study/
request.rs

1//! Study request builder and configuration.
2
3use std::time::Duration;
4
5use bon::Builder;
6
7use crate::Result;
8use crate::chart::study::StudyConfiguration;
9use crate::error::TradingViewError;
10use crate::{Interval, MarketSymbol, Ticker};
11
12/// Immutable request object for a study or fundamental data fetch.
13#[derive(Debug, Clone, Builder)]
14#[builder(on(String, into))]
15pub struct StudyRequest {
16    /// Optional ticker specifying symbol and exchange.
17    pub ticker: Option<Ticker>,
18    /// Optional symbol string (e.g., "AAPL").
19    pub symbol: Option<String>,
20    /// Optional exchange string (e.g., "NASDAQ").
21    pub exchange: Option<String>,
22    /// Chart interval / resolution for the study.
23    #[builder(default = Interval::OneDay)]
24    pub interval: Interval,
25    /// Base bar count requested for the primary series (default 100).
26    #[builder(default = 100)]
27    pub base_bar_count: u64,
28    /// The study configuration (Builtin or Pine Script).
29    #[builder(into)]
30    pub study: StudyConfiguration,
31    /// Optional custom study ID. If not provided, a random unique ID is generated.
32    pub study_id: Option<String>,
33    /// Request timeout (default 30 seconds).
34    #[builder(default = Duration::from_secs(30))]
35    pub timeout: Duration,
36}
37
38impl StudyRequest {
39    /// Resolves the symbol and exchange from ticker or explicit fields.
40    pub fn resolve_symbol_exchange(&self) -> Result<(String, String)> {
41        if let Some(ticker) = &self.ticker {
42            return Ok((ticker.symbol().to_string(), ticker.exchange().to_string()));
43        }
44        match (&self.symbol, &self.exchange) {
45            (Some(s), Some(e)) => Ok((s.clone(), e.clone())),
46            (None, _) => Err(TradingViewError::MissingSymbol.into()),
47            (_, None) => Err(TradingViewError::MissingExchange.into()),
48        }
49    }
50}