finance_solution/convert_rate.rs
1//! **Rate conversions**. Given a rate and number of compound periods per year, what is this rate
2//! when converted to APR, Effective annual, and Periodic rates? Also consider the [`apr`](./fn.apr.html) [`ear`](./fn.ear.html) and [`epr`](./fn.epr.html) helper functions.
3//!
4//! # Error handling (v0.1+)
5//!
6//! All public entry points return [`crate::FinanceResult`]. Invalid compounding period counts
7//! (`0`) and non-finite or out-of-domain rates yield [`crate::FinanceError`] — they do not panic.
8//!
9//! **APR**: **Annual Percentage Rate**, also written as Nominal Rate, or annual discount rate. An annualized represenation of the interest rate.
10//!
11//! ><small>For general use, try the [`apr`](./fn.apr.html) function by providing rate and compounding periods per year, for example `apr(0.034, 12).unwrap()`.</small>
12//!
13//! ><small>To _calculate_ the Annual Percentage Rate (APR) of a given rate, use the [`convert_ear_to_apr`](./fn.convert_ear_to_apr.html) or [`convert_epr_to_apr`](./fn.convert_epr_to_apr.html) functions.</small>
14//!
15//! ><small>To _convert_ an Annual Percentage Rate (APR) into a different rate, use the [`convert_apr_to_ear`](./fn.convert_apr_to_ear.html) or [`convert_apr_to_epr`](./fn.convert_apr_to_ear.html) functions.</small>
16//!
17//! **EPR**: **Effective Periodic Rate**, also written as **Periodic Rate**. The rate of the compounding period.
18//!
19//! ><small>For general use, try the [`epr`](./fn.epr.html) function by providing rate and compounds_per_year, for example `epr(0.034, 12).unwrap()`.</small>
20//!
21//! ><small>To <i>calculate</i> the Effective Periodic Rate (EPR) of a given rate use the [`convert_apr_to_epr`](./fn.convert_apr_to_epr.html) or [`convert_ear_to_epr`](./fn.convert_ear_to_epr.html) functions.</small>
22//!
23//! ><small>To _convert_ an Effective Period Rate (EPR) into a different rate, use the [`convert_epr_to_ear`](./fn.convert_epr_to_ear.html) or [`convert_epr_to_apr`](./fn.convert_epr_to_apr.html) functions.</small>
24//!
25//! **EAR**: **Effective Annual Rate**. The effective rate of a year which (typically) has multiple compounding periods within the year.
26//!
27//! ><small>For general use, try the [`ear`](./fn.ear.html) function by providing rate and compounding periods per year, for example `ear(0.034, 12).unwrap()`.</small>
28//!
29//! ><small>To _calculate_ the Effective Annual Rate (EAR) of a given rate use the [`convert_apr_to_ear`](./fn.convert_apr_to_ear.html) or [`convert_epr_to_ear`](./fn.convert_epr_to_ear.html) functions.</small>
30//!
31//! ><small>To _convert_ an Effective Annual Rate (EAR) into a different rate, use the [`convert_ear_to_apr`](./fn.convert_ear_to_apr.html) or [`convert_ear_to_epr`](./fn.convert_ear_to_epr.html) functions.</small>
32//!
33//! # Examples
34//! All functions in this module can be written with the suffix **_solution**, except for the [`apr`](./fn.apr.html), [`ear`](./fn.ear.html), [`epr`](./fn.epr.html) helper functions which already provide a solution struct.
35//! The solution functions provide helpful information in the `dbg!()` output, for example:
36//!
37//! ```
38//! use finance_solution::*;
39//! // Example 1: Give the apr function an apr and compounding-periods-per-year.
40//! let rate = apr(0.034, 12).unwrap();
41//! dbg!(rate);
42//! ```
43//! > prints to terminal:
44//! ```text
45//! {
46//! input_name: Apr
47//! input_rate: 0.034
48//! compounds_per_year: 12
49//! apr_in_percent: 3.4000%
50//! epr_in_percent: 0.2833%
51//! ear_in_percent: 3.4535%
52//! apr: 0.034
53//! epr: 0.0028333333333333335
54//! ear: 0.03453486936028982
55//! apr_formula:
56//! epr_formula: 0.034 / 12
57//! ear_formula: (1 + (0.034/12))^12 - 1
58//! }
59//! ```
60//! Example 2: explicit call to f64 function
61//! ```
62//! # use finance_solution::*;
63//! let apr = convert_apr_to_ear(0.034, 12).unwrap();
64//! dbg!(apr);
65//! ```
66//! > prints to terminal:
67//! ```text
68//! 0.03453486936028982
69//! ```
70//! Example 3: explicit call to a `_solution` function
71//! ```
72//! # use finance_solution::*;
73//! let apr = convert_rate::convert_apr_to_ear_solution(0.034, 12).unwrap(); // provides same output as apr! macro
74//! dbg!(apr.ear());
75//! ```
76//! > prints to terminal:
77//! ```text
78//! {
79//! input_name: Apr
80//! input_rate: 0.034
81//! compounds_per_year: 12
82//! apr_in_percent: 3.4000%
83//! epr_in_percent: 0.2833%
84//! ear_in_percent: 3.4535%
85//! apr: 0.034
86//! epr: 0.0028333333333333335
87//! ear: 0.03453486936028982
88//! apr_formula:
89//! epr_formula: 0.034 / 12
90//! ear_formula: (1 + (0.034/12))^12 - 1
91//! }
92//! ```
93//! Here are a few variations of how someone can use the `convert_rate` module functions:
94//! ```
95//! # use finance_solution::*;
96//! // What is the future value of $500 in 1 year
97//! // if the APR is 3.4% and it's compounded monthly?
98//! // Solve twice, first using EPR and then using EAR.
99//!
100//! // to solve, first convert the annual rate into a periodic rate (monthly):
101//! let epr = convert_rate::convert_apr_to_epr(0.034, 12).unwrap();
102//! assert_approx_equal!(epr, 0.002833333333333333);
103//!
104//! // then solve for future value:
105//! let answer_1 = future_value::future_value_solution(epr, 12, 500, false).unwrap();
106//! dbg!(&answer_1);
107//! ```
108//! > prints to terminal:
109//! ```text
110//! {
111//! calculated_field: FutureValue
112//! rate: 0.0028333333333333335
113//! periods: 12
114//! present_value: 500.0
115//! future_value: 517.2674346801452
116//! formula: "500.0000 * (1.002833 ^ 12)"
117//! }
118//! ```
119//! Now let's doublecheck the previous answer.
120//! ```
121//! # use finance_solution::*;
122//! // Double-check the previous answer_1 by solving the future_value
123//! // using 1 year as the period and the effective annual rate,
124//! // instead of using 12 monthly periods of the periodic rate.
125//! let rate = apr(0.034, 12).unwrap();
126//! let answer_2 = future_value::future_value_solution(rate.ear(), 1, 500, false).unwrap();
127//! dbg!(&answer_2.future_value()); // outputs: 517.2674346801449
128//! // assert_approx_equal!(answer_1.future_value, answer_2.future_value); // true
129//! ```
130//!
131//! Note: you might notice the last two decimal places are different:<br>
132//! > &answer1.future_value() = 517.26743468014**52**<br>
133//! > &answer2.future_value() = 517.26743468014**49**<br>
134//!
135//! This is not a mistake, this is a natural phenomenon of computer calculations to have slight inaccuracies in floating point number calculations. Both answers are technically correct.
136//! Users of the crate can use our [`round`](../round/index.html) module, or [`assert_approx_equal!`](../macro.assert_approx_equal.html) macro for working with floating point representations.
137//! Notice how we used [`assert_approx_equal!`](./macro.assert_approx_equal.html) in the example above to assert two slightly different numbers as being equal.
138//!
139//! Now you've learned Time-Value-of-Money problems can be
140//! solved using different rates and periods, while providing the same
141//! correct answer. And you've learned how to use this crate for rate conversions! 😊
142use log::warn;
143
144// Import needed for the function references in the Rustdoc comments.
145#[allow(unused_imports)]
146use crate::tvm_convert_rate::*;
147use crate::*;
148
149/// Validate rate + compounding periods for convert-rate helpers.
150fn check_inputs(rate: f64, periods: u32, fn_type: ConvertRateVariable) -> FinanceResult<()> {
151 if periods < 1 {
152 return Err(FinanceError::InvalidPeriod {
153 period: 0,
154 periods,
155 message: "compounding periods per year must be at least 1",
156 });
157 }
158 crate::util::error::require_finite("rate", rate)?;
159 // EAR (and continuous EAR) require rate > -1 so roots/logs stay real.
160 if (fn_type.is_ear() || fn_type.is_ear_continuous()) && rate <= -1.0 {
161 return Err(FinanceError::InvalidRate { rate });
162 }
163 // EPR used in (1+epr)^n also needs epr > -1 for real results when n is large.
164 if fn_type.is_epr() && rate <= -1.0 {
165 return Err(FinanceError::InvalidRate { rate });
166 }
167 if rate > 1.0 || rate < -1.0 {
168 warn!("You provided a rate of {}%. Are you sure?", rate * 100.0);
169 }
170 if periods > 366 {
171 warn!(
172 "You provided more than 366 compounding periods in a year (You provided {}). Are you sure?",
173 periods
174 );
175 }
176 Ok(())
177}
178
179/// Helper function to convert a quoted annual rate (APR) into all possible conversions (EAR, EPR).
180///
181/// # Errors
182/// Returns [`FinanceError`] if `compounding_periods_in_year < 1` or `rate` is non-finite.
183pub fn apr(apr: f64, compounding_periods_in_year: u32) -> FinanceResult<ConvertRateSolution> {
184 check_inputs(apr, compounding_periods_in_year, ConvertRateVariable::Apr)?;
185 let ear = (1_f64 + (apr / compounding_periods_in_year as f64))
186 .powf(compounding_periods_in_year as f64)
187 - 1_f64;
188 let epr = apr / compounding_periods_in_year as f64;
189 let apr_in_percent = format!("{:.4}%", apr * 100.);
190 let epr_in_percent = format!("{:.4}%", epr * 100.);
191 let ear_in_percent = format!("{:.4}%", ear * 100.);
192 let apr_formula = String::new();
193 let epr_formula = format!("{} / {}", apr, compounding_periods_in_year);
194 let ear_formula = format!(
195 "(1 + ({}/{}))^{} - 1",
196 apr, compounding_periods_in_year, compounding_periods_in_year
197 );
198 Ok(ConvertRateSolution::new(
199 ConvertRateVariable::Apr,
200 apr,
201 compounding_periods_in_year,
202 apr_in_percent,
203 epr_in_percent,
204 ear_in_percent,
205 apr,
206 epr,
207 ear,
208 &apr_formula,
209 &epr_formula,
210 &ear_formula,
211 ))
212}
213
214/// Helper function to convert an APR into an EAR using continuous compounding.
215///
216/// # Errors
217/// Returns [`FinanceError`] if `rate` is non-finite.
218pub fn apr_continuous(apr: f64) -> FinanceResult<ConvertRateSolution> {
219 let compounding_periods_in_year = 1; // not used
220 check_inputs(
221 apr,
222 compounding_periods_in_year,
223 ConvertRateVariable::AprContinuous,
224 )?;
225 // formula: e^apr - 1
226 let e: f64 = 2.71828182845904;
227 let ear: f64 = if apr < 0.0 {
228 (e.powf(apr.abs()) - 1_f64) * -1_f64
229 } else {
230 e.powf(apr) - 1_f64
231 };
232 let epr = 0.0; // epr cannot exist for infinite periods
233 let apr_in_percent = format!("{:.4}%", apr * 100.);
234 let epr_in_percent = "NaN".to_string();
235 let ear_in_percent = format!("{:.4}%", ear * 100.);
236 let apr_formula = String::new();
237 let epr_formula = String::new();
238 let ear_formula = format!("({}^{} - 1", e, apr);
239 Ok(ConvertRateSolution::new(
240 ConvertRateVariable::AprContinuous,
241 apr,
242 compounding_periods_in_year,
243 apr_in_percent,
244 epr_in_percent,
245 ear_in_percent,
246 apr,
247 epr,
248 ear,
249 &apr_formula,
250 &epr_formula,
251 &ear_formula,
252 ))
253}
254
255/// Helper function to convert an EAR into an APR using continuous compounding.
256///
257/// # Errors
258/// Returns [`FinanceError`] if `ear` is non-finite or `ear <= -1.0`.
259pub fn ear_continuous(ear: f64) -> FinanceResult<ConvertRateSolution> {
260 let compounding_periods_in_year = 1; // not used
261 check_inputs(
262 ear,
263 compounding_periods_in_year,
264 ConvertRateVariable::EarContinuous,
265 )?;
266 let apr: f64 = if ear < 0.0 {
267 (ear.abs() + 1_f64).ln() * -1_f64
268 } else {
269 (ear + 1_f64).ln()
270 };
271 let epr = 0.0;
272 let apr_in_percent = format!("{:.4}%", apr * 100.);
273 let epr_in_percent = "NaN".to_string();
274 let ear_in_percent = format!("{:.4}%", ear * 100.);
275 let apr_formula = format!("(ln({} + 1)", apr);
276 let epr_formula = String::new();
277 let ear_formula = String::new();
278 Ok(ConvertRateSolution::new(
279 ConvertRateVariable::EarContinuous,
280 ear,
281 compounding_periods_in_year,
282 apr_in_percent,
283 epr_in_percent,
284 ear_in_percent,
285 apr,
286 epr,
287 ear,
288 &apr_formula,
289 &epr_formula,
290 &ear_formula,
291 ))
292}
293
294/// Helper function to convert an effective annual rate (EAR) into all possible conversions (APR, EPR).
295///
296/// # Errors
297/// Returns [`FinanceError`] if periods are zero, rate is non-finite, or `ear <= -1.0`.
298pub fn ear(ear: f64, compounding_periods_in_year: u32) -> FinanceResult<ConvertRateSolution> {
299 check_inputs(ear, compounding_periods_in_year, ConvertRateVariable::Ear)?;
300 let apr = convert_ear_to_apr(ear, compounding_periods_in_year)?;
301 let epr = convert_ear_to_epr(ear, compounding_periods_in_year)?;
302 let apr_in_percent = format!("{:.4}%", apr * 100.);
303 let epr_in_percent = format!("{:.4}%", epr * 100.);
304 let ear_in_percent = format!("{:.4}%", ear * 100.);
305 let apr_formula = format!("{} * {}", epr, compounding_periods_in_year);
306 let epr_formula = format!("(1 + {})^(1 / {}) - 1", ear, compounding_periods_in_year);
307 let ear_formula = format!("{}", ear);
308 Ok(ConvertRateSolution::new(
309 ConvertRateVariable::Ear,
310 ear,
311 compounding_periods_in_year,
312 apr_in_percent,
313 epr_in_percent,
314 ear_in_percent,
315 apr,
316 epr,
317 ear,
318 &apr_formula,
319 &epr_formula,
320 &ear_formula,
321 ))
322}
323
324/// Helper function to convert a periodic interest rate (EPR) to all rate conversions.
325///
326/// Note: an EPR of 0.99 with a large number of periods can create decimal inaccuracies due to
327/// floating point representation. The epr conversion method is tested and guaranteed accurate up
328/// to 780 periods between rates -0.034 and 0.989; rates outside this range may incur tiny
329/// floating-point drift.
330///
331/// # Errors
332/// Returns [`FinanceError`] if periods are zero, rate is non-finite, or `epr <= -1.0`.
333pub fn epr(epr: f64, compounding_periods_in_year: u32) -> FinanceResult<ConvertRateSolution> {
334 check_inputs(epr, compounding_periods_in_year, ConvertRateVariable::Epr)?;
335 let apr = epr * compounding_periods_in_year as f64;
336 let ear = convert_apr_to_ear(apr, compounding_periods_in_year)?;
337 let apr_in_percent = format!("{:.4}%", apr * 100.);
338 let epr_in_percent = format!("{:.4}%", epr * 100.);
339 let ear_in_percent = format!("{:.4}%", ear * 100.);
340 let apr_formula = format!("{} * {}", epr, compounding_periods_in_year);
341 let epr_formula = String::new();
342 let ear_formula = format!("(1 + {})^{} - 1", epr, compounding_periods_in_year);
343 Ok(ConvertRateSolution::new(
344 ConvertRateVariable::Epr,
345 epr,
346 compounding_periods_in_year,
347 apr_in_percent,
348 epr_in_percent,
349 ear_in_percent,
350 apr,
351 epr,
352 ear,
353 &apr_formula,
354 &epr_formula,
355 &ear_formula,
356 ))
357}
358
359/// Convert a nominal interest rate (Annual rate, APR) to EAR (effective annual rate). Returns f64.
360///
361/// Related Functions:
362/// * [`apr`](./fn.apr.html) to convert APR to all forms of rate conversion, and return a custom type with additional functionality and extra information available in the `dbg!()`.
363/// * [`convert_apr_to_ear_solution`](./fn.convert_apr_to_ear_solution.html) to convert APR to EAR and return a custom type with additional functionality and extra information available in the `dbg!()`.
364///
365/// The formula:
366///
367// EAR = (1 + (apr/periods))<sup>periods</sup> - 1
368// Latex formula: EAR=\left(1+\left(\frac{APR}{periods}\right)\right)^{periods}-1
369/// > <img src="http://i.upmath.me/svg/EAR%3D%5Cleft(1%2B%5Cfrac%7BAPR%7D%7Bperiods%7D%5Cright)%5E%7Bperiods%7D-1" />
370///
371/// # Arguments
372/// * `rate` - The input rate, expressed as a floating point number.
373/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
374/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`. Must be u32.
375///
376/// # Errors
377/// * `periods` - must be a u32 value greater than 0.
378///
379/// # Examples
380/// Convert annual rate to effective annual rate.
381/// ```
382/// use finance_solution::*;
383/// // The annual percentage rate is 3.4% and 12 compounding periods per year.
384/// let nominal_rate = 0.034;
385/// let periods = 12;
386///
387/// let effective_annual_rate = convert_rate::convert_apr_to_ear(nominal_rate, periods).unwrap();
388///
389/// // Confirm that the EAR is correct.
390/// assert_approx_equal!(0.034535, effective_annual_rate);
391/// ```
392pub fn convert_apr_to_ear(apr: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
393 check_inputs(apr, compounding_periods_in_year, ConvertRateVariable::Apr)?;
394 Ok((1_f64 + (apr / compounding_periods_in_year as f64))
395 .powf(compounding_periods_in_year as f64)
396 - 1_f64)
397}
398
399/// Convert an APR to EAR (effective annual rate). Returns a custom type with additional functionality and extra information available in the dbg!().
400///
401/// Related Functions:
402/// * [`apr`](./fn.apr.html) macro to convert APR to all forms of rate conversion, and return a custom type with additional functionality and extra information available in the dbg!().
403/// * [`convert_apr_to_ear`](./fn.convert_apr_to_ear.html) to convert APR to EAR and return the f64 value instead of a solution struct.
404///
405/// The formula:
406///
407// EAR = (1 + (apr/periods))<sup>periods</sup> - 1
408// Latex formula: EAR=\left(1+\left(\frac{APR}{periods}\right)\right)^{periods}-1
409/// > <img src="http://i.upmath.me/svg/EAR%3D%5Cleft(1%2B%5Cfrac%7BAPR%7D%7Bperiods%7D%5Cright)%5E%7Bperiods%7D-1" />
410///
411/// # Arguments
412/// * `rate` - The input rate, expressed as a floating point number.
413/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
414/// * `compounding_periods_in_year` - The number of compounding periods in a year. Often appears as `n` or `t`. Must be u32.
415///
416/// # Errors
417/// * `periods` - must be a u32 value greater than 0.
418///
419/// # Example
420/// /// Convert annual rate to effective annual rate.
421/// ```
422/// use finance_solution::*;
423/// // The annual percentage rate is 3.4%.
424/// let nominal_rate = 0.034;
425///
426/// // There are 12 compounding periods per year (monthly compounding).
427/// let periods = 12;
428///
429/// let effective_annual_rate = convert_apr_to_ear_solution(nominal_rate, periods).unwrap().ear();
430///
431/// // Confirm that the EAR is correct.
432/// assert_approx_equal!(0.034535, effective_annual_rate);
433/// ```
434pub fn convert_apr_to_ear_solution(
435 apr: f64,
436 compounding_periods_in_year: u32,
437) -> FinanceResult<ConvertRateSolution> {
438 self::apr(apr, compounding_periods_in_year)
439}
440
441/// Convert APR (annual rate) to periodic rate. Returns f64.
442///
443/// Related Functions:
444/// * [`apr`](./fn.apr.html) macro to convert APR to all forms of rate conversion, and return a custom type with additional functionality and extra information available in the dbg!().
445/// * [`convert_apr_to_epr_solution`](./fn.convert_apr_to_epr_solution.html) to convert APR to EPR and return a custom type with additional functionality and extra information available in the dbg!().
446///
447/// The formula:
448///
449// Periodic Rate = apr / compounding_periods_in_year
450// Latex formula: EPR=\frac{APR}{compounding\_periods\_in\_year}
451/// > <img src="http://i.upmath.me/svg/EPR%3D%5Cfrac%7BAPR%7D%7Bcompounding%5C_periods%5C_in%5C_year%7D" />
452///
453/// # Arguments
454/// * `rate` - The input rate, expressed as a floating point number.
455/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
456/// * `compounding_periods_in_year` - The number of compounding periods in a year. Often appears as `n` or `t`.
457///
458/// # Errors
459/// * `compounding_periods_in_year` - must be a u32 value greater than 0.
460///
461/// # Example
462/// Convert annual rate to periodic rate.
463/// ```
464/// use finance_solution::*;
465/// // The annual percentage rate is 3.4%.
466/// // There are 12 compounding periods per year.
467/// let nominal_rate = 0.034;
468/// let periods = 12;
469///
470/// let periodic_rate = convert_apr_to_epr(nominal_rate, periods).unwrap();
471///
472/// // Confirm that the periodic value is correct to six decimal places.
473/// assert_approx_equal!(0.00283333, periodic_rate);
474/// ```
475pub fn convert_apr_to_epr(apr: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
476 check_inputs(apr, compounding_periods_in_year, ConvertRateVariable::Apr)?;
477 Ok(apr / compounding_periods_in_year as f64)
478}
479/// Convert APR (annual rate) to periodic rate. Returns a custom solution type.
480///
481/// Related Functions:
482/// * [`apr`](./fn.apr.html) macro to convert APR to all forms of rate conversion, and return a custom type with additional functionality and extra information available in the dbg!().
483/// * [`convert_apr_to_epr`](./fn.convert_apr_to_epr.html) to convert APR to EPR and return a single f64 value instead of a solution struct.
484///
485/// The formula:
486///
487// Periodic Rate = apr / compounding_periods_in_year
488// Latex formula: EPR=\frac{APR}{compounding\_periods\_in\_year}
489/// > <img src="http://i.upmath.me/svg/EPR%3D%5Cfrac%7BAPR%7D%7Bcompounding%5C_periods%5C_in%5C_year%7D" />
490///
491/// # Arguments
492/// * `rate` - The input rate, expressed as a floating point number.
493/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
494/// * `compounding_periods_in_year` - The number of compounding periods in a year. Often appears as `n` or `t`.
495///
496/// # Errors
497/// * `compounding_periods_in_year` - must be a u32 value greater than 0.
498///
499/// # Example
500/// Convert annual rate to periodic rate.
501/// ```
502/// use finance_solution::*;
503/// // The annual percentage rate is 3.4%.
504/// // There are 12 compounding periods per year.
505/// let nominal_rate = 0.034;
506/// let periods = 12;
507///
508/// let apr_to_epr_solution = convert_apr_to_epr_solution(nominal_rate, periods).unwrap();
509///
510/// // Confirm that the periodic rate is correct to six decimal places.
511/// assert_approx_equal!(0.00283333, apr_to_epr_solution.epr());
512/// ```
513pub fn convert_apr_to_epr_solution(
514 apr: f64,
515 compounding_periods_in_year: u32,
516) -> FinanceResult<ConvertRateSolution> {
517 self::apr(apr, compounding_periods_in_year)
518}
519
520/// Convert an EAR to APR. Returns f64.
521///
522/// Related Functions:
523/// * [`ear`](./fn.ear.html) to convert EAR to all forms of rate conversion, and return a custom type with additional functionality and extra information available in the dbg!().
524/// * [`convert_ear_to_apr_solution`](./fn.convert_ear_to_apr_solution.html) to convert EAR to APR and return a custom type with additional functionality and extra information available in the dbg!().
525///
526/// The formula is:
527///
528// Latex formula: APR=( (1+ear)^{(1/compounding\_periods\_in\_year)} - 1)\times compounding\_periods\_in\_year
529/// <img src="http://i.upmath.me/svg/APR%3D(%20(1%2Bear)%5E%7B(1%2Fcompounding%5C_periods%5C_in%5C_year)%7D%20-%201)%5Ctimes%20compounding%5C_periods%5C_in%5C_year" />
530///
531/// <small> Note: This formula involves converting the EAR to EPR first, and then converting the EPR to APR.</small>
532///
533/// # Arguments
534/// * `rate` - The input rate, expressed as a floating point number.
535/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
536/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
537///
538/// # Errors
539/// * `periods` - must be a u32 value greater than 0.
540///
541/// # Example
542/// Convert effective annual rate (EAR) to annual percentage rate (APR).
543/// ```
544/// use finance_solution::*;
545/// // The effective annual rate is 3.4534%
546/// // There are 12 compounding periods per year.
547/// let effective_annual_rate = 0.03453486936;
548/// let periods = 12;
549///
550/// let nominal_rate = convert_rate::convert_ear_to_apr(effective_annual_rate, periods).unwrap();
551///
552/// // Confirm that the APR is correct.
553/// assert_approx_equal!(0.034, nominal_rate);
554/// ```
555pub fn convert_ear_to_apr(ear: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
556 check_inputs(ear, compounding_periods_in_year, ConvertRateVariable::Ear)?;
557 Ok(
558 ((1_f64 + ear).powf(1_f64 / compounding_periods_in_year as f64) - 1_f64)
559 * compounding_periods_in_year as f64,
560 )
561}
562/// Convert an EAR to APR. Returns solution struct with additional information and functionality.
563///
564/// Related Functions:
565/// * [`ear`](./fn.ear.html) general-purpose macro to convert EAR into all rate variations.
566/// * [`convert_ear_to_apr`](./fn.convert_ear_to_apr.html) to convert EAR to APR and return an f64 value.
567///
568/// The formula is:
569///
570// Latex formula: APR=( (1+ear)^{(1/compounding\_periods\_in\_year)} - 1)\times compounding\_periods\_in\_year
571/// <img src="http://i.upmath.me/svg/APR%3D(%20(1%2Bear)%5E%7B(1%2Fcompounding%5C_periods%5C_in%5C_year)%7D%20-%201)%5Ctimes%20compounding%5C_periods%5C_in%5C_year" />
572///
573/// <small> Note: This formula involves converting the EAR to EPR first, and then converting the EPR to APR.</small>
574///
575/// # Arguments
576/// * `rate` - The input rate, expressed as a floating point number.
577/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
578/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
579///
580/// # Errors
581/// * `periods` - must be a u32 value greater than 0.
582///
583/// # Example
584/// Convert effective annual rate (EAR) to annual percentage rate (APR).
585/// ```
586/// use finance_solution::*;
587/// // The effective annual rate is 3.453486936028982%
588/// let effective_annual_rate = 0.03453486936028982;
589///
590/// // There are 12 compounding periods per year.
591/// let periods = 12;
592///
593/// let ear_to_apr_solution = convert_rate::convert_ear_to_apr_solution(effective_annual_rate, periods).unwrap();
594///
595/// // Confirm that the APR is correct.
596/// assert_approx_equal!(0.034, ear_to_apr_solution.apr());
597/// ```
598pub fn convert_ear_to_apr_solution(
599 ear: f64,
600 compounding_periods_in_year: u32,
601) -> FinanceResult<ConvertRateSolution> {
602 self::ear(ear, compounding_periods_in_year)
603}
604
605/// Convert an EAR (Effective Annual Rate) to periodic rate (aka EPR, effective periodic rate). Returns f64.
606///
607/// Related Functions:
608/// * [`convert_ear_to_epr_solution`](./fn.convert_ear_to_epr_solution.html) to convert EAR to EPR and return a custom type with extra information available in the dbg!().
609///
610/// The formula is:
611///
612// Periodic Rate = (1 + ear)<sup>(1 / compounding_periods_in_year)</sup> - 1
613// Latex formula: EPR=(1 + ear)^{(1/compounding\_periods\_in\_year)}-1
614/// > <img src="http://i.upmath.me/svg/EPR%3D(1%20%2B%20ear)%5E%7B(1%2Fcompounding%5C_periods%5C_in%5C_year)%7D-1" />
615///
616/// or this formula can be re-written as:
617///
618/// <img src="https://i.upmath.me/svg/EPR%20%3D%20%5Csqrt%5Bperiods%5D%7B1%2Bear%7D%20-%201" />
619///
620/// # Arguments
621/// * `ear` - The input rate (effective annual rate), expressed as a floating point number.
622/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
623/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
624///
625/// # Errors
626/// * `periods` - must be a u32 value greater than or equal to 1.
627///
628/// # Example
629/// Convert effective annual rate to periodic rate.
630/// ```
631/// use finance_solution::*;
632/// // The effective annual rate is 3.4534%.
633/// // There are 12 compounding periods per year.
634/// let effective_annual_rate = 0.03453486936;
635/// let periods = 12;
636///
637/// let periodic_rate = convert_rate::convert_ear_to_epr(effective_annual_rate, periods).unwrap();
638///
639/// // Confirm that the EPR is correct.
640/// assert_approx_equal!(0.00283333, periodic_rate);
641/// ```
642pub fn convert_ear_to_epr(ear: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
643 check_inputs(ear, compounding_periods_in_year, ConvertRateVariable::Ear)?;
644 Ok((1_f64 + ear).powf(1_f64 / compounding_periods_in_year as f64) - 1_f64)
645}
646/// Convert an EAR (Effective Annual Rate) to periodic rate (also known as EPR). Returns a solution struct with additional information and functionality.
647/// /// Related Functions:
648/// * [`convert_ear_to_epr`](./fn.convert_ear_to_epr.html) to convert EAR to EPR and return a single f64 value.
649///
650/// The formula is:
651///
652// Periodic Rate = (1 + ear)<sup>(1 / compounding_periods_in_year)</sup> - 1
653// Latex formula: EPR=(1 + ear)^{(1/compounding\_periods\_in\_year)}-1
654/// > <img src="http://i.upmath.me/svg/EPR%3D(1%20%2B%20ear)%5E%7B(1%2Fcompounding%5C_periods%5C_in%5C_year)%7D-1" />
655///
656/// or this formula can be re-written as:
657///
658/// <img src="https://i.upmath.me/svg/EPR%20%3D%20%5Csqrt%5Bperiods%5D%7B1%2Bear%7D%20-%201" />
659///
660/// # Arguments
661/// * `ear` - The input rate (effective annual rate), expressed as a floating point number.
662/// For instance 0.05 would mean 5%. Often appears as `r` or `i` in formulas.
663/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
664///
665/// # Errors
666/// * `periods` - must be a u32 value greater than or equal to 1.
667///
668/// # Example
669/// Convert effective annual rate to periodic rate.
670/// ```
671/// use finance_solution::*;
672/// // The effective annual rate is 3.4534%.
673/// // There are 12 compounding periods per year.
674/// let effective_annual_rate = 0.03453486936;
675/// let periods = 12;
676///
677/// let ear_to_epr_solution = convert_rate::convert_ear_to_epr_solution(effective_annual_rate, periods).unwrap();
678///
679/// // Confirm that the EPR is correct.
680/// assert_approx_equal!(0.00283333, ear_to_epr_solution.epr());
681/// ```
682pub fn convert_ear_to_epr_solution(
683 ear: f64,
684 compounding_periods_in_year: u32,
685) -> FinanceResult<ConvertRateSolution> {
686 self::ear(ear, compounding_periods_in_year)
687}
688
689/// Convert a periodic rate (aka EPR, effective periodic rate) to EAR (effective annual rate). Return a single f64 value.
690///
691/// Related Functions:
692/// * [`convert_epr_to_ear_solution`](./fn.convert_epr_to_ear_solution.html) to convert EPR to EAR and return a custom type with extra information available in the dbg!().
693///
694/// The formula is:
695///
696// EAR = (1 + epr)<sup>(compounding_periods_in_year)</sup> - 1
697// Latex formula: EAR=(1 + epr)^{compounding\_periods\_in\_year}-1
698/// > <img src="http://i.upmath.me/svg/EAR%3D(1%20%2B%20epr)%5E%7Bcompounding%5C_periods%5C_in%5C_year%7D-1" />
699///
700/// # Arguments
701/// * `epr` - The input rate (periodic rate), expressed as a floating point number.
702/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
703/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
704///
705/// # Errors
706/// * `periods` - must be a u32 value greater than or equal to 1.
707///
708/// # Example
709/// Convert a periodic rate to effective annual rate.
710/// ```
711/// use finance_solution::*;
712/// // The periodic rate is 3.4534%. There are 12 compounding periods per year.
713/// let (periodic_rate, periods) = (0.034, 12);
714///
715/// let ear = convert_rate::convert_epr_to_ear(periodic_rate, periods).unwrap();
716///
717/// // Confirm that the EAR is correct.
718/// assert_approx_equal!(0.49364182107104493, ear);
719/// ```
720pub fn convert_epr_to_ear(epr: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
721 check_inputs(epr, compounding_periods_in_year, ConvertRateVariable::Epr)?;
722 Ok((1_f64 + epr).powf(compounding_periods_in_year as f64) - 1_f64)
723}
724/// Convert a periodic rate (EPR) to effective annual rate (EAR), returning a solution struct with additionality information and features.
725///
726/// Related Functions:
727/// * [`convert_epr_to_ear`](./fn.convert_epr_to_ear.html) to convert EPR to EAR and return a single f64 value.
728///
729/// The formula is:
730///
731// EAR = (1 + epr)<sup>(compounding_periods_in_year)</sup> - 1
732// Latex formula: EAR=(1 + epr)^{compounding\_periods\_in\_year}-1
733/// > <img src="http://i.upmath.me/svg/EAR%3D(1%20%2B%20epr)%5E%7Bcompounding%5C_periods%5C_in%5C_year%7D-1" />
734///
735/// # Arguments
736/// * `epr` - The input rate (periodic rate), expressed as a floating point number.
737/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
738/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
739///
740/// # Errors
741/// * `periods` - must be a u32 value greater than or equal to 1.
742///
743/// # Example
744/// Convert a periodic rate to effective annual rate.
745/// ```
746/// use finance_solution::*;
747/// // The periodic rate is 3.4534%. There are 12 compounding periods per year.
748/// let (periodic_rate, periods) = (0.034, 12);
749///
750/// let epr_to_ear_solution = convert_rate::convert_epr_to_ear_solution(periodic_rate, periods).unwrap();
751///
752/// // Confirm that the EAR is correct.
753/// assert_approx_equal!(0.49364182107104493, epr_to_ear_solution.ear());
754/// ```
755pub fn convert_epr_to_ear_solution(
756 epr: f64,
757 compounding_periods_in_year: u32,
758) -> FinanceResult<ConvertRateSolution> {
759 self::epr(epr, compounding_periods_in_year)
760}
761
762/// Convert periodic rate to APR (aka Annual rate, nominal interest rate, Annual Percentage Rate). Returns f64.
763///
764/// Related Functions:
765/// * [`convert_epr_to_apr_solution`](./fn.convert_epr_to_apr_solution.html) to convert EPR to APR and return a solution struct with better debugging and additional information.
766///
767/// The formula is:
768///
769// APR = epr * compounding_periods_per_year
770// Latex formula: APR=periodic\_rate\times compounding\_periods\_per\_year
771/// > <img src="http://i.upmath.me/svg/APR%3Dperiodic%5C_rate%5Ctimes%20compounding%5C_periods%5C_per%5C_year" />
772///
773/// # Arguments
774/// * `epr` - The input rate (periodic rate), expressed as a floating point number.
775/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
776/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
777///
778/// # Errors
779/// * `periods` - must be a u32 value greater than or equal to 1.
780///
781/// # Example
782/// Convert a periodic rate to the annual rate (APR).
783/// ```
784/// use finance_solution::*;
785/// // The periodic rate is 3.4%. There are 12 compounding periods per year.
786/// let (periodic_rate, periods) = (0.034, 12);
787///
788/// let apr = convert_rate::convert_epr_to_apr(periodic_rate, periods).unwrap();
789///
790/// // Confirm that the APR is correct.
791/// assert_approx_equal!(0.4080, apr);
792/// ```
793pub fn convert_epr_to_apr(epr: f64, compounding_periods_in_year: u32) -> FinanceResult<f64> {
794 check_inputs(epr, compounding_periods_in_year, ConvertRateVariable::Epr)?;
795 Ok(epr * compounding_periods_in_year as f64)
796}
797/// Convert periodic rate to APR (aka Annual rate, nominal interest rate, Annual Percentage Rate). Returns a custom solution type.
798///
799/// Related Functions:
800/// * [`convert_epr_to_apr`](./fn.convert_epr_to_apr.html) to convert EPR to APR and return an f64 instead of a full solution struct.
801///
802/// The formula is:
803///
804// APR = epr * compounding_periods_per_year
805// Latex formula: APR=periodic\_rate\times compounding\_periods\_per\_year
806/// > <img src="http://i.upmath.me/svg/APR%3Dperiodic%5C_rate%5Ctimes%20compounding%5C_periods%5C_per%5C_year" />
807///
808/// # Arguments
809/// * `epr` - The input rate (periodic rate), expressed as a floating point number.
810/// For instance 0.05 indicates 5%. Often appears as `r` or `i` in formulas.
811/// * `periods` - The number of compounding periods in a year. Often appears as `n` or `t`.
812///
813/// # Errors
814/// * `periods` - must be a u32 value greater than or equal to 1.
815///
816/// # Example
817/// Convert a periodic rate to the annual rate (APR).
818/// ```
819/// use finance_solution::*;
820/// // The periodic rate is 3.4%. There are 12 compounding periods per year.
821/// let (periodic_rate, periods) = (0.034, 12);
822///
823/// let epr_to_apr_solution = convert_rate::convert_epr_to_apr_solution(periodic_rate, periods).unwrap();
824///
825/// // Confirm that the APR is correct.
826/// assert_approx_equal!(0.4080, epr_to_apr_solution.apr());
827/// ```
828pub fn convert_epr_to_apr_solution(
829 epr: f64,
830 compounding_periods_in_year: u32,
831) -> FinanceResult<ConvertRateSolution> {
832 self::epr(epr, compounding_periods_in_year)
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838
839 #[test]
840 fn test_convert_rate_apr_symmetry() {
841 let apr_rates = vec![
842 0.034,
843 -0.034,
844 0.00283333333,
845 -0.00283333333,
846 0.0345348693603,
847 0.0,
848 -0.0,
849 1.0,
850 2.1,
851 0.00001,
852 ];
853 let periods = vec![12, 1, 2, 3, 4, 6, 24, 52, 365, 780];
854
855 for rates_i in apr_rates {
856 for &periods_i in periods.iter() {
857 check_rate_conversion_symmetry(rates_i, periods_i);
858
859 fn check_rate_conversion_symmetry(rate: f64, periods: u32) {
860 // apr scenarios
861 let apr_epr = convert_apr_to_epr(rate, periods).unwrap();
862 let _epr_apr = convert_epr_to_apr(apr_epr, periods).unwrap();
863 let apr_ = apr(rate, periods).unwrap();
864 assert_approx_equal!(apr_epr, apr_.epr());
865 assert_approx_equal!(_epr_apr, rate);
866
867 let apr_ear = convert_apr_to_ear(rate, periods).unwrap();
868 let _ear_apr = convert_ear_to_apr(apr_ear, periods).unwrap();
869 assert_approx_equal!(apr_ear, apr_.ear());
870 assert_approx_equal!(_ear_apr, rate);
871 }
872 }
873 }
874 }
875
876 #[test]
877 fn test_convert_rate_ear_symmetry() {
878 let ear_rates = vec![
879 0.034,
880 -0.034,
881 0.00283333333,
882 -0.00283333333,
883 0.0345348693603,
884 0.0,
885 -0.0,
886 1.0,
887 2.1,
888 0.00001,
889 ];
890 let periods = vec![12, 1, 2, 3, 4, 6, 24, 52, 365, 780];
891
892 for rates_i in ear_rates {
893 for &periods_i in periods.iter() {
894 check_rate_conversion_symmetry(rates_i, periods_i);
895
896 fn check_rate_conversion_symmetry(rate: f64, periods: u32) {
897 // ear scenarios
898 let ear_apr = convert_ear_to_apr(rate, periods).unwrap();
899 let _apr_ear = convert_apr_to_ear(ear_apr, periods).unwrap();
900 let ear_ = ear(rate, periods).unwrap();
901 assert_approx_equal!(ear_apr, ear_.apr());
902 assert_approx_equal!(_apr_ear, rate);
903
904 let ear_epr = convert_ear_to_epr(rate, periods).unwrap();
905 let _epr_ear = convert_epr_to_ear(ear_epr, periods).unwrap();
906 assert_approx_equal!(ear_epr, ear_.epr());
907 assert_approx_equal!(_epr_ear, rate);
908 }
909 }
910 }
911 }
912
913 #[test]
914 fn test_convert_rate_epr_symmetry() {
915 let epr_rates = vec![
916 0.034,
917 -0.034,
918 0.00283333333,
919 -0.00283333333,
920 0.0345348693603,
921 0.0,
922 -0.039,
923 -0.0,
924 0.98,
925 0.00001,
926 0.98999,
927 ];
928 let periods = vec![12, 1, 2, 3, 4, 6, 24, 52, 365, 780];
929 // note: epr_rate of 0.99 causes floating point representation error on big periods. Periods over 780 also cause failed tests.
930
931 for rates_i in epr_rates {
932 for &periods_i in periods.iter() {
933 check_rate_conversion_symmetry(rates_i, periods_i);
934
935 fn check_rate_conversion_symmetry(rate: f64, periods: u32) {
936 // epr scenarios
937 let epr_apr = convert_epr_to_apr(rate, periods).unwrap();
938 let _apr_epr = convert_apr_to_epr(epr_apr, periods).unwrap();
939 let epr_ = epr(rate, periods).unwrap();
940 assert_approx_equal!(epr_apr, epr_.apr());
941 assert_approx_equal!(_apr_epr, rate);
942
943 let epr_ear = convert_epr_to_ear(rate, periods).unwrap();
944 let _ear_epr = convert_ear_to_epr(epr_ear, periods).unwrap();
945 assert_approx_equal!(epr_ear, epr_.ear());
946 assert_approx_equal!(_ear_epr, rate);
947 }
948 }
949 }
950 }
951
952 #[test]
953 fn test_convert_rates_simple_1() {
954 // test on excel values using 12 periods
955 const PERIODS: u32 = 12;
956 let apr_epr_ear_rates = vec![
957 (0.034, 0.00283333333, 0.0345348693603),
958 (-0.034, -0.002833333333, -0.03347513889),
959 (1.0, 0.08333333333, 1.6130352902247),
960 (-1.0, -0.083333333333, -0.64800437199),
961 (2.1, 0.175, 5.9255520766347),
962 (-2.1, -0.175, -0.90058603794),
963 ];
964 for rate_tupe in apr_epr_ear_rates {
965 let ap = convert_apr_to_epr(rate_tupe.0, PERIODS).unwrap();
966 let ae = convert_apr_to_ear(rate_tupe.0, PERIODS).unwrap();
967 let pa = convert_epr_to_apr(rate_tupe.1, PERIODS).unwrap();
968 let pe = convert_epr_to_ear(rate_tupe.1, PERIODS).unwrap();
969 let ea = convert_ear_to_apr(rate_tupe.2, PERIODS).unwrap();
970 let ep = convert_ear_to_epr(rate_tupe.2, PERIODS).unwrap();
971 check_rate_conversion_symmetry(ap, ae, pa, pe, ea, ep);
972
973 fn check_rate_conversion_symmetry(
974 ap: f64,
975 ae: f64,
976 pa: f64,
977 pe: f64,
978 ea: f64,
979 ep: f64,
980 ) {
981 assert_eq!(round_6(ap), round_6(ep));
982 assert_eq!(round_6(ae), round_6(pe));
983 assert_eq!(round_6(pa), round_6(ea));
984 }
985 }
986 }
987
988 #[test]
989 fn test_convert_rate_err_zero_periods() {
990 assert!(convert_apr_to_ear(0.05, 0).is_err());
991 assert!(apr(0.05, 0).is_err());
992 assert!(ear(-1.5, 12).is_err());
993 assert!(epr(-1.0, 12).is_err());
994 }
995}