fred_rs/series/observation.rs
1//! Get the observations or data values for an economic data series
2//!
3//! [https://research.stlouisfed.org/docs/api/fred/series_observations.html](https://research.stlouisfed.org/docs/api/fred/series_observations.html)
4//!
5//! ```
6//! use fred_rs::client::FredClient;
7//! use fred_rs::series::observation::{Builder, Units, Frequency, Response};
8//!
9//! // Create the client object
10//! let mut c = match FredClient::new() {
11//! Ok(c) => c,
12//! Err(msg) => {
13//! println!("{}", msg);
14//! return
15//! },
16//! };
17//!
18//! // Create the argument builder
19//! let mut builder = Builder::new();
20//!
21//! // Set the arguments for the builder
22//! builder
23//! .observation_start("2000-01-01")
24//! .units(Units::PCH)
25//! .frequency(Frequency::M);
26//!
27//! // Make the request and pass in the builder to apply the arguments
28//! let resp: Response = match c.series_observation("GNPCA", Some(builder)) {
29//! Ok(resp) => resp,
30//! Err(msg) => {
31//! println!("{}", msg);
32//! return
33//! },
34//! };
35//! ```
36
37use serde::Deserialize;
38use std::fmt::{self, Display, Formatter};
39
40#[derive(Deserialize, Clone, Debug, Default)]
41/// Response data structure for the fred/series/observation endpoint
42///
43/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html](https://research.stlouisfed.org/docs/api/fred/series_observations.html)
44pub struct Response {
45 /// The realtime start of the request
46 pub realtime_start: String,
47 /// The realtiem end of the request
48 pub realtime_end: String,
49 /// The start of the observation period
50 pub observation_start: String,
51 /// The end of the observation period
52 pub observation_end: String,
53 /// The units of the observation (e.g. Billions of Chained 2009 Dollars)
54 pub units: String,
55 /// The output type [Link](enum.OutputType.html)
56 pub output_type: usize,
57 /// The file type (will always be JSON for fred-rs)
58 pub file_type: String,
59 /// On what metric the data are order
60 pub order_by: String,
61 /// Ascending (asc) of descending (desc)
62 pub sort_order: String,
63 /// The number of data items returned
64 pub count: usize,
65 /// The first result returned
66 pub offset: usize,
67 /// The maximum number of results requested
68 pub limit: usize,
69 /// The data values returned
70 pub observations: Vec<DataPoint>,
71}
72
73impl Display for Response {
74 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
75 for item in self.observations.iter() {
76 match item.fmt(f) {
77 Ok(_) => (),
78 Err(e) => return Err(e),
79 }
80 match writeln!(f, "") {
81 Ok(_) => (),
82 Err(e) => return Err(e),
83 }
84 }
85 Ok(())
86 }
87}
88
89#[derive(Deserialize, Clone, Debug, Default)]
90/// A single observation datapoint
91///
92/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html](https://research.stlouisfed.org/docs/api/fred/series_observations.html)
93pub struct DataPoint {
94 pub realtime_start: String,
95 pub realtime_end: String,
96 /// Date of the data point
97 pub date: String,
98 /// String encoded data point
99 pub value: String,
100}
101
102impl Display for DataPoint {
103 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
104 write!(f, "({}: {})", self.date, self.value)
105 }
106}
107
108/// Sort order options for the fred/series/observation endpoint
109///
110/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#sort_order](https://research.stlouisfed.org/docs/api/fred/series_observations.html#sort_order)
111pub enum SortOrder {
112 /// Dates returned in ascending order (default)
113 Ascending,
114 /// Dates returned in descending order
115 Descending,
116}
117
118/// Data transformation options for the fred/series/observation endpoint
119///
120/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#units](https://research.stlouisfed.org/docs/api/fred/series_observations.html#units)
121pub enum Units {
122 /// Linear: no transform applied (default)
123 LIN,
124 /// Change: returns the period over period change of the observation
125 CHG,
126 /// 1 Year Change: Returns the YoY change of the observation
127 CH1,
128 /// Percent Change: Returns the period over period percent change of the observation
129 PCH,
130 /// 1 Year Percent Change: Returns the YoY percent change of the observation
131 PC1,
132 /// Compounded Annual Rate of Change
133 PCA,
134 /// Continuously Compounded Rate of Change
135 CCH,
136 /// Continuously Compounded Annual Rate of Change
137 CCA,
138 /// Natual Log: Returns the natural logarithm of the observation
139 LOG,
140}
141
142/// Options for data series frequency
143///
144/// The frequency cannot exceed the native frequency of the data series.
145///
146/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#frequency](https://research.stlouisfed.org/docs/api/fred/series_observations.html#frequency)
147pub enum Frequency {
148 /// Daily (fastest)
149 D,
150 /// Weekly
151 W,
152 /// Bi-Weekly
153 BW,
154 /// Monthly
155 M,
156 /// Quarterly
157 Q,
158 /// Semi-Annualy
159 SA,
160 /// Annual (slowest)
161 A,
162 /// Weekly, Ending Friday
163 WEF,
164 /// Weekly, Ending Thursday
165 WETH,
166 /// Weekly, Ending Wednesday
167 WEW,
168 /// Weekly, Ending Tuesday
169 WETU,
170 /// Weekly, Ending Monday
171 WEM,
172 /// Weekly, Ending Sunday
173 WESU,
174 /// Weekly, Ending Saturday
175 WESA,
176 /// Bi-Weekly, Ending Wednesday
177 BWEW,
178 /// Bi-Weekly, Ending Monday
179 BWEM,
180}
181
182/// Provides an aggregation method for frequency aggregation
183///
184/// This argument should be used in conjunction with the frequency argument if the default aggregation method (AVG) is not preferred.
185///
186/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#aggregation_method](https://research.stlouisfed.org/docs/api/fred/series_observations.html#aggregation_method)
187pub enum AggregationMethod {
188 /// Average (default): intermediate datapoints are averaged to produce the aggregate
189 AVG,
190 /// Sum: intermediate datapoints are summed to produce the aggregate
191 SUM,
192 /// End of Period: The final result in the period is returned
193 EOP
194}
195
196/// Specifies the data output type
197///
198/// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#output_type](https://research.stlouisfed.org/docs/api/fred/series_observations.html#output_type)
199pub enum OutputType {
200 /// Observations by Real Time Period
201 RT,
202 /// Observations by Vintage Date, All Observations
203 VDALL,
204 /// Observations by Vintage Date, New and Revised Observations Only
205 VDNEW,
206 /// Observations, Initial Release Only
207 INITIAL
208}
209
210/// Argument builder for the fred/series/observation endpoint.
211///
212/// Each method adds an argument to the builder which can then be passed to the client used to fetch the data to apply the arguments.
213pub struct Builder {
214 option_string: String,
215 vintage_dates: String,
216}
217
218
219impl Builder {
220 /// Initializes a new observation::Builder that can be used to add commands to an API request
221 ///
222 /// The builder does not do validity checking of the arguments nor does it check for duplicates.
223 ///
224 /// ```
225 /// use fred_rs::series::observation::{Builder, Units, SortOrder};
226 /// // Create a new builder
227 /// let mut builder = Builder::new();
228 /// // add arguments to the builder
229 /// builder
230 /// .limit(100)
231 /// .units(Units::LOG)
232 /// .sort_order(SortOrder::Descending);
233 /// ```
234 pub fn new() -> Builder {
235 Builder {
236 option_string: String::new(),
237 vintage_dates: String::new(),
238 }
239 }
240
241 /// Returns the current arguments as a URL formatted string
242 pub(crate) fn build(mut self) -> String {
243 if self.vintage_dates.len() > 0 {
244 self.option_string += format!("&vintage_dates={}", self.vintage_dates).as_str()
245 }
246
247 self.option_string
248 }
249
250 /// Adds a realtime_start argument to the builder
251 ///
252 /// # Arguments
253 /// * `start_date` - date formatted as YYYY-MM-DD
254 ///
255 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#realtime_start](https://research.stlouisfed.org/docs/api/fred/series_observations.html#realtime_start)
256 pub fn realtime_start(&mut self, start_date: &str) -> &mut Builder {
257 self.option_string += format!("&realtime_start={}", start_date).as_str();
258 self
259 }
260
261 /// Adds a realtime_end argument to the builder
262 ///
263 /// # Arguments
264 /// * `end_date` - date formatted as YYYY-MM-DD
265 ///
266 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#realtime_end](https://research.stlouisfed.org/docs/api/fred/series_observations.html#realtime_end)
267 pub fn realtime_end(&mut self, end_date: &str) -> &mut Builder {
268 self.option_string += format!("&realtime_end={}", end_date).as_str();
269 self
270 }
271
272 /// Adds a limit argument to the builder
273 ///
274 /// The limit argument specifies a maximum number of observations to return.
275 ///
276 /// # Arguments
277 /// * `num_points` - Maximum number of data points to return
278 ///
279 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#limit](https://research.stlouisfed.org/docs/api/fred/series_observations.html#limit)
280 pub fn limit(&mut self, num_points: usize) -> &mut Builder {
281 let num_points = if num_points > 1000000 { // max value is 1000
282 1000000
283 } else {
284 num_points
285 };
286 self.option_string += format!("&limit={}", num_points).as_str();
287 self
288 }
289
290 /// Adds an offset argument to the builder
291 ///
292 /// Adding an offset shifts the starting result number. For example, if limit is 5 and offset is 0 then results 1-5 will be returned, but if offset was 5 then results 6-10 would be returned.
293 ///
294 /// # Arguments
295 /// * `ofs` - the offset amount
296 ///
297 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#offset](https://research.stlouisfed.org/docs/api/fred/series_observations.html#offset)
298 pub fn offset(&mut self, ofs: usize) -> &mut Builder {
299 self.option_string += format!("&offset={}", ofs).as_str();
300 self
301 }
302
303 /// Change the sort order of the data
304 ///
305 /// # Arguments
306 /// * `order` - Data sort order enum
307 ///
308 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#sort_order](https://research.stlouisfed.org/docs/api/fred/series_observations.html#sort_order)
309 pub fn sort_order(&mut self, order: SortOrder) -> &mut Builder {
310 match order {
311 SortOrder::Descending => {
312 self.option_string += format!("&sort_order=desc").as_str()
313 },
314 _ => () // Ascending is the default so do nothing
315 }
316 self
317 }
318
319 /// Set the start year for data points
320 ///
321 /// # Arguments
322 /// * `start_date` - date formatted as YYYY-MM-DD
323 ///
324 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#observation_start](https://research.stlouisfed.org/docs/api/fred/series_observations.html#observation_start)
325 pub fn observation_start(&mut self, start_date: &str) -> &mut Builder {
326 self.option_string += format!("&observation_start={}", start_date).as_str();
327 self
328 }
329
330 /// Set the end year for data points
331 ///
332 /// # Arguments
333 /// * `end_date` - date formatted as YYYY-MM-DD
334 ///
335 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#observation_end](https://research.stlouisfed.org/docs/api/fred/series_observations.html#observation_end)
336 pub fn observation_end(&mut self, end_date: &str) -> &mut Builder {
337 self.option_string += format!("&observation_end={}", end_date).as_str();
338 self
339 }
340
341 /// Set the units of the data series
342 ///
343 /// # Arguments
344 /// * `units` - Data units to apply to the data set (see ObservationUnits)
345 ///
346 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#units](https://research.stlouisfed.org/docs/api/fred/series_observations.html#units)
347 pub fn units(&mut self, units: Units) -> &mut Builder {
348 match units {
349 Units::CHG => {
350 self.option_string += format!("&units=chg").as_str()
351 },
352 Units::CH1 => {
353 self.option_string += format!("&units=ch1").as_str()
354 },
355 Units::PCH => {
356 self.option_string += format!("&units=pch").as_str()
357 },
358 Units::PC1 => {
359 self.option_string += format!("&units=pc1").as_str()
360 },
361 Units::PCA => {
362 self.option_string += format!("&units=pca").as_str()
363 },
364 Units::CCH => {
365 self.option_string += format!("&units=cch").as_str()
366 },
367 Units::CCA => {
368 self.option_string += format!("&units=cca").as_str()
369 },
370 Units::LOG => {
371 self.option_string += format!("&units=log").as_str()
372 },
373 _ => (), // lin is the default
374 }
375 self
376 }
377
378 /// Set the frequency of the data series
379 ///
380 /// The requested frequency must be less than or equal to the native frequency for the data set.
381 ///
382 /// # Arguments
383 /// * `freq` - Frequency of data observations to return
384 ///
385 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#frequency](https://research.stlouisfed.org/docs/api/fred/series_observations.html#frequency)
386 pub fn frequency(&mut self, freq: Frequency) -> &mut Builder {
387 match freq {
388 Frequency::D => {
389 self.option_string += format!("&frequency=d").as_str()
390 },
391 Frequency::W => {
392 self.option_string += format!("&frequency=w").as_str()
393 },
394 Frequency::BW => {
395 self.option_string += format!("&frequency=bw").as_str()
396 },
397 Frequency::M => {
398 self.option_string += format!("&frequency=m").as_str()
399 },
400 Frequency::Q => {
401 self.option_string += format!("&frequency=q").as_str()
402 },
403 Frequency::SA => {
404 self.option_string += format!("&frequency=sa").as_str()
405 },
406 Frequency::A => {
407 self.option_string += format!("&frequency=a").as_str()
408 },
409 Frequency::WEF => {
410 self.option_string += format!("&frequency=wef").as_str()
411 },
412 Frequency::WETH => {
413 self.option_string += format!("&frequency=weth").as_str()
414 },
415 Frequency::WEW => {
416 self.option_string += format!("&frequency=wew").as_str()
417 },
418 Frequency::WETU => {
419 self.option_string += format!("&frequency=d").as_str()
420 },
421 Frequency::WEM => {
422 self.option_string += format!("&frequency=wem").as_str()
423 },
424 Frequency::WESU => {
425 self.option_string += format!("&frequency=wesu").as_str()
426 },
427 Frequency::WESA => {
428 self.option_string += format!("&frequency=wesa").as_str()
429 },
430 Frequency::BWEW => {
431 self.option_string += format!("&frequency=bwew").as_str()
432 },
433 Frequency::BWEM => {
434 self.option_string += format!("&frequency=bwem").as_str()
435 },
436 }
437 self
438 }
439
440 /// Set the aggregation method of the data series
441 ///
442 /// # Arguments
443 /// * `method` - See `ObservationAggregationMethod`
444 ///
445 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#aggregation_method](https://research.stlouisfed.org/docs/api/fred/series_observations.html#aggregation_method)
446 pub fn aggregation_method(&mut self, method: AggregationMethod) -> &mut Builder {
447 match method {
448 AggregationMethod::SUM => {
449 self.option_string += format!("&aggregation_method=sum").as_str()
450 },
451 AggregationMethod::EOP => {
452 self.option_string += format!("&aggregation_method=eop").as_str()
453 },
454 _ => () // AVG is the default so do nothing
455 }
456 self
457 }
458
459 /// Set the datapoint output type
460 ///
461 /// # Arguments
462 /// * `otype` - [OutputType](enum.OutputType.hmtl)
463 ///
464 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#output_type](https://research.stlouisfed.org/docs/api/fred/series_observations.html#output_type)
465 pub fn output_type(&mut self, otype: OutputType) -> &mut Builder {
466 match otype {
467 OutputType::VDALL => {
468 self.option_string += format!("&output_type=2").as_str()
469 },
470 OutputType::VDNEW => {
471 self.option_string += format!("&output_type=3").as_str()
472 },
473 OutputType::INITIAL => {
474 self.option_string += format!("&output_type=4").as_str()
475 },
476 _ => () // AVG is the default so do nothing
477 }
478 self
479 }
480
481 /// Add a vintage date argument
482 ///
483 /// This is the only parameter that could be added mroe than once.
484 ///
485 /// The API accepts a comma separated list of vintage dates for which to return data.
486 ///
487 /// [https://research.stlouisfed.org/docs/api/fred/series_observations.html#vintage_dates](https://research.stlouisfed.org/docs/api/fred/series_observations.html#vintage_dates)
488 ///
489 /// # Arguments
490 /// * `date` - date formatted as YYYY-MM-DD
491 pub fn vintage_date(&mut self, date: &str) -> &mut Builder {
492 if self.vintage_dates.len() != 0 {
493 self.vintage_dates.push(',');
494 }
495 self.vintage_dates += date;
496 self
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503 use crate::client::FredClient;
504
505 #[test]
506 fn series_observation_with_options() {
507 let mut c = match FredClient::new() {
508 Ok(c) => c,
509 Err(msg) => {
510 println!("{}", msg);
511 assert_eq!(2, 1);
512 return
513 },
514 };
515
516 let mut builder = Builder::new();
517 builder
518 .limit(5)
519 .sort_order(SortOrder::Descending);
520
521 let resp: Response = match c.series_observation("GNPCA", Some(builder)) {
522 Ok(resp) => resp,
523 Err(msg) => {
524 println!("{}", msg);
525 assert_eq!(2, 1);
526 return
527 },
528 };
529
530 for item in resp.observations {
531 println!("{}: {}", item.date, item.value.parse::<f64>().unwrap());
532 }
533 //assert_eq!(resp.observations[0].value, String::from("1120.076"));
534 }
535}