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()
|