Skip to main content

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}