Python Forum

Full Version: User interface for stock portfolio optimisation
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Thanks by advance for your help. I know it is a complex request. I did my best to do it on my own, but I am stuck… I am using Python 3 on Anaconda, Jupyter.

-----
Hi, I am a noob on Python and more generally in programming, but I am doing my best to learn thanks to online tutorials.
I would like to code a small downloadable program allowing anyone to obtain the optimal stock weights to include in a portfolio in order to maximise expected return. Even if you know nothing about finance, you can still help me because my computations are already working. I just need a “Tkinter expert” ?

Based on a code I found online and some personal adjustments, the code is operational and gives me the outcomes (text and graphs) that I want already. However, I would like to make the code "responsive" to inputs entered in a user interface. For this, I know I have to use Tkinter. I have made several attemps, but I cannot succeed to connect my python code and the user interface thanks to Tkinter. I just miss that "link" to make everything go smoothly.

Could you please help me?
I would like to add a picture of the final result I am looking for but I don't know how to dit on this forum. So here is a quick description.

-----

I would like to have a downloadable program with an icon, which opens a window when we click on it. Just like any program.
Once the window opens it would be organised as follow:

Top section: Logo + Welcome + Instructions
Next to it, two buttons one "Export the document as PDF" and another "Clear inputs"

Inputs section: 3 labels with 3 entry boxes and one button "Go"
Label 1: How many stocks are there in your portfolio?
Entry box 1: horizontal slider widget ranging from 3 to 30
Label 2: Enter the related stock tickers (yahoo finance)
Entry box 2: text box - important to type stocks as follow 'AAPL', 'GOOGL', 'FB' ......
Label 3: Precise time interval for historic performance and volatility
Entry box 3: radio button - 1 year or 3 years or 5 years from today (last closing price when the user opens the program)
A button "Go" to run the program


Outputs section: 2 graphs and 2 texts
Graph 1: Efficient frontier
Text 1: Max Sharpe Ratio Portfolio and Min Variance Portfolio descriptions
Graph 2: Pie chart with related weights
Graph 2: Stocks' historical return and volatility

-----

Here is the code (with no Tkinter attempt)
I will post my unworking Tkinter attempt afterwards.

import numpy as np
import pandas as pd 
from pandas_datareader import data as wb
import matplotlib.pyplot as plt
import seaborn as sns
import quandl
import scipy.optimize as sco
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
plt.style.use('fivethirtyeight')
np.random.seed(777)


#I think this is a CRITICAL part from which many inputs should be collected from the user interface’s entry boxes, but I do not know how to connect them (eg: replace 'AAPL', 'AMZN', 'FB', 'GOOGL' by what is written in entry box 2).
tickers = ['AAPL', 'AMZN', 'FB', 'GOOGL']
stocks = pd.DataFrame()
for t in tickers:
    stocks[t] = wb.DataReader(t, data_source='yahoo', start='2019-3-31', end='2020-3-31')['Adj Close']


def pfolio_ann_perf(weights, mean_returns, cov_matrix):
    returns = np.sum(mean_returns * weights ) * 250
    std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(250)
    return std, returns

#Here as well, I need to make sure that the value of rf-rate changes according to what the user wrote in entry box 4, but don’t know how.
returns = stocks.pct_change()
mean_returns = returns.mean()
cov_matrix = returns.cov()
num_pfolio = 25000
rf_rate = 0.01


def neg_sharpe_ratio(weights, mean_returns, cov_matrix, rf_rate):
    p_var, p_ret = pfolio_ann_perf(weights, mean_returns, cov_matrix)
    return -(p_ret - rf_rate) / p_var

def max_sharpe_ratio(mean_returns, cov_matrix, rf_rate):
    num_assets = len(mean_returns)
    args = (mean_returns, cov_matrix, rf_rate)
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bound = (0.0,1.0)
    bounds = tuple(bound for asset in range(num_assets))
    result = sco.minimize(neg_sharpe_ratio, num_assets*[1./num_assets,], args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints)
    return result


def pfolio_volatility(weights, mean_returns, cov_matrix):
    return pfolio_ann_perf(weights, mean_returns, cov_matrix)[0]

def min_variance(mean_returns, cov_matrix):
    num_assets = len(mean_returns)
    args = (mean_returns, cov_matrix)
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bound = (0.0,1.0)
    bounds = tuple(bound for asset in range(num_assets))

    result = sco.minimize(pfolio_volatility, num_assets*[1./num_assets,], args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints)

    return result


