Optimize your Investments using Math and Python | by Andrew Hershy | Towards Data Science

During the MBA, we learned all about predictive modeling techniques using Excel and University of Waikato’s free software, WEKA. We learned the foundational concepts but never ventured into the hard skills required for advanced calculation. After some time studying python, I thought it would be fun to rework one of my linear optimization projects I originally did in Excel’s solver. The goal of this article is to recreate the project in python’s PuLP, share what I learn along the way, and compare python’s results to Excel’s.
The real world benefits /applications of linear optimization are endless. I would highly recommend following along closely, at least on a conceptual level, and making an effort to learn this skill if you are not already familiar with it.
What is Linear Optimization?The Assignment / ReviewData Upload and CleanLinear Optimization using PuLP
What is Linear Optimization?
The Assignment / Review
Data Upload and Clean
Linear Optimization using PuLP
According to Wikipedia, linear programming is “a method to achieve the best outcome (such as maximum profit or lowest cost) in a mathematical model whose requirements are represented by linear relationships.” These lecture notes from Carnegie Mellon University were very helpful in my own understanding of the topic.
In my own words, I would describe it as being a way to solve minimum / maximum solutions for a particular variable (decision variable), intertwined with other linear variables, to an extent that it would be very difficult to solve the problem with a pen and paper.
This is a linear optimization problem with regard to risk and return of a portfolio. Our objective is to minimize portfolio risk while simultaneously satisfying 5 constraints:
The sum of the investments will be $100,000
The sum of the investments will be $100,000
2. The portfolio has an annual return of at least 7.5%
3. At least 50% of the investments are A-rated
4. At least 40% of the investments are immediately liquid
5. No more than $30,000 are in savings accounts and certificates of deposit
The detailed instructions are below:
To review the process in solving a linear optimization problem, there are 3 steps:
Decision Variables: Here, there are 8 decision variables. They are our investment options.Objective Function: We want to minimize the risk for the 8 investments. Below are the investments multiplied by their respective risk coefficients.
Decision Variables: Here, there are 8 decision variables. They are our investment options.
Objective Function: We want to minimize the risk for the 8 investments. Below are the investments multiplied by their respective risk coefficients.
3. Constraints: Lastly, we want to define exactly what our constraints are. These are algebraically expressed below in the same order as we listed the constraints previously:
In case you are confused about the “7,500” in constraint #2, that would be the 7.5% annual return we are looking for multiplied by our $100,000 investment.
Now that we have the problem set up, let’s upload the data into pandas and import pulp:
from pulp import *import pandas as pddf = pd.read_excel(r"C:\Users\Andrew\Desktop\Fin_optimization.xlsx")df
Looking good. There are a few formatting changes that must be made in order to move forward, however.
Turn the “Liquidity” and “Ratings” columns into binary values. This is in regard to constraints #3 and #4. The relevant string values in these columns are “Immediate” for Liquidity and “A” for Rating. Distinguishing these string values from the others is necessary for further calculation.Create a new binary column for Investment Type. Constraint #5 focuses on the savings and CD investment types, so distinguishing them from the other investment types will help later.Create a column of all 1’s for Amt_invested. This will be useful for constraint #1: the $100,000 total portfolio constraint.
Turn the “Liquidity” and “Ratings” columns into binary values. This is in regard to constraints #3 and #4. The relevant string values in these columns are “Immediate” for Liquidity and “A” for Rating. Distinguishing these string values from the others is necessary for further calculation.
Create a new binary column for Investment Type. Constraint #5 focuses on the savings and CD investment types, so distinguishing them from the other investment types will help later.
Create a column of all 1’s for Amt_invested. This will be useful for constraint #1: the $100,000 total portfolio constraint.
#1adf['Liquidity'] = (df['Liquidity']=='Immediate')df['Liquidity'] = df['Liquidity'].astype(int)#1bdf['Rating'] = (df['Rating']=='A')df['Rating']= df['Rating'].astype(int)#2savecd = [1,1,0,0,0,0,0,0]df['Saving&CD'] = savecd#3amt_invested = [1]*8df['Amt_Invested'] = amt_investeddf
Perfect. Let’s move on.
The first step using PuLP is to define the problem. The code below simply defines our problem as minimization (with regard to risk)and gives it the title, “Portfolio_Opt”. We will add more to this ‘prob’ variable later.
prob = LpProblem("Portfolio_Opt",LpMinimize)
Next we will create a list of our decision variables (investments options). Then we will use that list to create dictionaries for each feature:
#Create a list of the investment itemsinv_items = list(df['Potential Investment'])#Create a dictionary of risks for all inv itemsrisks = dict(zip(inv_items,df['Risk']))#Create a dictionary of returns for all inv itemsreturns = dict(zip(inv_items,df['Expected Return']))#Create dictionary for ratings of inv itemsratings = dict(zip(inv_items,df['Rating']))#Create a dictionary for liquidity for all inv itemsliquidity = dict(zip(inv_items,df['Liquidity']))#Create a dictionary for savecd for inve itemssavecd = dict(zip(inv_items,df['Saving&CD']))#Create a dictionary for amt as being all 1'samt = dict(zip(inv_items,df['Amt_Invested']))risks
Next, we are defining our decision variables as investments and are adding a few parameters to it,
Name: To label our decision variables
Lowbound = 0: To make sure there is no negative money in our solution
Continuous: Because we are dealing with cents to the dollar.
inv_vars = LpVariable.dicts("Potential Investment",inv_items,lowBound=0,cat='Continuous')
Finally, we add the modified decision variable to our problem variable we made earlier and additionally enter the constraints. We are iterating over dictionaries using “for loops” for each investment item.
#Setting the Decision Variablesprob += lpSum([risks[i]*inv_vars[i] for i in inv_items])#Constraint #1:prob += lpSum([amt[f] * inv_vars[f] for f in inv_items]) == 100000, "Investments"Constraint #2prob += lpSum([returns[f] * inv_vars[f] for f in inv_items]) >= 7500, "Returns"Constraint #3prob += lpSum([ratings[f] * inv_vars[f] for f in inv_items]) >= 50000, "Ratings"Constraint #4prob += lpSum([liquidity[f] * inv_vars[f] for f in inv_items]) >= 40000, "Liquidity"Constraint #5prob += lpSum([savecd[f] * inv_vars[f] for f in inv_items]) <= 30000, "Save and CD"prob
Below is the problem:
Result:
prob.writeLP("Portfolio_Opt.lp")print("The optimal portfolio consists of\n"+"-"*110)for v in prob.variables():    if v.varValue>0:        print(v.name, "=", v.varValue)
This is exactly the same outcome Excel’s solver gave.
Refer to my Github to see the full notebook file.
Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without seeking professional advice. See our Reader Terms for details.
Please subscribe if you found this helpful. If you enjoy my content, below are some projects I’ve worked on:
Uber Reviews Text Analysis
Excel vs SQL: A Conceptual Comparison
Simple Linear vs Polynomial Regression
Is Random Forest better than Logistic Regression? (a comparison)
Gini Index vs Information Entropy
Predicting Cancer with Logistic Regression in Python
Bivariate Logistic Regression Example (python)
Calculating R-squared from scratch (using python)