JuPOT - Tutorial


In [90]:
push!(LOAD_PATH, "$(homedir())/desktop/financial-opt-tools/src")

using JuPOT
# Generate synthetic data sets for Demonstration
############
# Assets
############
n = 10 # No. Of Assets
returns = rand(n)*0.4
covariance = let
    S = randn(n, n)
    S'S + eye(n)
end
names = [randstring(3) for i in 1:n] # List of asset names

# Assets data structure containing, names, expected returns, covarariance
assets = AssetsCollection(names, returns, covariance)


Out[90]:
10x2 DataFrames.DataFrame
| Row | A     | B         |
|-----|-------|-----------|
| 1   | "fqU" | 0.145521  |
| 2   | "nbG" | 0.0794849 |
| 3   | "yVw" | 0.205992  |
| 4   | "nlr" | 0.117006  |
| 5   | "eZU" | 0.230518  |
| 6   | "NO1" | 0.0615766 |
| 7   | "Bnh" | 0.292437  |
| 8   | "M22" | 0.235276  |
| 9   | "mUe" | 0.0722919 |
| 10  | "aZe" | 0.0120013 |

Simple MVO

\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ \end{align}

In [2]:
######################################################
################### SIMPLE MVO #######################
######################################################

# Example 1: Basic
target_return = 0.2
# refer to model definition for keyword arguments, etc.
mvo = SimpleMVO(assets, target_return; short_sale=false)


Out[2]:
 Sense: Min 

 Variables: 
w[1:10] >= 0

 Objective Function: 
  dot(w,10x10 Array{Float64,2}:
 23.7016     9.98336   -0.553158  …   0.733098  -0.814721  -0.56417 
  9.98336   17.9551     2.96608      -0.121899   0.172077   1.30404 
 -0.553158   2.96608   10.1192       -3.80496    0.209278   0.126627
 -0.570444   1.48801   -0.296499      1.93211   -1.10089    1.20087 
  2.06455   -0.231737  -2.93694      -1.33384   -0.421206   0.919909
  3.19321   -1.29136    4.48853   …  -0.792987   1.35509   -0.088859
 -2.3449     3.35075    0.865592     -0.983744   2.56856    1.105   
  0.733098  -0.121899  -3.80496       5.67463   -0.1951    -0.615585
 -0.814721   0.172077   0.209278     -0.1951     8.57526   -2.73014 
 -0.56417    1.30404    0.126627     -0.615585  -2.73014    6.77963  * w) 

 Constraints: 
0x2 DataFrames.DataFrame


2x2 DataFrames.DataFrame
| Row | Default   |
|-----|-----------|
| 1   | "default" |
| 2   | "default" |

| Row | Constraint                                                                                                                                                                                                              |
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1   | :(dot([0.5479590591553458,0.8124768275985059,0.38122815802239063,0.8908056131630402,0.21612177093254403,0.021889817591750127,0.18684295493730074,0.7584938923953053,0.23944112136715145,0.7859992194071441],w) ≥ 0.2) |
| 2   | :(dot(ones(10),w) == 1)                                                                                                                                                                                                 |


 Assets: 
 10x2 DataFrames.DataFrame
| Row | A     | B         |
|-----|-------|-----------|
| 1   | "xN2" | 0.547959  |
| 2   | "7NE" | 0.812477  |
| 3   | "k3A" | 0.381228  |
| 4   | "ji3" | 0.890806  |
| 5   | "TRc" | 0.216122  |
| 6   | "vy1" | 0.0218898 |
| 7   | "Hqy" | 0.186843  |
| 8   | "DOO" | 0.758494  |
| 9   | "Gi9" | 0.239441  |
| 10  | "lyg" | 0.785999  | 

In [3]:
optimize(mvo)


Out[3]:
10-element Array{Float64,1}:
 0.00709497 
 5.81211e-14
 0.221814   
 5.78534e-10
 0.235785   
 6.99527e-14
 0.0849516  
 0.310166   
 0.066205   
 0.0739836  

Constraints

\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ & && I_{tech}^\top w \leq 0.3 \\ & && I_{fin}^\top w \leq 0.5 \\ \end{align}

In [63]:
##################################
# Example 2: Adding a Constraint #
##################################
function genTechIndicator()
    [0,0,1,1,0,1,0,1,1,0]
end

# Adding a simple weight constraint
constraints = Dict((:techClassWeightConstraint => :(dot(w,tech) <= tech_thresh)),
                    (:finClassWeightConstraint => :(dot(w,fin) <= fin_thresh)))

parameters = Dict(:tech=>genTechIndicator(), 
                  :tech_thresh => 0.3,
                  :fin=> [1,1,0,0,1,0,1,0,0,0],
                  :fin_thresh => 0.5)
