-1

im trying to replicate a certain code from yuxing Yan's python for finance. I am at a road block because I am getting very high minimized figures(in this case stock weights, which ca be both +(long) and (-short) after optimization with fmin().

can anyone help me with a fresh pair of eyes. I have seen some suggestion about avoiding passing negative or complex figures to fmin() but I can't afford to as its vital to my code

#Lets import our modules

from scipy.optimize import fmin #to minimise our negative sharpe-ratio
import numpy as np#deals with numbers python
from datetime import datetime#handles date objects
import pandas_datareader.data as pdr #to read download equity data
import pandas as pd #for reading and accessing tables etc
import scipy as sp
from scipy.stats import norm
import scipy.stats as stats
from scipy.optimize import fminbound



assets=('AAPL',
'IBM',
'GOOG',
'BP',
'XOM',
'COST',
'GS')

#start and enddate to be downloaded
startdate='2016-01-01'
enddate='2016-01-31'
rf_rate=0.0003

n=len(assets)

#_______________________________________________
#This functions takes the assets,start and end dates and 
#returns portfolio return
#__________________________________________________
def port_returns (assets,startdate,enddate): 
                    
    #We use adjusted clsoing prices of sepcified dates of assets
     #as we will only be interested in returns
    data = pdr.get_data_yahoo(assets, start=startdate, end=enddate)['Adj Close']
    
    
    #We calculate the percentage change of our returns
    #using pct_change function in python
    returns=data.pct_change()
    return  returns
def portfolio_variance(returns,weight):
       #finding the correlation of our returns by 
        #dropping the nan values and transposing 
       correlation_coefficient = np.corrcoef(returns.dropna().T)
       
       #standard deviation of our returns
       std=np.std(returns,axis=0)
       
       #initialising our variance
       port_var = 0.0
    
       #creating a nested loop to calculate our portfolio variance
       #where the variance is w12σ12 + w22σ22 + 2w1w2(Cov1,2) 
       #and correlation coefficient is given by covaraince btn two assets  divided by standard
       #multiplication of standard deviation of both assets
       for i in range(n):
           for j in range(n):
            #we calculate the variance by continuously summing up the varaince between two
            #assets using i as base loop, multiplying by std and corrcoef
               port_var += weight[i]*weight[j]*std[i]*std[j]*correlation_coefficient[i, j]

       return port_var

def sharpe_ratio(returns,weights):
    #call our variance function
       variance=portfolio_variance(returns,weights)
       avg_return=np.mean(returns,axis=0)
        #turn our returns to an array
       returns_array = np.array(avg_return)
        #Our sharpe ratio uses  expected return gotten from multiplying weights and return
        # and standard deviation gotten by square rooting our variance
        #https://en.wikipedia.org/wiki/Sharpe_ratio
       return (np.dot(weights,returns_array) - rf_rate)/np.sqrt(variance)

def negate_sharpe_ratio(weights):
       #returns=port_returns (assets,startdate,enddate)
    #creating an array with our weights by
    #summing our n-1 inserted and subtracting by 1 to make our last weight
       weights_new=np.append(weights,1-sum(weights))
    #returning a negative sharpe ratio
       return -(sharpe_ratio(returns_data,weights_new))
      
returns_data=port_returns(assets,startdate,enddate)

# for n stocks, we could only choose n-1 weights
ones_weights_array= (np.ones(n-1, dtype=float) * 1.0 )/n
weight_1 = fmin(negate_sharpe_ratio,ones_weights_array)
final_weight = np.append(weight_1, 1 - sum(weight_1))
final_sharpe_ratio = sharpe_ratio(returns_data,final_weight)
print ('Optimal weights are ')
print (final_weight)
print ('final Sharpe ratio is ')
print(final_sharpe_ratio)

1 Answers1

0

A few things are causing your code not to work as written

is assets the list of items in ticker?

shouldstartdate be set equal to begdate?

Your call to port_returns() is looking for both assets and startdate which are never defined.

Function sharpe_ratio() is looking for a variable called rf_rate which is never defined. I assume this is the risk-free rate and the value assigned to rf at the beginning of the script. So should rf be called rf_rate instead?

After changing rf to rf_rate, begdate to startdate, and setting assets = list(ticker), it appears that this will work as written

ekrall
  • 192
  • 8
  • Thanks for the reply.The code does run but I get very strange figures after the optimization.Ideally it should be a couple of decimals off but im getting figures in the thousands.I have edited the code as well, incase you want to take one more look at it. – Majeed Yougbare Dec 18 '21 at 23:11
  • When I ran it I got weights that made sense. All between zero and one. Optimal weights are [0.39138638 0.54119293 0.0674207 ] are you sure that you used the same exact code that you pasted in your question? a different value of rf_rate, for example, would make it difficult to replicate your problem – ekrall Dec 19 '21 at 02:42
  • Yes I did get these figures as well but they are no where near what's in the book.I wonder what might be the problem.In any case I have edited the assets and made it only the month of jan 2016 to demonstrate the problem I was describing earlier.I would be happy if you could expand further on why im getting those big values for weights – Majeed Yougbare Dec 19 '21 at 06:28
  • I see. A problem with the approach coded is that fmin is not performing a constrained optimization. It is just looking for the minimum of the function. For some sets of inputs you may have an unbounded problem which is why you see huge weights. You should be able to verify this If you set "full_output=True" in your call to fmin (it will now return 5 values). https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin.html. – ekrall Dec 19 '21 at 15:59
  • The return value for "warnflag" will tell you if the maximum function evaluations/iterations were reached. If you see that along with unreasonable weights, it means the program just quit after an iteration limit. You may want to try a variant of fmin that allows constraints (fmin_cobyla), or try another package that allows you to set up a constrained optimization, and reformulate your approach. For example, the sum of the weights equals one should be a constraint. The book you used may have cherry-picked an example that worked – ekrall Dec 19 '21 at 15:59