Examples

For more information about the project and details on how to use it, please look at the examples discussed below.

Note

In the below examples, pf refers to an instance of finquant.portfolio.Portfolio, the object that holds all stock prices and computes its most common quantities automatically. To make FinQuant a user-friendly program, that combines data analysis, visualisation and optimisation, the object also provides interfaces to the main features that are provided in the modules in ./finquant/ and are discussed throughout this documentation.

Building a portfolio with data from web quandl/yfinance

This example shows how to use FinQuant to build a financial portfolio by downloading stock price data by using the Python package quandl/yfinance.

Note

This example refers to example/Example-Build-Portfolio-from-web.py of the GitHub repository. It can be downloaded with jupyter notebook cell information: download Example-Build-Portfolio-from-web.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# # Building a portfolio with data from `quandl`/`yfinance`
# ## Building a portfolio with `build_portfolio()` by downloading relevant data through `quandl`/`yfinance` with stock names, start and end date and column labels
# This example only focuses on how to use `build_portfolio()` to get an instance of `Portfolio` by providing minimal information that is passed on to `quandl`/`yfinance`. For a more exhaustive description of this package and example, please try `Example-Analysis` and `Example-Optimisation`.

import pandas as pd
import datetime

# importing some custom functions/objects
from finquant.portfolio import build_portfolio

# ## Get data from `quandl`/`yfinance` and build portfolio
# First we need to build a pandas.DataFrame that holds relevant data for our portfolio. The minimal information needed are stock names and the amount of money to be invested in them, e.g. Allocation.

# To play around yourself with different stocks, here is a short list of companies and their tickers
# d = {0: {'Name':'WIKI/GOOG', 'Allocation':20},  # Google
#      1: {'Name':'WIKI/AMZN', 'Allocation':33},  # Amazon
#      2: {'Name':'WIKI/MSFT', 'Allocation':18},  # Microsoft
#      3: {'Name':'WIKI/AAPL', 'Allocation':10},  # Apple
#      4: {'Name':'WIKI/KO', 'Allocation':15},    # Coca-Cola
#      5: {'Name':'WIKI/XOM', 'Allocation':11},   # Exxon Mobil
#      6: {'Name':'WIKI/JPM', 'Allocation':21},   # JP Morgan
#      7: {'Name':'WIKI/DIS', 'Allocation':9},    # Disney
#      8: {'Name':'WIKI/MCD', 'Allocation':23},   # McDonald's
#      9: {'Name':'WIKI/WMT', 'Allocation':3},    # Walmart
#     10: {'Name':'WIKI/YHOO', 'Allocation':7},   # Yahoo
#     11: {'Name':'WIKI/GS', 'Allocation':9},     # Goldman Sachs
#     }

d = {
    0: {"Name": "WIKI/GOOG", "Allocation": 20},
    1: {"Name": "WIKI/AMZN", "Allocation": 10},
    2: {"Name": "WIKI/MCD", "Allocation": 15},
    3: {"Name": "WIKI/DIS", "Allocation": 18},
}
# If you wish to use Yahoo Finance as source, you must remove "WIKI/" from the stock names/tickers

pf_allocation = pd.DataFrame.from_dict(d, orient="index")

# ### User friendly interface to quandl/yfinance
# As mentioned above, in this example `build_portfolio()` is used to build a portfolio by performing a query to `quandl`/`yfinance`.
#
# To download Google's stock data, `quandl` requires the string `"WIKI/GOOG"`. For simplicity, `FinQuant` facilitates a set of functions under the hood to sort out lots of specific commands/required input for `quandl`/`yfinance`. When using `FinQuant`, the user simply needs to provide a list of stock names/tickers.
# For example, if using `quandl` as a data source (default), a list of names/tickers as shown below is a valid input for `FinQuant`'s function `build_portfolio(names=names)`:
#  * `names = ["WIKI/GOOG", "WIKI/AMZN"]`
#
# If using `yfinance` as a data source, `FinQuant`'s function `build_portfolio(names=names)` expects the stock names to be without any leading/trailing string (check Yahoo Finance for correct stock names):
#  * `names = ["GOOG", "AMZN"]`
#
# By default, `FinQuant` uses `quandl` to obtain stock price data. The function `build_portfolio()` can be called with the optional argument `data_api` to use `yfinance` instead:
#  * `build_portfolio(names=names, data_api="yfinance")`
#
# In the below example we are using the default option, `quandl`.