# refer to model definition for keyword arguments, etc.
mvo = SimpleMVO(assets, target_return, constraints; short_sale=false)
w = optimize(mvo, parameters)


Out[63]:
10-element Array{Float64,1}:
 0.0499577  
 4.4887e-12 
 0.114043   
 0.000115883
 0.256528   
 5.46294e-12
 0.193514   
 0.171354   
 0.0144872  
 0.2        

In [64]:
# Constraint Checks
print("Tech Class Constraint: \n")
techClassWeight = dot(w, parameters[:tech])
print(techClassWeight, ", ", techClassWeight <= parameters[:tech_thresh])

print("\n")

print("Fin Class Constraint: \n")
finClassWeight = dot(w, parameters[:fin])
print(finClassWeight, ", ", finClassWeight <= parameters[:fin_thresh])


Tech Class Constraint: 
0.29999999999897375, true
Fin Class Constraint: 
0.49999999999262246, true

Updated Constraint

\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ & && I_{tech}^\top w \leq 0.04 \\ & && I_{fin}^\top w \leq 0.5 \\ \end{align}

In [65]:
####################################################### 
# Example 3: Changing a Constraint's parameter values #
#######################################################

# Changing values of an entered constraint
parameters[:tech_thresh] = 0.04

# refer to model definition for keyword arguments, etc.
w = optimize(mvo, parameters)


Out[65]:
10-element Array{Float64,1}:
 0.0854787  
 5.03612e-11
 1.24508e-10
 4.85935e-11
 0.204448   
 1.53004e-11
 0.210073   
 0.0122023  
 0.0277977  
 0.46       

In [66]:
# Constraint Checks
print("Tech Class Constraint: \n")
techClassWeight = dot(w, parameters[:tech])
print(techClassWeight, ", ", techClassWeight <= parameters[:tech_thresh])

print("\n")

print("Fin Class Constraint: \n")
finClassWeight = dot(w, parameters[:fin])
print(finClassWeight, ", ", finClassWeight <= parameters[:fin_thresh])


Tech Class Constraint: 
0.03999999999938704, true
Fin Class Constraint: 
0.4999999999983238, true

Constraints

New Model

\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ & && I_{fin}^\top w \leq 0.5 \\ \end{align}

In [67]:
####################################
# Example 4: Deleting a Constraint #
####################################

# Removing a previously defined constraint
delete!(constraints, :techClassWeightConstraint)

# refer to model definition for keyword arguments, etc.
mvo = SimpleMVO(assets, target_return, constraints; short_sale=false)
w = optimize(mvo, parameters)


Out[67]:
10-element Array{Float64,1}:
 0.00709497 
 8.75224e-14
 0.221814   
 1.10185e-9 
 0.235785   
 1.04763e-13
 0.0849516  
 0.310166   
 0.066205   
 0.0739836  

In [68]:
#Display Current Constraint Container
constraints


Out[68]:
Dict{Symbol,Expr} with 1 entry:
  :finClassWeightConstrai… => :(dot(w,fin) <= fin_thresh)

In [69]:
# Constraint Checks
print("Tech Class Constraint: \n")
techClassWeight = dot(w, parameters[:tech])
print(techClassWeight, ", ", techClassWeight <= parameters[:tech_thresh])

print("\n")

print("Fin Class Constraint: \n")
finClassWeight = dot(w, parameters[:fin])
print(finClassWeight, ", ", finClassWeight <= parameters[:fin_thresh])


Tech Class Constraint: 
0.5981848381137542, false
Fin Class Constraint: 
0.3278316105611047, true
\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ & && I_{fin}^\top w \leq 0.5 \\ & && {w_i} \leq 0.5 && \forall i \in \{1, 2, 3\} \\ & && {w_i} \leq 0.01 \text{ } && \forall i \in \{4, 5, 6\}\\ \end{align}

In [71]:
##########################################
# Example 5: Adding multiple Constraints #
##########################################

# Adding a multiple weight constraints
# Adding a simple weight constraint
assetClassThresholds = [0.5, 0.5, 0.5, 0.01, 0.01, 0.01]
assetsWeightConstraints = [symbol("assetWeightConstraint$i") => :(w[$i] <= $(assetClassThresholds[i])) for i=1:6]


# Different constraint sets can be merged to form new ones
new_constraints = merge(constraints, assetsWeightConstraints)

# refer to model definition for keyword arguments, etc.
mvo = SimpleMVO(assets, target_return, new_constraints; short_sale=false)
w = optimize(mvo, parameters)


Out[71]:
10-element Array{Float64,1}:
 0.046634   
 3.84734e-11
 0.191747   
 0.01       
 0.01       
 0.00735835 
 0.0797681  
 0.312535   
 0.145346   
 0.196611   