def efficient_return(mean_returns, cov_matrix, target):
    num_assets = len(mean_returns)
    args = (mean_returns, cov_matrix)

    def pfolio_return(weights):
        return pfolio_ann_perf(weights, mean_returns, cov_matrix)[1]

    constraints = ({'type': 'eq', 'fun': lambda x: pfolio_return(x) - target},
                   {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bounds = tuple((0,1) for asset in range(num_assets))
    result = sco.minimize(pfolio_volatility, num_assets*[1./num_assets,], args=args, method='SLSQP', bounds=bounds, constraints=constraints)
    return result


def efficient_frontier(mean_returns, cov_matrix, returns_range):
    efficients = []
    for ret in returns_range:
        efficients.append(efficient_return(mean_returns, cov_matrix, ret))
    return efficients


def display_ef_with_selected(mean_returns, cov_matrix, rf_rate):
    max_sharpe = max_sharpe_ratio(mean_returns, cov_matrix, rf_rate)
    sdp, rp = pfolio_ann_perf(max_sharpe['x'], mean_returns, cov_matrix)
    max_sharpe_allocation = pd.DataFrame(max_sharpe.x,index=stocks.columns,columns=['allocation'])
    max_sharpe_allocation.allocation = [round(i*100,2)for i in max_sharpe_allocation.allocation]
    max_sharpe_allocation = max_sharpe_allocation.T
    max_sharpe_allocation

    min_vol = min_variance(mean_returns, cov_matrix)
    sdp_min, rp_min = pfolio_ann_perf(min_vol['x'], mean_returns, cov_matrix)
    min_vol_allocation = pd.DataFrame(min_vol.x,index=stocks.columns,columns=['allocation'])
    min_vol_allocation.allocation = [round(i*100,2)for i in min_vol_allocation.allocation]
    min_vol_allocation = min_vol_allocation.T
    
    an_vol = np.std(returns) * np.sqrt(252)
    an_rt = mean_returns * 252
    
    print ("-"*80)
    print ("Maximum Sharpe Ratio Portfolio Allocation\n")
    print ("Annualised Return:", round(rp,2))
    print ("Annualised Volatility:", round(sdp,2))
    print ("\n")
    print (max_sharpe_allocation)
    print ("-"*80)
    print ("Minimum Volatility Portfolio Allocation\n")
    print ("Annualised Return:", round(rp_min,2))
    print ("Annualised Volatility:", round(sdp_min,2))
    print ("\n")
    print (min_vol_allocation)
    print ("-"*80)
    print ("Individual Stock Returns and Volatility\n")
    for i, txt in enumerate(stocks.columns):
        print (txt,":","annualised return",round(an_rt[i],2),", annualised volatility:",round(an_vol[i],2))
    print ("-"*80)
    
    fig, ax = plt.subplots(figsize=(10, 7))
    ax.scatter(an_vol,an_rt,marker='o',s=200)

    for i, txt in enumerate(stocks.columns):
        ax.annotate(txt, (an_vol[i],an_rt[i]), xytext=(10,0), textcoords='offset points')
    ax.scatter(sdp,rp,marker='*',color='r',s=500, label='Maximum Sharpe ratio')
    ax.scatter(sdp_min,rp_min,marker='*',color='g',s=500, label='Minimum volatility')

    target = np.linspace(rp_min, 0.34, 50)
    efficient_pfolio = efficient_frontier(mean_returns, cov_matrix, target)
    ax.plot([p['fun'] for p in efficient_pfolio], target, linestyle=':', color='black', label='Efficient frontier')
    ax.set_title('Portfolio Optimization with Individual Stocks')
    ax.set_xlabel('Annualised Volatility')
    ax.set_ylabel('Annualised Returns')
    ax.legend(labelspacing=0.8)

display_ef_with_selected(mean_returns, cov_matrix, rf_rate)
-----

This is my failed attempt at tkinter. Since it was not working for entry boxes, I did not try to do more.

from tkinter import *
from PIL import ImageTk, Image


root = Tk()
root.title('MT Finance')
root.iconbitmap(r'D:/Coding/Portfolio_Optimisation/Logo.ico')


mylabel1 = Label(root, text='Please type stock tickers (eg: "AAPL")')
mylabel1.grid(row=2, column=0)
e1 = Entry(root, width=35, borderwidth=5)
e1.grid(row=3, column=0, columnspan=3, padx=10, pady=10)

mylabel2 = Label(root, text='Please type start date (eg: 1-1-2019)')
mylabel2.grid(row=4, column=0)
e2 = Entry(root, width=35, borderwidth=5)
e2.grid(row=5, column=0, columnspan=3, padx=10, pady=10)

mylabel3 = Label(root, text='Please type end date (eg: 1-1-2020)')
mylabel3.grid(row=6, column=0)
e3 = Entry(root, width=35, borderwidth=5)
e3.grid(row=7, column=0, columnspan=3, padx=10, pady=10)


#def mystocks():
    #selected_stocks = Label(root, text=e1.get())
    #selected_stocks.grid(row=9, column=0)
    #e1.delete(0, END)
    
# Display the button triggering the function

#mybutton = Button(root, text='Write stocks', padx=10, pady=10, command=mystocks, fg='red')
#mybutton.grid(row=8, column=0)



tickers = [e1.get()]
start_date = [e2.get()]
end_date = [e3.get()]

stocks = pd.DataFrame()
for t in tickers:
    stocks[t] = wb.DataReader(t, data_source='yahoo', start_date, end_date)['Adj Close']

def price_graph():
    (stocks / stocks.iloc[0] * 100).plot(figsize=(15, 6))
    plt.legend(loc='upper left', fontsize=10)
    plt.ylabel('price in $')
    plt.show()

price_graph_button = Button(root, text='Price Graph', padx=10, pady=10, command=price_graph, fg='red')
price_graph_button.grid(row=8, column=0)

root.mainloop()
-----

As, said before, all my computations and graphs are displaying correctly in Python. However, I need your help to make the connection between my code and a simple user interface via Tkinter. I watched several tutorials but I am stuck.
First of all, I would like to have a basic, ugly version working from inputs to outputs. Afterwards, and if you are still kind enough to help me, we can have a look at the “export”, “clear” buttons and the layout :D

If this is not clear please tell me and I will be happy to give more information!

PS: I had to retype all this TWICE because the forum does not allow to edit a message 10 minutes after starting to type. This was, VERY, annoying.
your thread is similar to this one data from internet and post in gui tkinter
maybe it can help with your start,
Thank you for your help @joe_momma. I had a look at the other post, but unfortunately, as I have told you, I am a real beginner and this is hard for me to understand and adapt to my particular case. Could you provide some more guidance on how I should do this concretely please? Maybe you know some videos online to make this particular thing or maybe you could give me some hints yourself?

Do you think I should repost it in the "Homework" section to look for help maybe?

Thank you.