# here we set the list of names based on the names in
# the DataFrame pf_allocation
names = pf_allocation["Name"].values.tolist()

# dates can be set as datetime or string, as shown below:
start_date = datetime.datetime(2015, 1, 1)
end_date = "2017-12-31"

# While quandl/yfinance will download lots of different prices for each stock,
# e.g. high, low, close, etc, FinQuant will extract the column "Adj. Close" ("Adj Close" if using yfinance).

pf = build_portfolio(
    names=names, pf_allocation=pf_allocation, start_date=start_date, end_date=end_date
)

# ## Portfolio is successfully built
# Getting data from the portfolio

# the portfolio information DataFrame
print(pf.portfolio)

# the portfolio stock data, prices DataFrame
print(pf.data.head(3))

# print out information and quantities of given portfolio
print(pf)
pf.properties()

# ## Please continue with `Example-Build-Portfolio-from-file.py`.
# As mentioned above, this example only shows how to use `build_portfolio()` to get an instance of `Portfolio` by downloading data through `quandl`/`yfinance`.

Building a portfolio with preset data

This example shows how to use FinQuant to build a financial portfolio by providing stock price data yourself, e.g. by reading data from disk/file.

Note

This example refers to example/Example-Build-Portfolio-from-file.py of the GitHub repository. It can be downloaded with jupyter notebook cell information: download Example-Build-Portfolio-from-file.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# # Building a portfolio with data from disk
# ## Building a portfolio with `build_portfolio()` with data obtained from data files.
# Note: The stock data is provided in two data files. The stock data was previously pulled from quandl.

import pathlib
import pandas as pd
import datetime

# importing FinQuant's function to automatically build the portfolio
from finquant.portfolio import build_portfolio

# ### Get data from disk/file
# Here we use `pandas.read_cvs()` method to read in the data.

# stock data was previously pulled from quandl and stored in ex1-stockdata.csv
# commands used to save data:
# pf.portfolio.to_csv("ex1-portfolio.csv", encoding='utf-8', index=False, header=True)
# pf.data.to_csv("ex1-stockdata.csv", encoding='utf-8', index=True, index_label="Date")
# read data from files:
df_pf_path = pathlib.Path.cwd() / ".." / "data" / "ex1-portfolio.csv"
df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
df_pf = pd.read_csv(df_pf_path)
df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)

# ### Examining the DataFrames

print(df_pf)

print(df_data.head(3))

# ## Building a portfolio with `build_portfolio()`
# `build_portfolio()` is an interface that can be used in different ways. Two of which is shown below. For more information the docstring is shown below as well.
# In this example `build_portfolio()` is being passed `df_data`, which was read in from file above.

print(build_portfolio.__doc__)

# ## Building a portfolio with data only
# Below is an example of only passing a `DataFrame` containing data (e.g. stock prices) to `build_portfolio()` in order to build an instance of `Portfolio`. In this case, the allocation of stocks is automatically generated by equally distributing the weights across all stocks.

# building a portfolio by providing stock data
pf = build_portfolio(data=df_data)

# ### Portfolio is successfully built
# Below it is shown how the allocation of the stocks and the data (e.g. prices) of the stocks can be obtained from the object `pf`.

# the portfolio information DataFrame
print(pf.portfolio.name)
print(pf.portfolio)

# the portfolio stock data, prices DataFrame
print(pf.data.head(3))

# ## Building a portfolio with data and desired allocation
# If a specific allocation of stocks in the portfolio is desired, a `DataFrame` such as `df_pf` (which was read from file above) can be passed to `build_portfolio()` as shown below.

# building a portfolio by providing stock data
# and a desired allocation
pf2 = build_portfolio(data=df_data, pf_allocation=df_pf)

# the portfolio information DataFrame
print(pf2.portfolio.name)
print(pf2.portfolio)

# the portfolio stock data, prices DataFrame
print(pf2.data.head(3))

Analysis of a portfolio

This example shows how to use an instance of finquant.portfolio.Portfolio, get the portfolio’s quantities, such as

  • Expected Returns,
  • Volatility,
  • Sharpe Ratio.

It also shows how to extract individual stocks from the given portfolio. Moreover it shows how to compute and visualise:

  • the different Returns provided by the module finquant.returns,
  • Moving Averages, a band of Moving Averages, and a Bollinger Band.

Note

This example refers to example/Example-Analysis.py of the GitHub repository. It can be downloaded with jupyter notebook cell information: download Example-Analysis.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# # Example:
# ## Building a portfolio with `build_portfolio()` with data obtained from data files.
# Note: The stock data is provided in two data files. The stock data was previously pulled from quandl.

import pathlib
import matplotlib.pyplot as plt
import pandas as pd
import datetime

# importing FinQuant's function to automatically build the portfolio
from finquant.portfolio import build_portfolio

# ## Building a portfolio with `build_portfolio()`
# As in previous example, using `build_portfolio()` to generate an object of `Portfolio`.

# read data from files:
df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)
# building a portfolio by providing stock data
pf = build_portfolio(data=df_data)

# ## Expected Return, Volatility and Sharpe Ratio of Portfolio
# The annualised expected return and volatility as well as the Sharpe Ratio are automatically computed. They are obtained as shown below.
# The expected return and volatility are based on 252 trading days by default. The Sharpe Ratio is computed with a risk free rate of 0.005 by default.

# expected (annualised) return
print(pf.expected_return)

# volatility
print(pf.volatility)

# Sharpe ratio (computed with a risk free rate of 0.005 by default)
print(pf.sharpe)

# ## Getting Skewness and Kurtosis of the stocks

print(pf.skew)

print(pf.kurtosis)

# ## Nicely printing out portfolio quantities
# To print the expected annualised return, volatility, Sharpe ratio, skewness and Kurtosis of the portfolio and its stocks, one can simply do `pf.properties()`.

print(pf)
pf.properties()

# ## Daily returns and log returns
# `FinQuant` provides functions to compute daily returns and annualised mean returns of a given DataFrame in various ways.

# annualised mean returns
print(pf.comp_mean_returns())

# daily returns (percentage change)
print(pf.comp_cumulative_returns().head(3))

print(pf.comp_daily_log_returns().head(3))

# plotting stock data of portfolio
pf.data.plot()
plt.show()

# The stock prices of Google and Amazon are much higher than those for McDonald's and Disney. Hence the fluctuations of the latter ones are barely seen in the above plot. One can use `pandas.plot()` method to create a secondary y axis.

pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
plt.show()

# plotting cumulative returns (price_{t} - price_{t=0}) / price_{t=0}
pf.comp_cumulative_returns().plot().axhline(y=0, color="black", lw=3)
plt.show()

# plotting daily percentage changes of returns
pf.comp_daily_returns().plot().axhline(y=0, color="black")
plt.show()

# plotting daily log returns
pf.comp_daily_log_returns().plot().axhline(y=0, color="black")
plt.show()

# cumulative log returns
pf.comp_daily_log_returns().cumsum().plot().axhline(y=0, color="black")
plt.show()

# ## Moving Averages
# `FinQuant` provides a module `finquant.moving_average` to compute moving averages. See below.

from finquant.moving_average import sma