In [73]:
print("Fin Class Constraint: \n")
finClassWeight = dot(w, parameters[:fin])
print(finClassWeight, ", ", finClassWeight <= parameters[:fin_thresh])

print("\n")

print("Weights 1 - 3 Constraint: \n")
for i=1:3
    print(w[i], ", ", w[i] <= 0.5)
    print("\n")
end

print("Weights 4 - 6 Constraint: \n")
for i=4:6
    print(w[i], ", ", w[i] <= 0.01)
    print("\n")
end


Fin Class Constraint: 
0.13640211660280685, true
Weights 1 - 3 Constraint: 
0.046634024632747455, true
3.847338889024689e-11, true
0.19174749057889884, true
Weights 4 - 6 Constraint: 
0.009999999980661997, true
0.009999999994074223, true
0.007358350255935825, true

In [74]:
#####################################
# Example 6: Using Different Assets #
#####################################

############
# Asset #2 #
############
n = 10 # No. Of Assets
returns_new = rand(n)
covariance_new = let
    S = randn(n, n)
    S'S + eye(n)
end
names_new = [randstring(3) for i in 1:n]
# Assets data structure containing, names, expected returns, covarariance
assets_new = AssetsCollection(names_new, returns_new, covariance_new)


# Using the same previously defined constraints we can run the model on a different set of assets effortlessly
mvo = SimpleMVO(assets_new, target_return; short_sale=false)
optimize(mvo, parameters)


Out[74]:
10-element Array{Float64,1}:
 1.35231e-11
 0.294902   
 1.10843e-11
 1.90023e-8 
 0.185996   
 2.80191e-11
 0.294275   
 7.3748e-10 
 0.0180918  
 0.206734   

In [75]:
# Forgot what constraints and parameters were defined for initial constraints? No Problem!
constraints # Prints the constraints


Out[75]:
Dict{Symbol,Expr} with 1 entry:
  :finClassWeightConstrai… => :(dot(w,fin) <= fin_thresh)

In [76]:
parameters # prints the parameters


Out[76]:
Dict{Symbol,Any} with 4 entries:
  :tech        => [0,0,1,1,0,1,0,1,1,0]
  :tech_thresh => 0.04
  :fin_thresh  => 0.5
  :fin         => [1,1,0,0,1,0,1,0,0,0]

In [78]:
# It's good practice to remove unnecessary parameters
delete!(parameters, :tech)
delete!(parameters, :tech_thresh)

parameters


Out[78]:
Dict{Symbol,Any} with 2 entries:
  :fin_thresh => 0.5
  :fin        => [1,1,0,0,1,0,1,0,0,0]

Robust MVO

\begin{align} &\text{minimize} && w^\top\Sigma w \\ &\text{subject to} && \big\lVert \Theta^{\frac{1}{2}}w \big\rVert \leq \epsilon \\ & && \mu^\top w\geq r_{\min} \\ & && \mathbf{1}^\top w = 1 \\ & && w \succeq 0 \\ \end{align}

In [ ]:
##############################
#  Example 7 Using Robust MVO#
##############################

# refer to model definition for keyword arguments, etc
# If no uncertainty matrix is entered the model defaults
# to the ellipse whose axes are proportional to the 
# individual variances of each asset

rmvo = RobustMVO(assets, target_return; short_sale=true)
optimize(rmvo, parameters)

In [92]:
#################################################################
#  Example 8 Creating Custom Functions (e.g efficient frontier) #
#################################################################
n = 20
variance = Array(Float32,n)
returns = Array(Float32,n)
target_returns = linspace(0,0.4,20)
for i in 1:n
    target_ret = target_returns[i]
    mvo = SimpleMVO(assets, target_ret; short_sale=true)
    w = optimize(mvo, parameters)
    variance[i] = mvo.objVal
    returns[i] = dot(w, JuPOT.getReturns(assets))
end

In [93]:
variance


Out[93]:
20-element Array{Float32,1}:
 0.806573
 0.806573
 0.806573
 0.806573
 0.806573
 0.806573
 0.806573
 0.806573
 0.806573
 0.808813
 0.833964
 0.887046
 0.968058
 1.077   
 1.21387 
 1.37868 
 1.57141 
 1.79208 
 2.04067 
 2.3172  

In [94]:
returns


Out[94]:
20-element Array{Float32,1}:
 0.181042
 0.181042
 0.181042
 0.181042
 0.181042
 0.181042
 0.181042
 0.181042
 0.181042
 0.189474
 0.210526
 0.231579
 0.252632
 0.273684
 0.294737
 0.315789
 0.336842
 0.357895
 0.378947
 0.4     

In [83]:
mean(JuPOT.getReturns(assets))


Out[83]:
0.4841258434570479

In [89]:
mean(rand(10)*0.4)


Out[89]:
0.15125274010964593

In [ ]: