2
\$\begingroup\$

The code works as intended but I believe it is very inefficient. The calculation of weights is simple, but I imagine this would be silly, for example, a 100 asset portfolio. The first problem is that my code returns to the beginning of the loop if the weight for y is entered incorrectly. Help with this would be amazing.

Secondly, as alluded to above, this is an inefficient approach. Someone entering 100 weights would not repeat this process. Any ideas where I could find some help for this approach would be great.

r_x=0.15
r_y=0.21
while True:
    try: w_x=float(input("Enter the weight of stock x in your portfolio: "))
    except ValueError:
        print("Please enter the weight using numbers")
        continue
    else:
        if w_x<0 or w_x>1: #seperating it instead of doing it altogether corrected this error for me
            print("Please enter the number as a decimal. Remember, your maximum investment is 100%.")
            continue
    try: w_y=float(input("Enter the weight of stock y in your portoflio: "))
    except ValueError:
        print("Please enter the weight using numbers")
    else:
        if w_y<0 or w_y>1:
            print("Please enter the numbers as a decimal. Remember, your maximum investment is 100%.")
            continue
        else:
            if w_x+w_y<0 or w_x+w_y>1:
                print("Remember, the total weight of the portfolio is 100%. The weights should add to 1.")
                continue
            else:
                r_p=r_x*w_x+r_y*w_y
                r_p=r_p*100
                print("The total return on your portoflio is %.2f"%r_p +"%")
                break
\$\endgroup\$
1
  • \$\begingroup\$ If you have more than a few stocks, then store the stock and weights in a data file of some kind. For example, a simple text file with each line containing a stock symbol and it's weight would be easy to process. A CSV file can be processed using the csv module in the standard library. A CSV file can be created using a text editor or saved from Excel. Excel files can also be read directly, but it is a bit more involved and requires a 3rd-party library. \$\endgroup\$
    – RootTwo
    Commented Jun 19, 2020 at 19:27

1 Answer 1

1
\$\begingroup\$

You should write a function that takes care of asking the user for input. It should be able to assign a type and validate the input. This way you can continue asking for a valid input for each of the variables independently. It also allows you to validate the second variable using the value of the first.

def ask_user(message, type_=str, valid=lambda x: True, wrong_type="Wrong type!", invalid="Invalid!"):
    while True:
        try:
            x = type_(input(message))
        except (TypeError, ValueError):
            print(wrong_type)
            continue
        if valid(x):
            return x
        else:
            print(invalid)

Which you can use like this:

if __name__ == "__main__":
    r_x = 0.15
    r_y = 0.21
    w_x = ask_user("Enter the weight of stock x in your portfolio: ",
                   type_=float,
                   wrong_type="Please enter the weight using numbers",
                   valid=lambda w_x: 0 <= w_x <= 1,
                   invalid="Please enter the number as a decimal. Remember, your maximum investment is 100%.")
    w_y = ask_user("Enter the weight of stock y in your portoflio: ",
                   type_=float,
                   wrong_type="Please enter the weight using numbers",
                   valid=lambda w_y: 0 <= w_x + w_y <= 1,
                   invalid="Remember, the total weight of the portfolio is 100%. The weights should add to 1.")
    r_p = r_x * w_x + r_y * w_y
    print(f"The total return on your portoflio is {r_p:.2%}")

I also used the newer f-strings for string formatting, with the % format code, which takes care of that multiplication by one hundred. I also added a if __name__ == "__main__": guard to allow importing from this script without the code being run.

Python has an official style-guide, PEP8. It recommends using spaces around = when using it for assignment, but not when using it for keyword arguments. Similarly, operators like + and * should generally also be surrounded by spaces.


If you want to be able to input n weights, I would add another function:

def ask_n_weights(message, n, type_=str, wrong_type="Wrong type!", invalid="Invalid!"):
    weights = []
    for i in range(1, n + 1):
        weights.append(ask_user(message.format(i),
                                type_=type_,
                                wrong_type=wrong_type,
                                valid=lambda w: 0 <= w + sum(weights) <= 1,
                                invalid=invalid))
    return weights

This uses the fact that sum([]) = 0 to also be correct for the first variable. In order not to loose the ability to know which weight you are entering I made it so that message should have one placeholder:

ask_n_weights("Enter weight {}: ", 4, type_=float)
# Enter weight 1: 0.25
# Enter weight 2: 0.25
# Enter weight 3: 0.25
# Enter weight 4: 0.25
# [0.25, 0.25, 0.25, 0.25]

You could also take an iterable of variable names instead of n.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Thank you! I have a lot of learning to do... The valid/invalid approach seems much more appropriate. Still not there with defining functions so a bit of learning and building on what you have given me will be great. thanks for your time :) \$\endgroup\$
    – BAMIR
    Commented Jun 20, 2020 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.