# simple moving average
ax = pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
# computing simple moving average over a span of 50 (trading) days
# and plotting it
sma(pf.data, span=50).plot(ax=ax, secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
plt.show()

from finquant.moving_average import ema

# exponential moving average
ax = pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
# computing exponential moving average and plotting it
ema(pf.data).plot(ax=ax, secondary_y=["WIKI/MCD", "WIKI/DIS"])
plt.show()

# ## Band of moving averages and Buy/Sell signals
# `FinQuant` also provides a method `finquant.moving_average.compute_ma` that automatically computes and plots several moving averages. It also **finds buy/sell signals based on crossovers** of the shortest and longest moving average.
# To learn more about it and its input arguments, read its docstring and see the example below.

from finquant.moving_average import compute_ma

print(compute_ma.__doc__)

# get stock data for disney
dis = pf.get_stock("WIKI/DIS").data.copy(deep=True)
# we want moving averages of 10, 50, 100, and 200 days.
spans = [10, 50, 100, 150, 200]
# compute and plot moving averages
dis_ma = compute_ma(dis, ema, spans, plot=True)
plt.show()

# ## Plot the Bollinger Band of one stock
# The Bollinger Band can be automatically computed and plotted with the method `finquant.moving_average.plot_bollinger_band`. See below for an example.

# plot the bollinger band of the disney stock prices
from finquant.moving_average import plot_bollinger_band

# get stock data for disney
dis = pf.get_stock("WIKI/DIS").data.copy(deep=True)
span = 20
# for simple moving average:
plot_bollinger_band(dis, sma, span)
plt.show()
# for exponential moving average:
plot_bollinger_band(dis, ema, span)
plt.show()

# ## Recomputing expected return, volatility and Sharpe ratio
# **Note**: When doing so, the instance variables for
#  - Expected return
#  - Volatility
#  - Sharpe Ratio
# are automatically recomputed.

# If the return, volatility and Sharpe ratio need to be computed based
# on a different time window and/or risk free rate, one can recompute
# those values as shown below
# 1. set the new value(s)
pf.freq = 100
pf.risk_free_rate = 0.02

# 2.a compute and get new values based on new freq/risk_free_rate
exret = pf.comp_expected_return(freq=100)
vol = pf.comp_volatility(freq=100)
sharpe = pf.comp_sharpe()
print(
    "For {} trading days and a risk free rate of {}:".format(pf.freq, pf.risk_free_rate)
)
print("Expected return: {:0.3f}".format(exret))
print("Volatility: {:0.3f}".format(vol))
print("Sharpe Ratio: {:0.3f}".format(sharpe))

# 2.b print out properties of portfolio (which is based on new freq/risk_free_rate)
pf.properties()

# ## Extracting data of stocks individually
# Each stock (its information and data) of the portfolio is stored as a `Stock` data structure. If needed, one can of course extract the relevant data from the portfolio DataFrame, or access the `Stock` instance. The commands are very similar to the once for `Portfolio`. See below how it can be used.

# getting Stock object from portfolio, for Google's stock
goog = pf.get_stock("WIKI/GOOG")
# getting the stock prices
goog_prices = goog.data
print(goog_prices.head(3))

print(goog.comp_daily_returns().head(3))

print(goog.expected_return)

print(goog.volatility)

print(goog.skew)

print(goog.kurtosis)

print(goog)
goog.properties()

# ## Extracting stock data by date
# Since quandl provides a DataFrame with an index of dates, it is easy to extract data from the portfolio for a given time frame. Three examples are shown below.

print(pf.data.loc[str(datetime.datetime(2015, 1, 2))])

print(pf.data.loc[pf.data.index > datetime.datetime(2016, 1, 2)].head(3))

print(pf.data.loc[pf.data.index.year == 2017].head(3))

Optimisation of a portfolio

This example focusses on the optimisation of a portfolio. To achieve this, the example shows the usage of finquant.efficient_frontier.EfficientFrontier for numerically optimising the portfolio, for the

  • Minimum Volatility
  • Maximum Sharpe Ratio
  • Minimum Volatility for a given target Return
  • Maximum Sharpe Ratio for a given target Volatility.

Furthermore, it is also shown how the entire Efficient Frontier and the optimal portfolios can be computed and visualised. If needed, it also gives an example of plotting the individual stocks of the given portfolio within the computed Efficient Frontier.

Also, the optimisation of a portfolio and its visualisation based on a Monte Carlo is shown.

Finally, FinQuant’s visualisation methods allow for overlays, if this is desired. Thus, with only the following few lines of code, one can create an overlay of the Monte Carlo run, the Efficient Frontier, its optimised portfolios for Minimum Volatility and Maximum Sharpe Ratio, as well as the portfolio’s individual stocks.

Note

This example refers to example/Example-Optimisation.py of the GitHub repository. It can be downloaded with jupyter notebook cell information: download Example-Optimisation.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# # Example: Portfolio optimisation
# This example shows how `FinQuant` can be used to optimise a portfolio.
# Two different approaches are implemented in `FinQuant`:
#  1. Efficient Frontier
#  2. Monte Carlo run
# With the *Efficient Frontier* approach, the portfolio can be optimised for
#  - minimum volatility,
#  - maximum Sharpe ratio
#  - minimum volatility for a given expected return
#  - maximum Sharpe ratio for a given target volatility
# by performing a numerical solve to minimise/maximise an objective function.
# Alternatively a *Monte Carlo* run of `n` trials can be performed to find the optimal portfolios for
#  - minimum volatility,
#  - maximum Sharpe ratio
# The approach branded as *Efficient Frontier* should be the preferred method for reasons of computational effort and accuracy. The latter approach is only included for the sake of completeness, and creation of beautiful plots.
#
# ## Visualisation
# Not only does `FinQuant` allow for the optimisation of a portfolio with the above mentioned methods and objectives, `FinQuant` also allows for the computation and visualisation of an *Efficient Frontier* and *Monte Carlo* run.
# Let `pf` be an instance of `Portfolio`. The *Efficient Frontier* can be computed and visualised with `pf.ef_plot_efrontier()`. The optimal portfolios for *minimum volatility* and *maximum Sharpe ratio* can be visualised with `pf.ef_plot_optimal_portfolios()`. And if required, the individual stocks of the portfolio can be visualised with `pf.plot_stocks(show=False)`. An overlay of these three commands is shown below.
# Finally, the entire result of a *Monte Carlo* run can also be visualised automatically by `FinQuant`. An example is shown below.

import pathlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import datetime

# importing FinQuant's function to automatically build the portfolio
from finquant.portfolio import build_portfolio

# ### Get data from disk/file
# Here we use `pandas.read_cvs()` method to read in the data.

# stock data was previously pulled from quandl and stored in ex1-stockdata.csv
# read data from files:
df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)
# building a portfolio by providing stock data
pf = build_portfolio(data=df_data)
print(pf)
pf.properties()

