I am stuck setting up a proper objective function for the minimization of a weighted average.
Exercise: For a portfolio of fixed income securities (ie bonds) find the selection of bonds to sell that generates a minimum gain. Within that constraint, minimize the weighted average yield of the selection of bonds.
The relevant data fields are Yield, Current Face Value and Current Profit or Loss. The weighted avg yield of the selection is the sumproduct of Yield and Current Face Value, divided by sum of Current Face Value. Continuous or Binary decision variables are acceptable.
This post is similar to my issue... Optimizing A Loan Portfolio On Weighted Average Criteria
I picked up PulP quicker than Scipy but I can use whatever package is best suited for this type of problem.
import pandas as pd
from pulp import *
d = {
'Current Face Value':[173669.3, 219544.4, 253647.88, 256776.38, 264824.02, 348820.44, 415053.58, 475354.13, 643773.24, 781129.21, 866839.35, 866013.93, 974840.18, 1053465.32, 1074261.8, 1097979.35, 1112974.78, 1125646.64, 1216768.1, 1231914.2, 1423300.7, 1462152.67, 1642708.16, 1679005.31, 1625365.08, 1708252.62, 1765860.7, 1763888.76, 1828103.0, 1833238.25, 1796976.58, 1973474.26, 2027289.74, 2058402.54, 2186044.1, 2185605.23, 2222353.6, 2260944.05],
'Current Profit or Loss':[-1613.81, 6546.25, 1965.83, 431.56, 1653.4, -556.95, 3192.99, 1732.18, -870.51, 16216.16, 140.5, -11624.28, 13347.67, 37106.55, 1638.02, 11903.53, 22179.76, 16074.41, 34904.67, 12146.89, 3976.73, -4810.06, -11510.87, 44416.35, 11475.39, 24260.51, 11766.51, 14648.76, 12272.55, 12255.15, -33461.55, -18799.52, 35814.91, 41468.95, 8442.11, 41645.9, 51555.44, -3333.12],
'Yield':[2.118191, 3.381024, 3.723284, 3.567963, 3.381002, 3.554571, 1.786467, 3.585767, 2.061981, 3.395873, 1.895965, 1.41617, 2.935917, 3.140995, 3.103661, 2.157629, 2.400065, 2.231383, 2.941482, 2.172782, 2.086372, 2.128788, 2.400682, 3.497868, 2.094667, 2.667469, 2.308526, 2.513017, 2.326085, 2.306328, 2.685972, 2.001348, 2.806967, 3.659145, 2.203806, 3.201562, 2.839683, 1.462699],
}
data = pd.DataFrame(d)
data_gains = data[data['Current Profit or Loss'] > 0].reset_index(drop=True) # securities with gains
data_losses = data[data['Current Profit or Loss'] <= 0].reset_index(drop=True) # securities with losses
print('Secs w/ gains:',str(len(data_gains)),' Secs w/ losses: '+str(len(data_losses)))
prob = LpProblem('Portfolio Selection',LpMinimize)
# decision variables
decision_variables = []
for rownum, row in data_gains.iterrows():
variable = pulp.LpVariable(str('x' + str(rownum)), lowBound = 0, upBound = 1, cat= 'Continuous')
decision_variables.append(variable)
print ('Decision_variables: ' + str(len(decision_variables)))
# objective - minimize weighted avg yield of selection
wa_yield = []
for rownum, row in data_gains.iterrows():
for i, flag in enumerate(decision_variables):
if rownum == i:
wa_yield.append(row['Current Face Value']*row['Yield']*flag) # numerator of the proper objective
wa_yield = sum(wa_yield)
prob += wa_yield
# create constrains - total gain to be in this range
min_gain, max_gain = 100000, 150000
total_gain = []
for rownum, row in data_gains.iterrows():
for i, flag in enumerate(decision_variables):
if rownum == i:
total_gain.append(row['Current Profit or Loss']*flag)
total_gain = sum(total_gain)
prob += (total_gain >= min_gain)
prob += (total_gain <= max_gain)
prob.solve()
print("Status:", LpStatus[prob.status])
result = []
for v in prob.variables():
print(v.name, "=", v.varValue)
result.append(v.varValue)