A Credit Payment Calculator So I've attempted this project from hyperskills where you're asked to make a calculator script that can calculate annuity and differentiated payments given that 3 of 4 values are known. I think i went with a real hardcoding way of doing this, as such I would like some advice on making my code more compact and efficient.

import math
import sys

def convert_month_into_years_months(months):
    years, months = divmod(months, 12)
    if years != 1:
        s_year = "s"
    else:
        s_year = ""

    if months != 1:
        s_month = "s"
    else:
        s_month = ""

    if not years:
        return f"You need {months} month{s_month} to repay this credit!"
    elif not months:
        return f"You need {years} year{s_year} to repay this credit!"
    else:
        return f"You need {years} year{s_year} and {months} month{s_month} to repay this credit!"

credit_principal_given = False
period_count_given = False
credit_interest_given = False
monthly_payment_given = False
annuity = False
diff = False

period_count = monthly_payment = credit_principal = credit_interest = 1

command_line = sys.argv
number_of_arguments = len(command_line[1:])
type_of_payment = command_line[1] if number_of_arguments != 0 else None

run = True
if type_of_payment == "--type=diff" and number_of_arguments == 4:
    diff = True
elif type_of_payment == "--type=annuity" and number_of_arguments == 4:
    annuity = True
else:
    run = False

if run:
    for arg in command_line[2:]:
        try:
            if arg[:10] == "--periods=":
                period_count = int(arg[10:])
                period_count_given = True
            elif arg[:10] == "--payment=" and annuity:
                monthly_payment = float(arg[10:])
                monthly_payment_given = True
            elif arg[:11] == "--interest=":
                credit_interest = float(arg[11:])
                credit_interest_given = True
            elif arg[:12] == "--principal=":
                credit_principal = float(arg[12:])
                credit_principal_given = True
        except:
            run = False
            break

    # Checks if any of the values are negative
    values = [period_count ,monthly_payment ,credit_principal ,credit_interest]
    if not all(True if value &gt; 0 else False for value in values):
        run = False


if run:

    nominal_interest_rate = (credit_interest/100)/12

    if annuity:
        if credit_principal_given and monthly_payment_given and credit_interest_given:
            period_count = math.ceil(math.log( (monthly_payment/( monthly_payment - ( nominal_interest_rate * credit_principal ) ) ), 1+nominal_interest_rate ))
            print(convert_month_into_years_months(period_count))

        elif monthly_payment_given and period_count_given and credit_interest_given:
            credit_principal = math.floor(monthly_payment / ( (nominal_interest_rate * (1 + nominal_interest_rate)**period_count )/( (1 + nominal_interest_rate)**period_count - 1) ) )
            print(f"Your credit principal = {credit_principal}!")

        elif credit_principal_given and period_count_given and credit_interest_given:
            monthly_payment = math.ceil(credit_principal * ( (nominal_interest_rate * (1 + nominal_interest_rate)**period_count )/( (1 + nominal_interest_rate)**period_count - 1) ) )
            print(f"Your annuity payment = {monthly_payment}!")

    elif diff:
        sum_diff_payments = 0

        for month in range(1, period_count+1):
            diff_payment = (credit_principal/period_count) + (nominal_interest_rate * (credit_principal - ( (credit_principal * (month-1))/period_count ) ) )
            diff_payment = math.ceil(diff_payment)
            sum_diff_payments += diff_payment

            print(f"Month {month}: paid out {diff_payment}")

    overpayment = round(abs(credit_principal - (period_count * monthly_payment)))
    print(f"\nOverpayment = {overpayment}")

else:
    print("Incorrect parameters")
The one thing that is immediately apparent to me is that parsing of command line arguments could be done better using the built-in argparse module but for an intro you can start with the tutorial.

Here is how the routine could be written, assuming all parameters are mandatory:

import argparse

# check command line options
parser = argparse.ArgumentParser()

parser.add_argument("--type", dest="type_of_payment", type=str, choices=["diff", "annuity"], required=True, help="Add some help text here")
parser.add_argument("--periods", dest="period_count", type=int, required=True)
parser.add_argument("--payment", dest="monthly_payment", type=float, required=True)
parser.add_argument("--interest", dest="credit_interest", type=float, required=True)
parser.add_argument("--principal", dest="credit_principal", type=float, required=True)

args = parser.parse_args()

# show the values
print(f"Type of payment: {args.type_of_payment}")
print(f"Periods: {args.period_count}")
print(f"Payment: {args.monthly_payment}")
print(f"Interest: {args.credit_interest}")
print(f"Principal: {args.credit_principal}")


This is more flexible, because:


you can provide parameters in any order
you can assign default values
you can specify the expected type and also restrict to a specific range of values - in this example type_of_payment must be either "diff" or "annuity"
you can also define your own function if you require more fine-tuned validation of certain parameters
you can easily define groups of mutually exclusive parameters


This is an example but you can customize it further. Then your code will become quite shorter = less distraction and more focus on the important stuff.