How to solve a system of equations and constraints for portfolio optimization?

痴心易碎 提交于 2021-02-19 08:00:07

问题


I have a DataFrame as follows :

Name  Volatility    Return
a      0.0243        0.212
b      0.0321        0.431
c      0.0323        0.443
d      0.0391        0.2123
e      0.0433        0.3123

I'd like to have a Volatility of 0.035 and the maximized Return for that volatility.

That is, I'd like, in a new Df the Name and the percentage of that asset that will be in my portfolio that gives the maximum Return for a Volatility equals to 0.035.

Therefore, I need to solve a system of equations with multiple conditions, to obtain the best solution (HighestReturn) for a fixed outcome (Volatility == 0.035).

The conditions are:

  • Each asset has a weight between 0 and 1.
  • The sum of the weights is 1.
  • The sum of the weights times the volatility of each asset is the "Desired Volatility".
  • The sum of the weights times the return of each asset is the "Total Return". This should be maximized.

回答1:


Here is an approach using Z3Py, an open source SAT/SMT solver. In a SAT/SMT solver you can write your code just as a list of conditions, and the program finds an optimal solution (or just a solution that satisfies all the conditions when Z3 is used as solver).

Originally SAT solvers only worked with pure boolean expressions, but modern SAT/SMT solvers also allow for fixed-bit and unlimited integers, fractions, reals and even functions as central variable.

To write the given equations into Z3, they are converted quite literally into Z3 expressions. The code below comments each of the steps.

import pandas as pd
from z3 import *

DesiredVolatility = 0.035
df = pd.DataFrame(columns=['Name', 'Volatility', 'Return'],
                  data=[['a', 0.0243, 0.212],
                        ['b', 0.0321, 0.431],
                        ['c', 0.0323, 0.443],
                        ['d', 0.0391, 0.2123],
                        ['e', 0.0433, 0.3123]])

# create a Z3 instance to optimize something
s = Optimize()
# the weight of each asset, as a Z3 variable
W = [Real(row.Name) for row in df.itertuples()]
# the total volatility
TotVol = Real('TotVol')
# the total return, to be maximized
TotReturn = Real('TotReturn')

# weights between 0 and 1, and sum to 1
s.add(And([And(w >= 0, w <= 1) for w in W]))
s.add(Sum([w for w in W]) == 1)
# the total return is calculated as the weighted sum of the asset returns
s.add(TotReturn == Sum([w * row.Return for w, row in zip(W, df.itertuples())]))
# the volatility is calculated as the weighted sum of the asset volatility
s.add(TotVol == Sum([w * row.Volatility for w, row in zip(W, df.itertuples())]))
# the volatility should be equal to the desired volatility
s.add(TotVol == DesiredVolatility)
# we're maximizing the total return
h1 = s.maximize(TotReturn)
# we ask Z3 to do its magick
res = s.check()
# we check the result, hoping for 'sat': all conditions satisfied, a maximum is found
if res == sat:
    s.upper(h1)
    m = s.model()
    #for w in W:
    #    print(f'asset {w}): {m[w]} = {m[w].numerator_as_long() / m[w] .denominator_as_long() : .6f}')
    # output the total return
    print(f'Total Return: {m[TotReturn]} = {m[TotReturn].numerator_as_long() / m[TotReturn] .denominator_as_long() :.6f}')
    # get the proportions out of the Z3 model
    proportions = [m[w].numerator_as_long() / m[w] .denominator_as_long() for w in W]
    # create a dataframe with the result
    df_result = pd.DataFrame({'Name': df.Name, 'Proportion': proportions})
    print(df_result)
else:
    print("No satisfiable solution found")

Result:

Total Return: 452011/1100000 = 0.410919
  Name  Proportion
0    a    0.000000
1    b    0.000000
2    c    0.754545
3    d    0.000000
4    e    0.245455

You can easily add additional constraints, for example "no asset can have more than 30% of the total":

# change 
s.add(And([And(w >= 0, w <= 1) for w in W]))`
# to
s.add(And([And(w >= 0, w <= 0.3) for w in W]))`

Which would result in:

Total Return: 558101/1480000 = 0.377095
  Name  Proportion
0    a    0.082432
1    b    0.300000
2    c    0.300000
3    d    0.017568
4    e    0.300000


来源:https://stackoverflow.com/questions/59790285/how-to-solve-a-system-of-equations-and-constraints-for-portfolio-optimization

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!