# # Portfolio optimisation
# ## Efficient Frontier
# Based on the **Efficient Frontier**, the portfolio can be optimised for
#  - minimum volatility
#  - maximum Sharpe ratio
#  - minimum volatility for a given target return
#  - maximum Sharpe ratio for a given target volatility
# See below for an example for each optimisation.

# if needed, change risk free rate and frequency/time window of the portfolio
print("pf.risk_free_rate = {}".format(pf.risk_free_rate))
print("pf.freq = {}".format(pf.freq))

pf.ef_minimum_volatility(verbose=True)

# optimisation for maximum Sharpe ratio
pf.ef_maximum_sharpe_ratio(verbose=True)

# minimum volatility for a given target return of 0.26
pf.ef_efficient_return(0.26, verbose=True)

# maximum Sharpe ratio for a given target volatility of 0.22
pf.ef_efficient_volatility(0.22, verbose=True)

# ### Manually creating an instance of EfficientFrontier
# If required, or preferred, the below code shows how the same is achieved by manually creating an instance of EfficientFrontier, passing it the mean returns and covariance matrix of the previously assembled portfolio.

from finquant.efficient_frontier import EfficientFrontier

# creating an instance of EfficientFrontier
ef = EfficientFrontier(pf.comp_mean_returns(freq=1), pf.comp_cov())
# optimisation for minimum volatility
print(ef.minimum_volatility())

# printing out relevant quantities of the optimised portfolio
(expected_return, volatility, sharpe) = ef.properties(verbose=True)

# ### Computing and visualising the Efficient Frontier
# `FinQuant` offers several ways to compute the *Efficient Frontier*.
#  1. Through the opject `pf`
#   - with automatically setting limits of the *Efficient Frontier*
#  2. By manually creating an instance of `EfficientFrontier` and providing the data from the portfolio
#   - with automatically setting limits of the *Efficient Frontier*
#   - by providing a range of target expected return values
# As before, let `pf` and be an instance of `Portfolio`. The following code snippets compute and plot an *Efficient Frontier* of a portfolio, its optimised portfolios and individual stocks within the portfolio.
#  - `pf.ef_plot_efrontier()`
#  - `pf.ef_plot_optimal_portfolios()`
#  - `pf.plot_stocks()`

# #### Efficient Frontier of `pf`

# computing and plotting efficient frontier of pf
pf.ef_plot_efrontier()
# adding markers to optimal solutions
pf.ef_plot_optimal_portfolios()
# and adding the individual stocks to the plot
pf.plot_stocks()
plt.show()

# #### Manually creating an Efficient Frontier with target return values

targets = np.linspace(0.12, 0.45, 50)
# computing efficient frontier
efficient_frontier = ef.efficient_frontier(targets)
# plotting efficient frontier
ef.plot_efrontier()
# adding markers to optimal solutions
pf.ef.plot_optimal_portfolios()
# and adding the individual stocks to the plot
pf.plot_stocks()
plt.show()

# ## Monte Carlo
# Perform a Monte Carlo run to find the portfolio with the minimum volatility and maximum Sharpe Ratio.

opt_w, opt_res = pf.mc_optimisation(num_trials=5000)
pf.mc_properties()
pf.mc_plot_results()
# again, the individual stocks can be added to the plot
pf.plot_stocks()
plt.show()

print(opt_res)
print()
print(opt_w)

# # Optimisation overlay
# ## Overlay of Monte Carlo portfolios and Efficient Frontier solutions

opt_w, opt_res = pf.mc_optimisation(num_trials=5000)
pf.mc_plot_results()
pf.ef_plot_efrontier()
pf.ef.plot_optimal_portfolios()
pf.plot_stocks()
plt.show()