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# # Building a portfolio with data from `quandl`/`yfinance`
2# ## Building a portfolio with `build_portfolio()` by downloading relevant data through `quandl`/`yfinance` with stock names, start and end date and column labels
3# This example only focuses on how to use `build_portfolio()` to get an instance of `Portfolio` by providing a few items of 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`.
4
5import datetime
6
7import pandas as pd
8
9# importing some custom functions/objects
10from finquant.portfolio import build_portfolio
11
12# ## Get data from `quandl`/`yfinance` and build portfolio
13# 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.
14
15# To play around yourself with different stocks, here is a short list of companies and their tickers on Yahoo Finance:
16# d = {0: {'Name':'GOOG', 'Allocation':20}, # Google
17# 1: {'Name':'AMZN', 'Allocation':33}, # Amazon
18# 2: {'Name':'MSFT', 'Allocation':18}, # Microsoft
19# 3: {'Name':'AAPL', 'Allocation':10}, # Apple
20# 4: {'Name':'KO', 'Allocation':15}, # Coca-Cola
21# 5: {'Name':'XOM', 'Allocation':11}, # Exxon Mobil
22# 6: {'Name':'JPM', 'Allocation':21}, # JP Morgan
23# 7: {'Name':'DIS', 'Allocation':9}, # Disney
24# 8: {'Name':'MCD', 'Allocation':23}, # McDonald's
25# 9: {'Name':'WMT', 'Allocation':3}, # Walmart
26# 10: {'Name':'GS', 'Allocation':9}, # Goldman Sachs
27# }
28
29d = {
30 0: {"Name": "GOOG", "Allocation": 20},
31 1: {"Name": "AMZN", "Allocation": 10},
32 2: {"Name": "MCD", "Allocation": 15},
33 3: {"Name": "DIS", "Allocation": 18},
34}
35# If you wish to use `quandl` as source, you must add "WIKI/" at the beginning of stock names/tickers, as "WIKI/GOOG".
36
37pf_allocation = pd.DataFrame.from_dict(d, orient="index")
38
39# ### User friendly interface to quandl/yfinance
40# As mentioned above, in this example `build_portfolio()` is used to build a portfolio by performing a query to `quandl`/`yfinance`. We mention that `quandl` will be removed in future versions of `FinQuant` as it is deprecated.
41#
42# To download Google's stock data, `quandl` requires the string `"WIKI/GOOG"` and `yfinance` the string `"GOOG"`.
43# 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.
44# For example, if using `quandl` as a data source (currently the default option), a list of names/tickers as shown below is a valid input for `FinQuant`'s function `build_portfolio(names=names)`:
45# * `names = ["WIKI/GOOG", "WIKI/AMZN"]`
46#
47# 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):
48# * `names = ["GOOG", "AMZN"]`
49#
50# By default, `FinQuant` currently uses `quandl` to obtain stock price data. The function `build_portfolio()` can be called with the optional argument `data_api` to use `yfinance` instead:
51# * `build_portfolio(names=names, data_api="yfinance")`
52#
53# In the below example we are using `yfinance` to download stock data. We specify the start and end date of the stock prices to be downloaded.
54# We also provide the optional parameter `market_index` to download the historical data of a market index.
55# `FinQuant` can use them to calculate the Treynor Ratio, beta parameter, and R squared coefficient, measuring the portfolio's daily volatility compared to the market.
56
57# here we set the list of names based on the names in
58# the DataFrame pf_allocation
59names = pf_allocation["Name"].values.tolist()
60
61# dates can be set as datetime or string, as shown below:
62start_date = datetime.datetime(2015, 1, 1)
63end_date = "2017-12-31"
64
65# the market index used to compare the portfolio to (in this case S&P 500).
66# If the parameter is omitted, no market comparison will be done
67market_index = "^GSPC"
68
69# While quandl/yfinance will download lots of different prices for each stock,
70# e.g. high, low, close, etc, FinQuant will extract the column "Adj. Close" ("Adj Close" if using yfinance).
71
72pf = build_portfolio(
73 names=names,
74 pf_allocation=pf_allocation,
75 start_date=start_date,
76 end_date=end_date,
77 data_api="yfinance",
78 market_index=market_index,
79)
80
81# ## Portfolio is successfully built
82# Getting data from the portfolio
83
84# the portfolio information DataFrame
85print(pf.portfolio)
86
87# the portfolio stock data, prices DataFrame
88print(pf.data.head(3))
89
90# print out information and quantities of given portfolio
91print(pf)
92pf.properties()
93
94# ## Please continue with `Example-Build-Portfolio-from-file.py`.
95# 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# # Building a portfolio with data from disk
2# ## Building a portfolio with `build_portfolio()` with data obtained from data files.
3# Note: The stock data is provided in two data files. The stock data was previously pulled from quandl.
4
5import datetime
6import pathlib
7
8import pandas as pd
9
10# importing FinQuant's function to automatically build the portfolio
11from finquant.portfolio import build_portfolio
12
13# ### Get data from disk/file
14# Here we use `pandas.read_cvs()` method to read in the data.
15
16# stock data was previously pulled from quandl and stored in ex1-stockdata.csv
17# commands used to save data:
18# pf.portfolio.to_csv("ex1-portfolio.csv", encoding='utf-8', index=False, header=True)
19# pf.data.to_csv("ex1-stockdata.csv", encoding='utf-8', index=True, index_label="Date")
20# read data from files:
21df_pf_path = pathlib.Path.cwd() / ".." / "data" / "ex1-portfolio.csv"
22df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
23df_pf = pd.read_csv(df_pf_path)
24df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)
25
26# ### Examining the DataFrames
27
28print(df_pf)
29
30print(df_data.head(3))
31
32# ## Building a portfolio with `build_portfolio()`
33# `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.
34# In this example `build_portfolio()` is being passed `df_data`, which was read in from file above.
35
36print(build_portfolio.__doc__)
37
38# ## Building a portfolio with data only
39# 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.
40
41# building a portfolio by providing stock data
42pf = build_portfolio(data=df_data)
43
44# ### Portfolio is successfully built
45# 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`.
46
47# the portfolio information DataFrame
48print(pf.portfolio.name)
49print(pf.portfolio)
50
51# the portfolio stock data, prices DataFrame
52print(pf.data.head(3))
53
54# ## Building a portfolio with data and desired allocation
55# 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.
56
57# building a portfolio by providing stock data
58# and a desired allocation
59pf2 = build_portfolio(data=df_data, pf_allocation=df_pf)
60
61# the portfolio information DataFrame
62print(pf2.portfolio.name)
63print(pf2.portfolio)
64
65# the portfolio stock data, prices DataFrame
66print(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# # Example:
2# ## Building a portfolio with `build_portfolio()` with data obtained from data files.
3# Note: The stock data is provided in two data files. The stock data was previously pulled from quandl.
4
5import datetime
6import pathlib
7
8import matplotlib.pyplot as plt
9import pandas as pd
10
11# importing FinQuant's function to automatically build the portfolio
12from finquant.portfolio import build_portfolio
13
14# ## Building a portfolio with `build_portfolio()`
15# As in previous example, using `build_portfolio()` to generate an object of `Portfolio`.
16
17# read data from files:
18df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
19df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)
20# building a portfolio by providing stock data
21pf = build_portfolio(data=df_data)
22
23# ## Expected Return, Volatility, Sharpe Ratio, Sortino Ratio, and Value at Risk of Portfolio
24# The annualised expected return and volatility, as well as the Sharpe Ratio, the Sortino Ratio, and Value at Risk are automatically computed.
25# They are obtained as shown below.
26# The expected return and volatility are based on 252 trading days by default.
27# The Sharpe Ratio and the Sortino ratio are computed with a risk free rate of 0.005 by default.
28# The Value at Risk is computed with a confidence level of 0.95 by default.
29
30# expected (annualised) return
31print(pf.expected_return)
32
33# volatility
34print(pf.volatility)
35
36# Sharpe Ratio (computed with a risk free rate of 0.005 by default)
37print(pf.sharpe)
38
39# Sortino Ratio (computed with a risk free rate of 0.005 by default)
40print(pf.sortino)
41
42# Value at Risk (computed with a confidence level of 0.95 by default)
43print(pf.var)
44
45# ## Getting Skewness and Kurtosis of the stocks
46
47print(pf.skew)
48
49print(pf.kurtosis)
50
51# ## Nicely printing out portfolio quantities
52# To print the expected annualised return, volatility, Sharpe Ratio, Sortino Ratio, skewness and Kurtosis of the portfolio and its stocks, one can simply do `pf.properties()`.
53
54print(pf)
55pf.properties()
56
57# ## Daily returns and log returns
58# `FinQuant` provides functions to compute daily returns and annualised mean returns of a given DataFrame in various ways.
59
60# annualised mean returns
61print(pf.comp_mean_returns())
62
63# daily returns (percentage change)
64print(pf.comp_cumulative_returns().head(3))
65
66print(pf.comp_daily_log_returns().head(3))
67
68# plotting stock data of portfolio
69pf.data.plot()
70plt.show()
71
72# 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.
73
74pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
75plt.show()
76
77# plotting cumulative returns (price_{t} - price_{t=0}) / price_{t=0}
78pf.comp_cumulative_returns().plot().axhline(y=0, color="black", lw=3)
79plt.show()
80
81# plotting daily percentage changes of returns
82pf.comp_daily_returns().plot().axhline(y=0, color="black")
83plt.show()
84
85# plotting daily log returns
86pf.comp_daily_log_returns().plot().axhline(y=0, color="black")
87plt.show()
88
89# cumulative log returns
90pf.comp_daily_log_returns().cumsum().plot().axhline(y=0, color="black")
91plt.show()
92
93# ## Moving Averages
94# `FinQuant` provides a module `finquant.moving_average` to compute moving averages. See below.
95
96from finquant.moving_average import sma
97
98# simple moving average
99ax = pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
100# computing simple moving average over a span of 50 (trading) days
101# and plotting it
102sma(pf.data, span=50).plot(ax=ax, secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
103plt.show()
104
105from finquant.moving_average import ema
106
107# exponential moving average
108ax = pf.data.plot(secondary_y=["WIKI/MCD", "WIKI/DIS"], grid=True)
109# computing exponential moving average and plotting it
110ema(pf.data).plot(ax=ax, secondary_y=["WIKI/MCD", "WIKI/DIS"])
111plt.show()
112
113# ## Band of moving averages and Buy/Sell signals
114# `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.
115# To learn more about it and its input arguments, read its docstring and see the example below.
116
117from finquant.moving_average import compute_ma
118
119print(compute_ma.__doc__)
120
121# get stock data for disney
122dis = pf.get_stock("WIKI/DIS").data.copy(deep=True)
123# we want moving averages of 10, 50, 100, and 200 days.
124spans = [10, 50, 100, 150, 200]
125# compute and plot moving averages
126dis_ma = compute_ma(dis, ema, spans, plot=True)
127plt.show()
128
129# ## Plot the Bollinger Band of one stock
130# The Bollinger Band can be automatically computed and plotted with the method `finquant.moving_average.plot_bollinger_band`. See below for an example.
131
132# plot the bollinger band of the disney stock prices
133from finquant.moving_average import plot_bollinger_band
134
135# get stock data for disney
136dis = pf.get_stock("WIKI/DIS").data.copy(deep=True)
137span = 20
138# for simple moving average:
139plot_bollinger_band(dis, sma, span)
140plt.show()
141# for exponential moving average:
142plot_bollinger_band(dis, ema, span)
143plt.show()
144
145# ## Recomputing expected return, volatility and Sharpe ratio
146# **Note**: When doing so, the instance variables for
147# - Expected return
148# - Volatility
149# - Sharpe Ratio
150# are automatically recomputed.
151
152# If the return, volatility and Sharpe ratio need to be computed based
153# on a different time window and/or risk free rate, one can recompute
154# those values as shown below
155# 1. set the new value(s)
156pf.freq = 100
157pf.risk_free_rate = 0.02
158
159# 2.a compute and get new values based on new freq/risk_free_rate
160exret = pf.comp_expected_return(freq=100)
161vol = pf.comp_volatility(freq=100)
162sharpe = pf.comp_sharpe()
163print(
164 "For {} trading days and a risk free rate of {}:".format(pf.freq, pf.risk_free_rate)
165)
166print("Expected return: {:0.3f}".format(exret))
167print("Volatility: {:0.3f}".format(vol))
168print("Sharpe Ratio: {:0.3f}".format(sharpe))
169
170# 2.b print out properties of portfolio (which is based on new freq/risk_free_rate)
171pf.properties()
172
173# ## Extracting data of stocks individually
174# 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.
175
176# getting Stock object from portfolio, for Google's stock
177goog = pf.get_stock("WIKI/GOOG")
178# getting the stock prices
179goog_prices = goog.data
180print(goog_prices.head(3))
181
182print(goog.comp_daily_returns().head(3))
183
184print(goog.expected_return)
185
186print(goog.volatility)
187
188print(goog.skew)
189
190print(goog.kurtosis)
191
192print(goog)
193goog.properties()
194
195# ## Extracting stock data by date
196# 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.
197
198print(pf.data.loc[str(datetime.datetime(2015, 1, 2))])
199
200print(pf.data.loc[pf.data.index > datetime.datetime(2016, 1, 2)].head(3))
201
202print(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# # Example: Portfolio optimisation
2# This example shows how `FinQuant` can be used to optimise a portfolio.
3# Two different approaches are implemented in `FinQuant`:
4# 1. Efficient Frontier
5# 2. Monte Carlo run
6# With the *Efficient Frontier* approach, the portfolio can be optimised for
7# - minimum volatility,
8# - maximum Sharpe ratio
9# - minimum volatility for a given expected return
10# - maximum Sharpe ratio for a given target volatility
11# by performing a numerical solve to minimise/maximise an objective function.
12# Alternatively a *Monte Carlo* run of `n` trials can be performed to find the optimal portfolios for
13# - minimum volatility,
14# - maximum Sharpe ratio
15# 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.
16#
17# ## Visualisation
18# 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.
19# 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.
20# Finally, the entire result of a *Monte Carlo* run can also be visualised automatically by `FinQuant`. An example is shown below.
21
22import datetime
23import pathlib
24
25import matplotlib.pyplot as plt
26import numpy as np
27import pandas as pd
28
29# importing FinQuant's function to automatically build the portfolio
30from finquant.portfolio import build_portfolio
31
32# ### Get data from disk/file
33# Here we use `pandas.read_cvs()` method to read in the data.
34
35# stock data was previously pulled from quandl and stored in ex1-stockdata.csv
36# read data from files:
37df_data_path = pathlib.Path.cwd() / ".." / "data" / "ex1-stockdata.csv"
38df_data = pd.read_csv(df_data_path, index_col="Date", parse_dates=True)
39# building a portfolio by providing stock data
40pf = build_portfolio(data=df_data)
41print(pf)
42pf.properties()
43
44# # Portfolio optimisation
45# ## Efficient Frontier
46# Based on the **Efficient Frontier**, the portfolio can be optimised for
47# - minimum volatility
48# - maximum Sharpe ratio
49# - minimum volatility for a given target return
50# - maximum Sharpe ratio for a given target volatility
51# See below for an example for each optimisation.
52
53# if needed, change risk free rate and frequency/time window of the portfolio
54print("pf.risk_free_rate = {}".format(pf.risk_free_rate))
55print("pf.freq = {}".format(pf.freq))
56
57pf.ef_minimum_volatility(verbose=True)
58
59# optimisation for maximum Sharpe ratio
60pf.ef_maximum_sharpe_ratio(verbose=True)
61
62# minimum volatility for a given target return of 0.26
63pf.ef_efficient_return(0.26, verbose=True)
64
65# maximum Sharpe ratio for a given target volatility of 0.22
66pf.ef_efficient_volatility(0.22, verbose=True)
67
68# ### Manually creating an instance of EfficientFrontier
69# 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.
70
71from finquant.efficient_frontier import EfficientFrontier
72
73# creating an instance of EfficientFrontier
74ef = EfficientFrontier(pf.comp_mean_returns(freq=1), pf.comp_cov())
75# optimisation for minimum volatility
76print(ef.minimum_volatility())
77
78# printing out relevant quantities of the optimised portfolio
79(expected_return, volatility, sharpe) = ef.properties(verbose=True)
80
81# ### Computing and visualising the Efficient Frontier
82# `FinQuant` offers several ways to compute the *Efficient Frontier*.
83# 1. Through the opject `pf`
84# - with automatically setting limits of the *Efficient Frontier*
85# 2. By manually creating an instance of `EfficientFrontier` and providing the data from the portfolio
86# - with automatically setting limits of the *Efficient Frontier*
87# - by providing a range of target expected return values
88# 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.
89# - `pf.ef_plot_efrontier()`
90# - `pf.ef_plot_optimal_portfolios()`
91# - `pf.plot_stocks()`
92
93# #### Efficient Frontier of `pf`
94
95# computing and plotting efficient frontier of pf
96pf.ef_plot_efrontier()
97# adding markers to optimal solutions
98pf.ef_plot_optimal_portfolios()
99# and adding the individual stocks to the plot
100pf.plot_stocks()
101plt.show()
102
103# #### Manually creating an Efficient Frontier with target return values
104
105targets = np.linspace(0.12, 0.45, 50)
106# computing efficient frontier
107efficient_frontier = ef.efficient_frontier(targets)
108# plotting efficient frontier
109ef.plot_efrontier()
110# adding markers to optimal solutions
111pf.ef.plot_optimal_portfolios()
112# and adding the individual stocks to the plot
113pf.plot_stocks()
114plt.show()
115
116# ## Monte Carlo
117# Perform a Monte Carlo run to find the portfolio with the minimum volatility and maximum Sharpe Ratio.
118
119opt_w, opt_res = pf.mc_optimisation(num_trials=5000)
120pf.mc_properties()
121pf.mc_plot_results()
122# again, the individual stocks can be added to the plot
123pf.plot_stocks()
124plt.show()
125
126print(opt_res)
127print()
128print(opt_w)
129
130# # Optimisation overlay
131# ## Overlay of Monte Carlo portfolios and Efficient Frontier solutions
132
133opt_w, opt_res = pf.mc_optimisation(num_trials=5000)
134pf.mc_plot_results()
135pf.ef_plot_efrontier()
136pf.ef.plot_optimal_portfolios()
137pf.plot_stocks()
138plt.show()