You can run the code examples in Google Colab.
To install the package:
[ ]:
%%capture --no-stderr
%pip install --quiet -U okama
Import okama and matplotlib packages.
[2]:
import warnings
import matplotlib.pyplot as plt
import okama as ok
warnings.filterwarnings("ignore")
plt.rcParams["figure.figsize"] = [12.0, 6.0]
AssetList has a set of useful methods for tracking the performance of index funds and comparing them with benchmarks:
tracking difference
tracking error
beta
rolling and cumulative correlations
Tracking difference
Tracking difference is calculated as the difference between the accumulated return of an index and that of the ETFs that replicate it.
Let’s compare the main S&P 500 ETFs.
[3]:
symbols = [
"SP500TR.INDX",
"SPY.US",
"VOO.US",
"IVV.US",
] # the benchmark symbol should be in the first place
sp = ok.AssetList(symbols, last_date="2022-04")
sp
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
File ~\PycharmProjects\okama\okama\api\api_methods.py:52, in API.connect(cls, endpoint, symbol, first_date, last_date, period)
51 r = session.get(request_url, params=params, verify=True, timeout=cls.default_timeout)
---> 52 r.raise_for_status()
53 except requests.exceptions.HTTPError as errh:
File c:\Users\Komov\miniconda3\envs\py13\Lib\site-packages\requests\models.py:1026, in Response.raise_for_status(self)
1025 if http_error_msg:
-> 1026 raise HTTPError(http_error_msg, response=self)
HTTPError: 400 Client Error: BAD REQUEST for url: https://api.okama.io/api/ts/macro/USD.INFL?first_date=2010-10-01+00%3A00%3A00&last_date=2022-04-01+00%3A00%3A00&period=M
The above exception was the direct cause of the following exception:
HTTPError Traceback (most recent call last)
Cell In[3], line 7
1 symbols = [
2 "SP500TR.INDX",
3 "SPY.US",
4 "VOO.US",
5 "IVV.US",
6 ] # the benchmark symbol should be in the first place
----> 7 sp = ok.AssetList(symbols, last_date="2022-04")
8 sp
File ~\PycharmProjects\okama\okama\common\make_asset_list.py:78, in ListMaker.__init__(self, assets, first_date, last_date, ccy, inflation)
76 if inflation:
77 self.inflation: str = f"{ccy}.INFL"
---> 78 self._inflation_instance = macro.Inflation(self.inflation, self.first_date, self.last_date)
79 self.inflation_first_date: pd.Timestamp = self._inflation_instance.first_date
80 self.inflation_last_date: pd.Timestamp = self._inflation_instance.last_date
File ~\PycharmProjects\okama\okama\macro.py:242, in Inflation.__init__(self, symbol, first_date, last_date)
236 def __init__(
237 self,
238 symbol: str = settings.default_macro_inflation,
239 first_date: Union[str, pd.Timestamp, None] = None,
240 last_date: Union[str, pd.Timestamp, None] = None,
241 ):
--> 242 super().__init__(
243 symbol,
244 first_date=first_date,
245 last_date=last_date,
246 )
File ~\PycharmProjects\okama\okama\macro.py:41, in MacroABC.__init__(self, symbol, first_date, last_date)
39 self._first_date = first_date
40 self._last_date = last_date
---> 41 self._values_monthly = self._get_values_monthly()
42 self._set_first_last_dates()
44 self._pl_txt = f"{self.pl.years} years, {self.pl.months} months"
File ~\PycharmProjects\okama\okama\macro.py:71, in MacroABC._get_values_monthly(self)
70 def _get_values_monthly(self) -> pd.Series:
---> 71 return data_queries.QueryData.get_macro_ts(self.symbol, self._first_date, self._last_date, period="M")
File ~\PycharmProjects\okama\okama\api\data_queries.py:51, in QueryData.get_macro_ts(symbol, first_date, last_date, period)
39 @staticmethod
40 def get_macro_ts(
41 symbol: str,
(...) 44 period: str = "M",
45 ) -> pd.Series:
46 """
47 Requests api_methods.API for Macroeconomic indicators time series (monthly data).
48 - Inflation time series
49 - Bank rates time series
50 """
---> 51 csv_input = api_methods.API.get_macro(symbol=symbol, first_date=first_date, last_date=last_date, period=period)
52 return QueryData.csv_to_series(csv_input, period=period)
File ~\PycharmProjects\okama\okama\api\api_methods.py:160, in API.get_macro(cls, symbol, first_date, last_date, period)
149 @classmethod
150 def get_macro(
151 cls,
(...) 155 period: str = "m",
156 ):
157 """
158 Get macro time series (monthly).
159 """
--> 160 return cls.connect(
161 endpoint=cls.endpoint_macro,
162 symbol=symbol,
163 first_date=first_date,
164 last_date=last_date,
165 period=period,
166 )
File ~\PycharmProjects\okama\okama\api\api_methods.py:57, in API.connect(cls, endpoint, symbol, first_date, last_date, period)
55 raise requests.exceptions.HTTPError(f"{symbol} is not found in the database.", 404) from errh
56 if r.status_code == 400:
---> 57 raise requests.exceptions.HTTPError(
58 f"Bad request for {symbol}: {r.text}",
59 r.status_code,
60 request_url,
61 ) from errh
62 raise requests.exceptions.HTTPError(
63 f"HTTP error fetching data for {symbol}:",
64 r.status_code,
(...) 67 request_url,
68 ) from errh
69 return r.text
HTTPError: [Errno Bad request for USD.INFL: {"message":"first_date: Value error, The date should be in YYYY-MM-DD, YYYY-MM, DD-MM-YYYY, or MM-YYYY format; last_date: Value error, The date should be in YYYY-MM-DD, YYYY-MM, DD-MM-YYYY, or MM-YYYY format"}
] 400: 'https://api.okama.io/api/ts/macro/USD.INFL'
[ ]:
sp.names
{'SP500TR.INDX': 'S&P 500 TR (Total Return)',
'SPY.US': 'SPDR S&P 500 ETF Trust',
'VOO.US': 'Vanguard S&P 500 ETF',
'IVV.US': 'iShares Core S&P 500 ETF'}
We can check the overall tracking difference as the difference in accumulated returns.
[ ]:
sp.tracking_difference().plot();
… or see annualized values.
[ ]:
sp.tracking_difference_annualized().plot();
[ ]:
sp.tracking_difference_annualized(rolling_window=5 * 12).plot(); # rolling window is 5 years
The rolling_window parameter is also available in tracking_error(), index_beta(), and index_corr().
Tracking Error
Tracking Error is the standard deviation of the difference between the ETF and index returns.
[ ]:
sp.tracking_error().plot();
… or rolling tracking error:
[ ]:
sp.tracking_error(rolling_window=12 * 2).plot();
Beta
[ ]:
sp.index_beta().plot(); # in this example as expected beta is close to 1. Each ETF represents broad market in form of S&P 500 index
We can see that all three funds have similar beta values.
Rolling beta is also available:
[ ]:
sp.index_beta(rolling_window=12).plot();
Correlation with index
Sometimes it is useful to check the correlation between different asset types.
In this example we compare the correlation between:
US stocks (S&P 500 index)
US bonds (Vanguard mutual fund)
gold (spot prices)
US real estate (REIT ETF)
[ ]:
assets = ["SP500TR.INDX", "VBMFX.US", "GC.COMM", "VNQ.US"] # GC.COMM - gold spot prices
x = ok.AssetList(assets)
x
assets [SP500TR.INDX, VBMFX.US, GC.COMM, VNQ.US]
currency USD
first_date 2004-10
last_date 2025-05
period_length 20 years, 8 months
inflation USD.INFL
dtype: object
[ ]:
x.names
{'SP500TR.INDX': 'S&P 500 TR (Total Return)',
'VBMFX.US': 'VANGUARD TOTAL BOND MARKET INDEX FUND INVESTOR SHARES',
'GC.COMM': 'Gold (COMEX)',
'VNQ.US': 'Vanguard Real Estate Index Fund ETF Shares'}
.index_corr() calculates expanding or rolling correlation with the benchmark.[ ]:
x.index_corr().plot(); # expanding correlation rolling_window = None
rolling_window parameter in months.[ ]:
x.index_corr(rolling_window=12 * 5).plot(); # rolling window size is 5 years
It can be clearly seen that the real estate ETF (VNQ) had a higher correlation with stocks than gold or bonds during this period.
[ ]: