Primer for the Pokemon Fair Value bond model
The intuition behind the model presented here is relatively simple - the “Fair Value” of a long dated bond should be a function of short-term interest rates, inflation and economic growth. Additionally as capital allocation becomes increasing global it is reasonable that our “Fair Value” should include a global factor.
$ FV_{x} = \beta_{x,0} +\beta_{x,1}\times \text{Rates} +\beta_{x,2} \times \text{Inf} +\beta_{x,3} \times \text{Growth} +\beta_{x,4}\times \text{Global} +\epsilon_{x}$
Mathematically we are indifferent to the choice of input, as we are simply calculating a multi-variate linear regression over several rolling windows; practically variable selection is probably the most challenging part of the model. If we assume markets attempt to incorporate forward estimates into prices than similarly we would prefer to incorporate forward looking macro variables into our estimates. Exact inputs are covered below, but in general:
- Short-Rates - forward path of cash rates is modelled by the current 2-year rate
- Inflation - ideally taken from market implied inflation from breakeven rates or inflation swaps
- Growth - survey based (ISM) or from Composite Leading Indicators like the OECD CLIs
- Global Factor - most difficult but could be the BIS REER or a blend of global bonds
Fair Value Output
Sample Python Code
Where the Pokemon FV model has been presented elsewhere in the chartbook it has been calculated using a homemade class function - eventually this will be wrapped and posted to PyPi - but the code below is a sample of what is happening.
def pokemon(df, window=36, valiadate_data=True):
# REQUIREMENTS:
# from sklearn.linear_model import LinearRegression
# INPUTS:
# df - DataFrame with datetimeindex, col[0] as long bond & col[1+] as regression variables
# window - rolling regression window; default is 36 months
# validate_data - forward fills missing data (useful where we may be missing current month)
# OUTPUTS:
# guess - DataFrame with long-bond, full sample and rolling model outputs
# stats - coefficients for each thingy
if valiadate_data is True:
df.fillna(method='ffill', inplace=True) # Forward fill missing data
df = df[~df.isna().any(axis=1)] # Each row complete
# Dummy Dataframe(s)
guess = pd.DataFrame(data=df.iloc[:,0])
guess.columns = ['long_bond']
coeff_vn = ['r2', 'intercept']
coeff_vn.extend(list(df.columns.values)[1:])
stats = pd.DataFrame(columns=coeff_vn)
# Full Sample Period
y, X = df.iloc[:,0], df.iloc[:,1:]
lm = LinearRegression().fit(X,y)
guess['full_sample'] = lm.predict(X)
stats.loc['full_sample',['r2','intercept']]=lm.score(X, y),lm.intercept_
stats.loc['full_sample'].iloc[2:] = lm.coef_
# Rolling Window
for i, v in enumerate(guess.index):
if i < window:
continue
y = df.iloc[i-window:i,0] # Dependant Var [long bond]
X = df.iloc[i-window:i,1:] # Independant [short, inf, growth]
roll_lm = LinearRegression().fit(X,y)
guess.loc[v,'rolling'] = roll_lm.predict(X)[-1]
stats.loc[v,['r2', 'intercept']] = roll_lm.score(X, y), lm.intercept_
stats.loc[v].iloc[2:] = roll_lm.coef_
return guess, stats
Data Sources
All data is open sourced and comes from Quandl. For the Pokemon model this means a compromise between what we would consider to be the optimal data and what is easily achievable. Below note the data used here (and consider some better data).
US Treasuries
- US Treasury for nominal 10 & 2 year yields
- UMich 5-year Median Inflation Expectations
- ISM Mfg Composite for Growth estimates
Gilts
- Bank-of-England 10-year Nominal Zero-Coupon as a proxy for the standard nominal
- ECB provide the 3m GBP LIBOR rate; need a better source but the BoE doesn’t publish the 2 (or 1) year gilt yields or forward rate data.
- Rate Inflation
- OECD Amplitude-Adjusted Composite Leading Indicator
Bunds
- Bundesbank for the 10-year & 2-year Bund yield. This needs further investigation as the Quandl dataset for the German 2-year only goes back to Jan-14, which isn’t enough for a sensible full-sample model
- Rate Inflation
- OECD Amplitude-Adjusted Composite Leading Indicator
JGBs
- Ministry of Finance Japan for the 10-year & 2-year JGBs
- Rate Inflation
- OECD Amplitude-Adjusted Composite Leading Indicator
Small Print
It should come as no surprise that the concept presented here is not new. Any CFA candidate who has has been even vaguely dilligent should recognise the basis. NDR do some excellent modelling and often present a simplified version and Goldman Sachs Research have been incorporating a forward looking Sudoku Fair Value model in their Fixed Income research since 2007; the GS model is probably superior as they take more time developing a trade-weighted variable for the “Global” factor.
Pokemon is indeed a nod to the childrens cartoon series. The point at which the model was developed happened to coincide with the Pokemon Go phenomenon in the UK during which time several PMs and Analysts were found wondering Green Park in London in search of Pokemon.