From 39e901e9916aa5505c0172177998f7c49ab9b422 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Fri, 28 Apr 2023 23:08:00 -0400 Subject: [PATCH 01/18] Added an algorithm to calculate the present value of cash flows --- financial/present_value.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 financial/present_value.py diff --git a/financial/present_value.py b/financial/present_value.py new file mode 100644 index 000000000000..14d158af9c80 --- /dev/null +++ b/financial/present_value.py @@ -0,0 +1,20 @@ +# Algorithm that calculates the present value of a stream of yearly cash flows given... +# 1. The discount rate (as a decimal, not a percent) +# 2. An array of cash flows, with the index of the cash flow being the associated year + +# Note: This algorithm assumes that cash flows are paid at the end of the specified year + +from typing import List, Tuple + +def present_value(discount_rate: float, cash_flows: List[float]) -> Tuple[float, str]: + present_value = 0.0 + + if discount_rate == -1: + return (0, 'Invalid discount rate, please choose a rate other than -1') + + for idx, cash_flow in enumerate(cash_flows): + present_value += cash_flow / ((1 + discount_rate) ** idx) + + return (present_value, 'The present value of the given yearly cash flows at a ' + + str(round(discount_rate * 100, 2)) + '% discount rate is $' + + str(round(present_value, 2))) \ No newline at end of file From 9fdb6680e63fecb9ce2da30d4ebf6d6bf6fa758f Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Fri, 28 Apr 2023 23:24:38 -0400 Subject: [PATCH 02/18] added doctest and reference --- financial/present_value.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index 14d158af9c80..fbcb6ad20617 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -1,3 +1,5 @@ +# Reference: https://www.investopedia.com/terms/p/presentvalue.asp + # Algorithm that calculates the present value of a stream of yearly cash flows given... # 1. The discount rate (as a decimal, not a percent) # 2. An array of cash flows, with the index of the cash flow being the associated year @@ -17,4 +19,8 @@ def present_value(discount_rate: float, cash_flows: List[float]) -> Tuple[float, return (present_value, 'The present value of the given yearly cash flows at a ' + str(round(discount_rate * 100, 2)) + '% discount rate is $' - + str(round(present_value, 2))) \ No newline at end of file + + str(round(present_value, 2))) + +if __name__ == "__main__": + import doctest + doctest.testmod() \ No newline at end of file From 686b4bcea34342c0428474a95e9e21cad7736201 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 03:29:17 +0000 Subject: [PATCH 03/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index fbcb6ad20617..326c27f99c7f 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -8,19 +8,26 @@ from typing import List, Tuple + def present_value(discount_rate: float, cash_flows: List[float]) -> Tuple[float, str]: present_value = 0.0 if discount_rate == -1: - return (0, 'Invalid discount rate, please choose a rate other than -1') + return (0, "Invalid discount rate, please choose a rate other than -1") for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) - - return (present_value, 'The present value of the given yearly cash flows at a ' - + str(round(discount_rate * 100, 2)) + '% discount rate is $' - + str(round(present_value, 2))) + + return ( + present_value, + "The present value of the given yearly cash flows at a " + + str(round(discount_rate * 100, 2)) + + "% discount rate is $" + + str(round(present_value, 2)), + ) + if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + + doctest.testmod() From 6b9a4f8b0646b6a76aa7d24f9df9014751c99bf5 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Fri, 28 Apr 2023 23:42:55 -0400 Subject: [PATCH 04/18] Resolving deprecation issues with typing module --- financial/present_value.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 326c27f99c7f..39313e838311 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -6,10 +6,10 @@ # Note: This algorithm assumes that cash flows are paid at the end of the specified year -from typing import List, Tuple +from typing import list, tuple -def present_value(discount_rate: float, cash_flows: List[float]) -> Tuple[float, str]: +def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, str]: present_value = 0.0 if discount_rate == -1: From 582bd5e6fc7fbeb1ba7effc78d0af27316690d40 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Sat, 29 Apr 2023 00:04:05 -0400 Subject: [PATCH 05/18] Fixing argument type checks and adding doctest case --- financial/present_value.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 39313e838311..7ae5a9a9e2b0 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -6,14 +6,15 @@ # Note: This algorithm assumes that cash flows are paid at the end of the specified year -from typing import list, tuple - - def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, str]: + """ + >>> present_value(0.13, [10, 20.70, -293, 297]) + 4.692504038749803 + """ present_value = 0.0 if discount_rate == -1: - return (0, "Invalid discount rate, please choose a rate other than -1") + raise ValueError('Invalid discount rate, please choose a rate other than -1') for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) From a01d5ac8d1ca0cadb7e436694bcf171048bfe968 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 04:05:30 +0000 Subject: [PATCH 06/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index 7ae5a9a9e2b0..a7587bce9301 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -6,6 +6,7 @@ # Note: This algorithm assumes that cash flows are paid at the end of the specified year + def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, str]: """ >>> present_value(0.13, [10, 20.70, -293, 297]) @@ -14,7 +15,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, present_value = 0.0 if discount_rate == -1: - raise ValueError('Invalid discount rate, please choose a rate other than -1') + raise ValueError("Invalid discount rate, please choose a rate other than -1") for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) From d025ce5fd16ffedafd657f1e3449fae218d481a5 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Sat, 29 Apr 2023 00:16:01 -0400 Subject: [PATCH 07/18] Fixing failing doctest case by requiring less precision due to floating point inprecision --- financial/present_value.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index a7587bce9301..533e1306c5d4 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -9,8 +9,8 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, str]: """ - >>> present_value(0.13, [10, 20.70, -293, 297]) - 4.692504038749803 + >>> round(present_value(0.13, [10, 20.70, -293, 297]), 2) + 4.69 """ present_value = 0.0 From 7e86cab4b05136997dc8bc80dbf95b018c96cc77 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Sat, 29 Apr 2023 00:20:34 -0400 Subject: [PATCH 08/18] Updating return type --- financial/present_value.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 533e1306c5d4..1a5264b94ce6 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -7,7 +7,7 @@ # Note: This algorithm assumes that cash flows are paid at the end of the specified year -def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, str]: +def present_value(discount_rate: float, cash_flows: list[float]) -> float: """ >>> round(present_value(0.13, [10, 20.70, -293, 297]), 2) 4.69 @@ -21,11 +21,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> tuple[float, present_value += cash_flow / ((1 + discount_rate) ** idx) return ( - present_value, - "The present value of the given yearly cash flows at a " - + str(round(discount_rate * 100, 2)) - + "% discount rate is $" - + str(round(present_value, 2)), + present_value ) From 39cc1f1a011e9ff6795a57bace58a94d11534d23 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 04:23:51 +0000 Subject: [PATCH 09/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 1a5264b94ce6..f10aa7d047f6 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -20,9 +20,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) - return ( - present_value - ) + return present_value if __name__ == "__main__": From 4d2472095b07e77fad616051a87953c2d7ab7a16 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Sat, 29 Apr 2023 00:38:59 -0400 Subject: [PATCH 10/18] Added test cases for more coverage --- financial/present_value.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index f10aa7d047f6..fb89c4e36f3f 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -11,6 +11,14 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: """ >>> round(present_value(0.13, [10, 20.70, -293, 297]), 2) 4.69 + >>> round(present_value(0.07, [-109129.39, 30923.23, 15098.93, 29734,39]), 2) + -42739.63 + >>> round(present_value(0.07, [109129.39, 30923.23, 15098.93, 29734,39]), 2) + 175519.15 + >>> present_value(-1, [109129.39, 30923.23, 15098.93, 29734,39]) + Traceback (most recent call last): + ... + ValueError: Invalid discount rate, please choose a rate other than -1 """ present_value = 0.0 @@ -20,7 +28,9 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) - return present_value + return ( + present_value + ) if __name__ == "__main__": From 5e6c574d0e40858b41ece0220ec06c4cbb89604c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 04:39:32 +0000 Subject: [PATCH 11/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index fb89c4e36f3f..c592caa461a3 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -28,9 +28,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) - return ( - present_value - ) + return present_value if __name__ == "__main__": From 87f69477d1bb831d746d0b6496f2b6bab1e49b99 Mon Sep 17 00:00:00 2001 From: Sahil Goel Date: Sun, 30 Apr 2023 11:55:13 -0400 Subject: [PATCH 12/18] Make improvements based on Rohan's suggestions --- financial/present_value.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index c592caa461a3..a5da47600d6d 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -9,26 +9,34 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: """ - >>> round(present_value(0.13, [10, 20.70, -293, 297]), 2) + >>> present_value(0.13, [10, 20.70, -293, 297]) 4.69 - >>> round(present_value(0.07, [-109129.39, 30923.23, 15098.93, 29734,39]), 2) + >>> present_value(0.07, [-109129.39, 30923.23, 15098.93, 29734,39]) -42739.63 - >>> round(present_value(0.07, [109129.39, 30923.23, 15098.93, 29734,39]), 2) + >>> present_value(0.07, [109129.39, 30923.23, 15098.93, 29734,39]) 175519.15 >>> present_value(-1, [109129.39, 30923.23, 15098.93, 29734,39]) Traceback (most recent call last): ... - ValueError: Invalid discount rate, please choose a rate other than -1 + ValueError: Discount rate cannot be negative + >>> present_value(0.03, []) + Traceback (most recent call last): + ... + ValueError: Cash flows list cannot be empty """ present_value = 0.0 - if discount_rate == -1: - raise ValueError("Invalid discount rate, please choose a rate other than -1") + if discount_rate < 0: + raise ValueError("Discount rate cannot be negative") + + if not cash_flows: + raise ValueError("Cash flows list cannot be empty") for idx, cash_flow in enumerate(cash_flows): present_value += cash_flow / ((1 + discount_rate) ** idx) - return present_value + decimal_places = 2 + return round(present_value, decimal_places) if __name__ == "__main__": From ae16f6e72a3482aca6433c03c5a0a89ba434875a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 15:55:47 +0000 Subject: [PATCH 13/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index a5da47600d6d..f3175dadc231 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -28,7 +28,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: if discount_rate < 0: raise ValueError("Discount rate cannot be negative") - + if not cash_flows: raise ValueError("Cash flows list cannot be empty") From c0afee8ed356b017761402ea61368c9b437b2ac0 Mon Sep 17 00:00:00 2001 From: Sahil Goel <55365655+sahilg13@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:15:01 -0400 Subject: [PATCH 14/18] Update financial/present_value.py Committed first suggestion Co-authored-by: Christian Clauss --- financial/present_value.py | 1 - 1 file changed, 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index f3175dadc231..54e3bd0b8dfc 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -24,7 +24,6 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: ... ValueError: Cash flows list cannot be empty """ - present_value = 0.0 if discount_rate < 0: raise ValueError("Discount rate cannot be negative") From 682b743a947b0ec1a17e11df12fe3ee8049749f1 Mon Sep 17 00:00:00 2001 From: Sahil Goel <55365655+sahilg13@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:15:26 -0400 Subject: [PATCH 15/18] Update financial/present_value.py Committed second suggestion Co-authored-by: Christian Clauss --- financial/present_value.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 54e3bd0b8dfc..5947f38756a3 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -31,8 +31,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: if not cash_flows: raise ValueError("Cash flows list cannot be empty") - for idx, cash_flow in enumerate(cash_flows): - present_value += cash_flow / ((1 + discount_rate) ** idx) + present_value = sum(cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows)) decimal_places = 2 return round(present_value, decimal_places) From 0299cfbbafbc0ec897640bd82b78e9b2da5945b3 Mon Sep 17 00:00:00 2001 From: Sahil Goel <55365655+sahilg13@users.noreply.github.com> Date: Sun, 30 Apr 2023 13:15:42 -0400 Subject: [PATCH 16/18] Update financial/present_value.py Committed third suggestion Co-authored-by: Christian Clauss --- financial/present_value.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 5947f38756a3..c6dc61c63445 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -33,8 +33,7 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: present_value = sum(cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows)) - decimal_places = 2 - return round(present_value, decimal_places) + return round(present_value, ndigits=2) if __name__ == "__main__": From ae23ac691206c4ae2406c0ca9c17a3e8f3e921b4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 17:15:51 +0000 Subject: [PATCH 17/18] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- financial/present_value.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/financial/present_value.py b/financial/present_value.py index c6dc61c63445..7b56a8db2c72 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -31,7 +31,9 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: if not cash_flows: raise ValueError("Cash flows list cannot be empty") - present_value = sum(cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows)) + present_value = sum( + cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows) + ) return round(present_value, ndigits=2) From 0847eec53ad4bf7fa1d32399a34864392a51490b Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 30 Apr 2023 19:33:12 +0200 Subject: [PATCH 18/18] Apply suggestions from code review --- financial/present_value.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/financial/present_value.py b/financial/present_value.py index 7b56a8db2c72..dc8191a6ef53 100644 --- a/financial/present_value.py +++ b/financial/present_value.py @@ -1,10 +1,11 @@ -# Reference: https://www.investopedia.com/terms/p/presentvalue.asp +""" +Reference: https://www.investopedia.com/terms/p/presentvalue.asp -# Algorithm that calculates the present value of a stream of yearly cash flows given... -# 1. The discount rate (as a decimal, not a percent) -# 2. An array of cash flows, with the index of the cash flow being the associated year +An algorithm that calculates the present value of a stream of yearly cash flows given... +1. The discount rate (as a decimal, not a percent) +2. An array of cash flows, with the index of the cash flow being the associated year -# Note: This algorithm assumes that cash flows are paid at the end of the specified year +Note: This algorithm assumes that cash flows are paid at the end of the specified year def present_value(discount_rate: float, cash_flows: list[float]) -> float: @@ -24,17 +25,13 @@ def present_value(discount_rate: float, cash_flows: list[float]) -> float: ... ValueError: Cash flows list cannot be empty """ - if discount_rate < 0: raise ValueError("Discount rate cannot be negative") - if not cash_flows: raise ValueError("Cash flows list cannot be empty") - present_value = sum( cash_flow / ((1 + discount_rate) ** i) for i, cash_flow in enumerate(cash_flows) ) - return round(present_value, ndigits=2)