# -*- coding: utf-8 -*-
#******************************************************************************
# Name of the program: PyCPTAM
# Title of the program: Python Module for Constructing Portfolios via Two
# Alternative Methods
# Version: 001
#
# This program is released under GNU General Public License, version 3.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# This software has been developed by Dr. Alan Mustafa under supervision of
# Prof. Abdulnasser Hatemi-J (Hatemi-J, 2012).
# Contacts:
# - Prof. Abdulnasser Hatemi-J: AHatemi@uaeu.ac.ae
# - Dr. Alan Mustafa: Alan.Mustafa@ieee.org
#
# The work is funded partially by a summer research grant provided by the
# UAE university, which is enormously appreciated.
#
# In case this module is used it needs to be cited as the following:
# Mustafa A. and Hatemi-J A. (2023) PyCPTAM: Python Module for Constructing
# Portfolios via Two Alternative Methods, Statistical Software Components,
# Boston College Department of Economics
#
# Date: January 2023
#
# © 2023 Dr. Alan Mustafa and Prof. Abdulnasser Hatemi-J
#
#******************************************************************************
import numpy as np
import pandas as pd
import os
from datetime import datetime
#######################################
import tkinter as tk
# import tkinter as tk2
from tkinter.filedialog import askopenfilename
from tabulate import tabulate
###############################################################################
# Start of GUI
###############################################################################
#========================= functions =========================================
class create_window_menu_UI2(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
#============= Application Title --------------------------------------
self.lblTitle = tk.Label(self, text="Construction of Portfolio via Two Alternative Methods", font=("Helvetica", 16))
self.lblTitle.grid(row=1, column=0, columnspan=5, sticky="EW")
# hr = tk.Frame(self,height=10,width=850,bg="green")
hr = tk.Frame(self,height=3,width=850,bg="green")
hr.grid(row=2, column=0, columnspan=5, sticky="NWNESWSE")
#============= Adding a Blank Row -------------------------------------
self.lblBlnkRow = tk.Label(self, text="", font=("Helvetica", 12))
self.lblBlnkRow.grid(row=16, column=0, sticky="EW")
#============= Load My Data Button ---------------------------------
self.btnSelectDataFile = tk.Button(self, text="Select the Dataset File", command=lambda: var_DatasetFile.set(os.path.split(askopenfilename())[1]), font=("Arial", 12))
self.btnSelectDataFile.grid(row=20, column=0, sticky="E")
var_DatasetFile = tk.StringVar()
self.tbx_DatasetFile = tk.Entry(self, textvariable=var_DatasetFile, font=("Helvetica", 10), state="disabled", justify="center")
self.tbx_DatasetFile.grid(row=20, column=1, sticky="EW")
#============= Adding a label for OR sign -----------------------------
self.btnLoadMydata = tk.Label(self, text="OR ", font=("Arial Narrow", 12))
self.btnLoadMydata.grid(row=20, column=2, sticky="EW")
#============= Load Sample Data Button --------------------------------
self.btnLoadSmplData = tk.Button(self, text="Load the Sample Data \nand Calculate the Portfolio", command=lambda: loadSampleData(self), font=("Arial", 12))
self.btnLoadSmplData.grid(row=20, column=3, rowspan=2, sticky="W")
#============= Calculate Proftfolio Diversification Button ---------------------------------
self.btnCalcPD = tk.Button(self, text="Calculate the Protfolio", command=lambda: LoadDataFile(var_DatasetFile,self), font=("Arial", 12))
self.btnCalcPD.grid(row=21, column=0, sticky="E")
#============= Dataset File Selection ---------------------------------
self.btnExit = tk.Button(self, text="Close", command=self.master.destroy, font=("Arial", 12))
self.btnExit.grid(row=22, column=3, sticky="W")
#============= Start of Printing Data in a Table ----------------------
self.msgOutput_lblHeader = tk.Label(self, text="", font=("Consolas", 12), anchor="w")
self.msgOutput_lblHeader .grid(row=160, column=0, columnspan=3, sticky="ew")
self.msgOutput_RunMsg2 = tk.Label(self, text="", font=("Consolas", 12), anchor="w")
self.msgOutput_RunMsg2.grid(row=165, column=0, columnspan=3, sticky="ew")
#============= ---------------
self.lblRprtDesc = tk.Message(self, text="", font=("Arial Narrow", 12), anchor="w", justify="left", bg='#f0f0f0')
self.lblRprtDesc.bind("", lambda e: self.lblRprtDesc.configure(width=e.width-5))
self.lblRprtDesc.grid(row=165, column = 3, columnspan=2, sticky="W")
#============= Printing Keys for the Data in the Table if needed ------
self.msgOutput_tblKeys = tk.Message(self, text="", font=("Consolas", 12), anchor="w", justify="left")
self.msgOutput_tblKeys.bind("", lambda e: self.msgOutput_tblKeys.configure(width=e.width-10))
self.msgOutput_tblKeys.grid(row=166, column=0, columnspan=3, sticky="ew")
#============= Output EndNote ---------------------------------
hr = tk.Frame(self,height=1,width=850,bg="green")
hr.grid(row=200, column=0, columnspan=4, sticky="NWNESWSE")
self.msgEndNote = tk.Message(self, text="", font=("Helvetica", 8, "italic"), anchor="w", justify="left", bg="#d4d4d4")
self.msgEndNote.bind("", lambda e: self.msgEndNote.configure(width=e.width-10))
self.msgEndNote.grid(row=210, column=0, columnspan=4, sticky="ew")
###############################################################################
# End of GUI #
###############################################################################
###############################################################################
################## START OF SAMPLE DATA ###############################
###############################################################################
def loadSampleData(self):
headers=["Date_of_Assets","USD-JPY","Brent Oil","DAX","Dow Jones"]
dates = ['02/01/2019', '03/01/2019', '04/01/2019', '07/01/2019', '08/01/2019', '09/01/2019', '10/01/2019', '11/01/2019', '14/01/2019', '15/01/2019', '16/01/2019',
'17/01/2019', '18/01/2019', '21/01/2019', '22/01/2019', '23/01/2019', '24/01/2019', '25/01/2019', '28/01/2019', '29/01/2019', '30/01/2019', '31/01/2019',
'01/02/2019', '04/02/2019', '05/02/2019', '06/02/2019', '07/02/2019', '08/02/2019', '11/02/2019', '12/02/2019', '13/02/2019', '14/02/2019', '15/02/2019',
'18/02/2019', '19/02/2019', '20/02/2019', '21/02/2019', '22/02/2019', '25/02/2019', '26/02/2019', '27/02/2019', '28/02/2019', '01/03/2019', '04/03/2019',
'05/03/2019', '06/03/2019', '07/03/2019', '08/03/2019', '11/03/2019', '12/03/2019', '13/03/2019', '14/03/2019', '15/03/2019', '18/03/2019', '19/03/2019',
'20/03/2019', '21/03/2019', '22/03/2019', '25/03/2019', '26/03/2019', '27/03/2019', '28/03/2019', '29/03/2019']
asset1 = [108.88, 107.67, 108.53, 108.72, 108.75, 108.17, 108.42, 108.55, 108.17, 108.67, 109.09, 109.24, 109.78, 109.67, 109.38, 109.6, 109.64, 109.55, 109.36,
109.39, 109.03, 108.88, 109.5, 109.89, 109.97, 109.97, 109.81, 109.73, 110.38, 110.48, 111, 110.48, 110.5, 110.62, 110.62, 110.86, 110.7, 110.69, 111.06, 110.58,
111, 111.39, 111.92, 111.75, 111.89, 111.77, 111.59, 111.17, 111.2, 111.36, 111.17, 111.72, 111.47, 111.42, 111.39, 110.69, 110.81, 109.92, 109.97, 110.64, 110.52,
110.64, 110.86]
asset2 = [54.91, 55.95, 57.06, 57.33, 58.72, 61.44, 61.68, 60.48, 58.99, 60.64, 61.32, 61.18, 62.7, 62.74, 61.5, 61.14, 61.09, 61.64, 59.93, 61.32, 61.65, 61.89,
62.75, 62.51, 61.98, 62.69, 61.63, 62.1, 61.51, 62.42, 63.61, 64.57, 66.25, 66.5, 66.45, 67.08, 67.07, 67.12, 64.76, 65.21, 66.39, 66.03, 65.07, 65.67, 65.86, 65.99,
66.3, 65.74, 66.58, 66.67, 67.55, 67.23, 67.16, 67.54, 67.61, 68.5, 67.86, 67.03, 67.21, 67.97, 67.83, 67.82, 68.39]
asset3 = [10580.19, 10416.66, 10767.69, 10747.81, 10803.98, 10893.32, 10921.59, 10887.46, 10855.91, 10891.79, 10931.24, 10918.62, 11205.54, 11136.2, 11090.11,
11071.54, 11130.18, 11281.79, 11210.31, 11218.83, 11181.66, 11173.1, 11180.66, 11176.58, 11367.98, 11324.72, 11022.02, 10906.78, 11014.59, 11126.08, 11167.22, 11089.79,
11299.8, 11299.2, 11309.21, 11401.97, 11423.28, 11457.7, 11505.39, 11540.79, 11487.33, 11515.64, 11601.68, 11592.66, 11620.74, 11587.63, 11517.8, 11457.84, 11543.48,
11524.17, 11572.41, 11587.47, 11685.69, 11657.06, 11788.41, 11603.89, 11549.96, 11364.17, 11346.65, 11419.48, 11419.04, 11428.16, 11526.04]
asset4 = [23346.24, 22686.22, 23433.16, 23531.35, 23787.45, 23879.12, 24001.92, 23995.95, 23909.84, 24065.59, 24207.16, 24370.1, 24706.35, 24706.35, 24404.48,
24575.62, 24553.24, 24737.2, 24528.22, 24579.96, 25014.86, 24999.67, 25063.89, 25239.37, 25411.52, 25390.3, 25169.53, 25106.33, 25053.11, 25425.76, 25543.27, 25439.39,
25883.25, 25883.25, 25891.32, 25954.44, 25850.63, 26031.81, 26091.95, 26057.98, 25985.16, 25916, 26026.32, 25819.65, 25806.63, 25673.46, 25473.23, 25450.24, 25650.88,
25554.66, 25702.89, 25709.94, 25848.87, 25914.1, 25887.38, 25745.67, 25962.51, 25502.32, 25516.83, 25657.73, 25625.59, 25717.46, 25928.68]
csvFileName = 'PD_Sample.csv' # PD: Portfolio Diversification
df = pd.DataFrame(dates)
df['Asset1'] = asset1
df['Asset2'] = asset2
df['Asset3'] = asset3
df['Asset4'] = asset4
df.columns = headers
df.to_csv(csvFileName, header=True, index=False)
calc_pd_vrar(df,self)
###############################################################################
################## END SAMPLE DATA ##################################
###############################################################################
###############################################################################
################## START OF LOADING DATA FILE ######################
###############################################################################
def LoadDataFile(fileName,self): # df: DataFrame
theFile = pd.read_csv(fileName.get(), sep = ',', decimal = ',', header=0, index_col=False)
df2 = pd.DataFrame(theFile)
headers_list = list(df2.columns.values)
for column in headers_list[1:]:
df2[column] = df2[column].astype(np.float64)
calc_pd_vrar(df2,self)
###############################################################################
################## ENDING OF LOADING DATA FILE ####################
###############################################################################
###############################################################################
# Start of Calculations: Calculation for Constructing Portfolio #
###############################################################################
def calc_pd_vrar(df,self): # df: DataFrame
x = df.iloc[:, 1:].values;
noOfCols = len(df.iloc[0, :].values);
roa = np.exp(np.diff(np.log(x), axis = 0))-1 # => roa = (b2 - b1)/b1 : Return of Asset
theCov = np.cov(roa, rowvar=False)
Er = np.average(roa, axis=0) # Er: Average Return Values
SD = np.std(roa, axis=0, ddof=1)
RAdjR = np.divide(Er, SD)
#=============== Calculating Hs&K and Cs&E =====================
# Hs: the selected columns from K
# K: calculating values from both matrices of Ev and Cov
# Cs: table of covariance
# E: selected columns from Covariance table
rows = len(theCov) - 1
cols = len(theCov[0])
Cij = np.array([[0 for i in range(cols)] for j in range(rows)], dtype=float)
Kij = np.array([[0 for i in range(cols)] for j in range(rows)], dtype=float)
for iRows in range(0,rows):
for jCols in range(0,cols):
Cij[iRows,jCols] = (2*theCov[iRows+1,jCols])-(2*theCov[iRows, jCols])
Kij[iRows,jCols] = (Er[iRows]*2*theCov[iRows+1,jCols])-(Er[iRows+1]*2*theCov[iRows, jCols])
theOnes = np.ones(rows+1)
Cij = np.vstack([Cij, theOnes])
Kij = np.vstack([Kij, theOnes])
detC = np.linalg.det(Cij)
detK = np.linalg.det(Kij)
#=============== Calculating Ws ===============================
# Es stands for 'W (weight) for MV'
# Ws stands for W (weight) for MRAR
wRows = len(Kij)
wCols = len(Kij[0])
Ei = np.array([0 for i in range(wCols)], dtype=float)
Wi = np.array([0 for i in range(wCols)], dtype=float)
Eij = np.array([[0 for i in range(wCols-1)] for j in range(wRows-1)], dtype=float)
Wij = np.array([[0 for i in range(wCols-1)] for j in range(wRows-1)], dtype=float)
WijCol = 0
for theWiCol in range(0,wCols):
for jwCols in range(0,wCols):
if theWiCol != jwCols:
for iwRows in range(0,wRows-1):
Eij[iwRows,WijCol] = Cij[iwRows, jwCols]
Wij[iwRows,WijCol] = Kij[iwRows, jwCols]
WijCol +=1
sign_p = (theWiCol + 1) + wCols; # One is added to theWiCol becuase the loop start from 0.
sign_ = (-1)**sign_p;
Ei[theWiCol] = sign_ * (np.linalg.det(Eij) / detC)
Wi[theWiCol] = sign_ * (np.linalg.det(Wij) / detK)
WijCol =0
PD_MV_AR = np.sum(np.array(Er) * np.array(Ei))
PD_MRAR_AR = np.sum(np.array(Er) * np.array(Wi))
noOfAssets = noOfCols - 1;
sigma_p_ = 0
sigma_p_c_ = 0
for sd_i in range(0,noOfAssets):
for sd_j in range(0,noOfAssets):
w_sd_i = Ei[sd_i];
w_sd_j = Ei[sd_j];
w_D_sd_i = Wi[sd_i];
w_D_sd_j = Wi[sd_j];
sigma_p_ = sigma_p_ + (w_sd_i * w_sd_j * theCov[sd_i, sd_j]);
sigma_p_c_ = sigma_p_c_ + (w_D_sd_i * w_D_sd_j * theCov[sd_i, sd_j]);
PD_MV_sigma_p = np.sqrt(sigma_p_);
PD_MRAR_sigma_p_c = np.sqrt(sigma_p_c_);
PD_MV_RAR = PD_MV_AR / PD_MV_sigma_p;
PD_MRAR_RAR = PD_MRAR_AR / PD_MRAR_sigma_p_c;
MV = [PD_MV_AR,PD_MV_sigma_p,PD_MV_RAR,'','']
MRAR = [PD_MRAR_AR,PD_MRAR_sigma_p_c,PD_MRAR_RAR,'','']
Asset_df = []
Er_df = []
SD_df = []
RAdjR_df = []
Ei_df = []
Wi_df = []
PD_MV = []
PD_MRAR = []
Asset_df = 'Assets'
Er_lbl = 'E(r)'
lbl_Note = 'Note'
SD_df = 'Standard Deviasion'
RAdjR_df = 'Risk Adjusted Return'
Ei_df = 'w for MV'
Wi_df = 'w for MRAR'
PD_MV = 'Portfolio - Minimum Variance (MV)'
PD_MRAR = 'Portfolio - Maximum Risk Adjusted Return (MRAR)'
Asset_df = np.append(Asset_df,df.columns[1:])
Er_df = np.append(Er_lbl,Er)
SD_df = np.append(SD_df,SD)
RAdjR_df = np.append(RAdjR_df,RAdjR)
Ei_df = np.append(Ei_df,Ei)
Wi_df = np.append(Wi_df,Wi)
PD_MV = np.append(PD_MV,MV)
PD_MRAR = np.append(PD_MRAR,MRAR)
estResult=[]
estResult = Asset_df
estResult = np.vstack((estResult,Er_df))
estResult = np.vstack((estResult,SD_df))
estResult = np.vstack((estResult,RAdjR_df))
estResult = np.vstack((estResult,Ei_df))
estResult = np.vstack((estResult,Wi_df))
estResult2 = estResult.transpose()
estResult4 = np.vstack((estResult2,PD_MV))
estResult5 = np.vstack((estResult4,PD_MRAR))
# "ebs: Estimated Budget Shares" for the summary table
esb_w_MV = "w_MV"
esb_w_MRAR = "w_MRAR"
wi_MV_1 = np.array(np.round(Ei, decimals = 5))
wi_MRAR_1 = np.array(np.round(Wi, decimals = 5))
wi_MV_1 = np.append(esb_w_MV,wi_MV_1)
wi_MRAR_1 = np.append(esb_w_MRAR,wi_MRAR_1)
Smry0 = np.vstack((Asset_df,wi_MV_1))
Smry1 = np.vstack((Smry0,wi_MRAR_1))
Smry = Smry1.transpose()
Er_1 = np.array(np.sign(Er)).astype(object)
Er_1[Er_1 == -1] = '*'
Er_1[Er_1 == 1] = ''
if (np.any(Er_1[:] == '*')):
Er_df_onGUI = np.append(lbl_Note,Er_1)
Smry_onGUI_1 = np.vstack((Smry1,Er_df_onGUI))
Smry_onGUI = Smry_onGUI_1.transpose()
infoText = """Two copies of the report of formats (*.csv) and (*.txt) with additional details have been added to the same folder as the program resides in [PyCPTAM_rprt_YMD_Time.csv/txt] formats!"""
infoText_bg = '#cce7c9'
keys_ = '\nDenotations:\n'
keys_ += 'w: Weights as the Budget Shares\n'
keys_ += 'MV: Minimum Variance method\n'
keys_ += 'MRAR: Maximum Risk Adjusted Return method\n'
keys_ += '*: This means that the value for the Average Return of the asset is negative and it should be removed from the portfolio'
else:
Smry_onGUI = Smry1.transpose()
infoText = ""
infoText_bg = '#f0f0f0'
keys_ = '\nDenotations:\n'
keys_ += 'w: Weights as the Budget Shares\n'
keys_ += 'MV: Minimum Variance\n'
keys_ += 'MRAR: Maximum Risk Adjusted Return\n'
fileName,dt_string2 = create_rprt_file(estResult5,Smry,Smry_onGUI,infoText,infoText_bg,keys_,self);
EndNote(fileName,self);
return;
################################################
def create_rprt_file(theDF,Smry,Smry_onGUI,infoText,infoText_bg,keys_,self): # theDF: New DataFrame
#=============== creating Output Report File ==============================
now = datetime.now()
dt_string2 = now.strftime("%Y%m%d_%H%M%S")
txtFileName = 'PyCPTAM_rprt' + '_' + dt_string2 + '.txt'
output_rprt_file = open(txtFileName,'w')
output_rprt_file.write('#############################################################################\n')
output_rprt_file.write('# #\n')
output_rprt_file.write('# PORTFOLIO DIVERSIFICATION RESULTS VIA TWO ALTERNATIVE METHODS #\n')
output_rprt_file.write('# #\n')
output_rprt_file.write('#############################################################################\n')
output_rprt_file.write(tabulate(theDF))
output_rprt_file.write('\n')
output_rprt_file.close
############################
csvFileName = 'PyCPTAM_rprt' + '_' + dt_string2 + '.csv'
df = pd.DataFrame(theDF)
df.to_csv(csvFileName, header=False, index=False)
############################
self.msgOutput_lblHeader["text"] = "Estimated Budget Shares via Two Alternative Methods"
self.msgOutput_RunMsg2["text"] = tabulate(Smry_onGUI, tablefmt='psql');
self.lblRprtDesc["text"] = infoText
self.lblRprtDesc["bg"] = infoText_bg
self.msgOutput_tblKeys["text"] = keys_
return(txtFileName,dt_string2);
################################################
def EndNote(fileName,self):
output_rprt_file = open(fileName,'a');
output_rprt_file.write('\n');
output_rprt_file.write('==========================================================================================\n');
output_rprt_file.write('| REFERENCES |\n');
output_rprt_file.write('| - Markowitz H. (1952) Portfolio Selection, Journal of Finance, vol. 7(1), 77-91. |\n');
output_rprt_file.write('| - Hatemi-J A. and El-Khatib Y. (2015) Portfolio Selection: An Alternative Approach, |\n');
output_rprt_file.write('| Economics Letters, vol. 135, 141-143. |\n');
output_rprt_file.write('| - Hatemi-J A., Hajji, M.A. and El-Khatib Y. (2022) Exact solution for the portfolio |\n');
output_rprt_file.write('| diversification problem based on maximizing the risk adjusted return. Research in |\n');
output_rprt_file.write('| International Business and Finance, 59, 101548. |\n');
output_rprt_file.write('| |\n');
output_rprt_file.write('==========================================================================================\n');
output_rprt_file.write('\n');
output_rprt_file.write('==========================================================================================\n');
output_rprt_file.write('| ADDITIONAL INFORMATION |\n');
output_rprt_file.write('| |\n');
output_rprt_file.write('| This program code is the copyright of the authors. Applications are allowed |\n');
output_rprt_file.write('| only if proper reference and acknowledgments are |\n');
output_rprt_file.write('| provided. For non-Commercial applications only. No performance guarantee is |\n');
output_rprt_file.write('| made. Bug reports are welcome. If this code is used for research or in any |\n');
output_rprt_file.write('| other code, proper attribution needs to be included. |\n');
output_rprt_file.write('| |\n');
output_rprt_file.write('| © 2023 Dr. Alan Mustafa and Prof. Abdulnasser Hatemi-J |\n');
output_rprt_file.write('==========================================================================================\n');
output_rprt_file.close;
# --------------------------------------------------------------------------
txtEndNote = "";
txtEndNote = txtEndNote + """REFERENCES:
- Markowitz H. (1952) Portfolio Selection, Journal of Finance, vol. 7(1), 77-91.
- Hatemi-J A. and El-Khatib Y. (2015) Portfolio Selection: An Alternative Approach, Economics Letters, vol. 135, 141-143.
- Hatemi-J A., Hajji, M.A. and El-Khatib Y. (2022) Exact solution for the portfolio diversification problem based on maximizing the risk adjusted return. Research in International Business and Finance, 59, 101548.
ADDITIONAL INFORMATION:
This program code is the copyright of the authors. Applications are allowed only if proper reference and acknowledgments are provided. For non-Commercial
applications only. No performance guarantee is made. Bug reports are welcome. If this code is used for research or in any other code, proper attribution
needs to be included.
© 2023 Dr. Alan Mustafa and Prof. Abdulnasser Hatemi-J
"""
self.msgEndNote["text"] = "%s" % (txtEndNote)
return;
###############################################################################
# End of Calculations: Asymmetric Causality Test #
###############################################################################
#In the main function, create the GUI and pass it to the App class
def main():
window2= tk.Tk()
window2.title("PyCPTAM")
# window2.geometry('950x900')
window2.geometry('850x700')
create_window_menu_UI2(window2).grid(row=0, column=0, columnspan=1, sticky="W")
window2.mainloop()
#Run the main function
if __name__ == "__main__":
main()