Validating initial inputs then a joint validation test

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

• 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. – RootTwo Jun 19 '20 at 19:27

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):
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.

• 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 :) – BAMIR Jun 20 '20 at 13:43