finance_query/adapters/fmp/
advanced.rs1use serde::{Deserialize, Serialize};
4
5use crate::adapters::common::encode_path_segment;
6use crate::error::Result;
7
8use super::build_client;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12#[non_exhaustive]
13pub struct SicCode {
14 #[serde(rename = "sicCode")]
16 pub sic_code: Option<String>,
17 #[serde(rename = "industryTitle")]
19 pub industry_title: Option<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24#[non_exhaustive]
25pub struct SicEntry {
26 pub symbol: Option<String>,
28 pub name: Option<String>,
30 pub cik: Option<String>,
32 #[serde(rename = "sicCode")]
34 pub sic_code: Option<String>,
35 #[serde(rename = "industryTitle")]
37 pub industry_title: Option<String>,
38 #[serde(rename = "businessAddress")]
40 pub business_address: Option<String>,
41 #[serde(rename = "phoneNumber")]
43 pub phone_number: Option<String>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48#[non_exhaustive]
49pub struct CotSymbol {
50 #[serde(rename = "trading_symbol")]
52 pub trading_symbol: Option<String>,
53 #[serde(rename = "short_name")]
55 pub short_name: Option<String>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60#[non_exhaustive]
61pub struct CotReport {
62 pub date: Option<String>,
64 pub symbol: Option<String>,
66 #[serde(rename = "short_name")]
68 pub short_name: Option<String>,
69 #[serde(rename = "current_long_all")]
71 pub current_long_all: Option<f64>,
72 #[serde(rename = "current_short_all")]
74 pub current_short_all: Option<f64>,
75 #[serde(rename = "change_long_all")]
77 pub change_long_all: Option<f64>,
78 #[serde(rename = "change_short_all")]
80 pub change_short_all: Option<f64>,
81 #[serde(rename = "pct_of_oi_long_all")]
83 pub pct_of_oi_long_all: Option<f64>,
84 #[serde(rename = "pct_of_oi_short_all")]
86 pub pct_of_oi_short_all: Option<f64>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91#[non_exhaustive]
92pub struct CotAnalysis {
93 pub date: Option<String>,
95 pub symbol: Option<String>,
97 #[serde(rename = "short_name")]
99 pub short_name: Option<String>,
100 pub sector: Option<String>,
102 #[serde(rename = "currentNetPosition")]
104 pub current_net_position: Option<f64>,
105 #[serde(rename = "previousNetPosition")]
107 pub previous_net_position: Option<f64>,
108 #[serde(rename = "changeInNetPosition")]
110 pub change_in_net_position: Option<f64>,
111 #[serde(rename = "marketSentiment")]
113 pub market_sentiment: Option<String>,
114 #[serde(rename = "reversalTrend")]
116 pub reversal_trend: Option<String>,
117}
118
119pub async fn sic_codes() -> Result<Vec<SicCode>> {
121 let client = build_client()?;
122 client
123 .get("/api/v4/standard_industrial_classification_list", &[])
124 .await
125}
126
127pub async fn sic_by_code(code: &str) -> Result<Vec<SicEntry>> {
131 let client = build_client()?;
132 client
133 .get(
134 "/api/v4/standard_industrial_classification",
135 &[("sicCode", code)],
136 )
137 .await
138}
139
140pub async fn cot_symbols() -> Result<Vec<CotSymbol>> {
142 let client = build_client()?;
143 client
144 .get("/api/v4/commitment_of_traders_report/list", &[])
145 .await
146}
147
148pub async fn cot_report(symbol: &str) -> Result<Vec<CotReport>> {
152 let client = build_client()?;
153 let path = format!(
154 "/api/v4/commitment_of_traders_report/{}",
155 encode_path_segment(symbol)
156 );
157 client.get(&path, &[]).await
158}
159
160pub async fn cot_analysis(symbol: &str) -> Result<Vec<CotAnalysis>> {
164 let client = build_client()?;
165 let path = format!(
166 "/api/v4/commitment_of_traders_report_analysis/{}",
167 encode_path_segment(symbol)
168 );
169 client.get(&path, &[]).await
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[tokio::test]
177 async fn test_sic_codes_mock() {
178 let mut server = mockito::Server::new_async().await;
179 let _mock = server
180 .mock("GET", "/api/v4/standard_industrial_classification_list")
181 .match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
182 "apikey".into(),
183 "test-key".into(),
184 )]))
185 .with_status(200)
186 .with_body(
187 serde_json::json!([
188 {
189 "sicCode": "3674",
190 "industryTitle": "Semiconductors and Related Devices"
191 }
192 ])
193 .to_string(),
194 )
195 .create_async()
196 .await;
197
198 let client = super::super::build_test_client(&server.url()).unwrap();
199 let result: Vec<SicCode> = client
200 .get("/api/v4/standard_industrial_classification_list", &[])
201 .await
202 .unwrap();
203 assert_eq!(result.len(), 1);
204 assert_eq!(result[0].sic_code.as_deref(), Some("3674"));
205 assert_eq!(
206 result[0].industry_title.as_deref(),
207 Some("Semiconductors and Related Devices")
208 );
209 }
210}