Forked from nicklatin/high_low_spread_estimator.py
Created
February 20, 2022 02:48
-
-
Save krishvishal/4bc56c3410e17f16d87fa82f6586028a to your computer and use it in GitHub Desktop.
Computes the high-low spread estimator, an estimate of bid-offer spreads, a measure of liquidity risk. See Corwin & Schultz (2011) for details: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1106193
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # high-low spread estimator (hlse) provides an estimate of bid-offer spreads, a measure of liquidity risk | |
| # described in Corwin and Schultz (2011): https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1106193 | |
| def hlse(df, frequency='daily'): | |
| """ | |
| Computes the high-low spread estimator, an estimate of bid-offer spreads, described by Corwin & Schultz (2011) | |
| Parameters | |
| ---------- | |
| df: DataFrame | |
| dataframe with date, open, high, low and close (OHLC) time series | |
| frequency: str | |
| 'daily' for daily bid-offer spread estimate, or 'monthly' for monthly bid-offer spread estimate, defaults to daily | |
| Returns | |
| ------- | |
| S: series | |
| time series of high-low spread estimate | |
| """ | |
| # define mid, 2 day high and 2 day low vars | |
| mid, high_2d, low_2d = (df.high + df.low)/2, df.high.rolling(2).max(), df.low.rolling(2).min() | |
| # adjustment for overnight price moves | |
| df['gap_up'], df['gap_down'] = df.low - df.close.shift(1), df.high - df.close.shift(1) | |
| df['high_adj'], df['low_adj'] = np.where(df.gap_up > 0, df.high - df.gap_up, df.high), np.where(df.gap_up > 0, df.low - df.gap_up, df.low) | |
| df['high_adj'], df['low_adj'] = np.where(df.gap_down < 0, df.high - df.gap_down, df.high), np.where(df.gap_down < 0, df.low - df.gap_down, df.low) | |
| # B beta | |
| B = (np.log(df.high_adj/df.low_adj))**2 + (np.log(df.high_adj.shift(1)/df.low_adj.shift(1)))**2 | |
| # G gamma | |
| G = (np.log(high_2d/low_2d))**2 | |
| # alpha | |
| alpha = ((np.sqrt(2 * B) - np.sqrt(B)) / (3 - 2 * np.sqrt(2))) - (np.sqrt(G/(3 - 2 * np.sqrt(2)))) | |
| # replace negative values by 0 | |
| alpha = pd.Series(np.where(alpha < 0, 0, alpha), index=alpha.index) | |
| # substitute alpha into equation 14 to get high-low spread estimate S | |
| S = (2 * (np.exp(alpha) - 1)) / (1 + np.exp(alpha)) | |
| # resample using daily mean | |
| if frequency == 'monthly': | |
| S = S.resample('M').mean() | |
| # drop NaNs | |
| S.dropna(inplace=True) | |
| return S |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment