# high-low spread estimator (hlse) def hlse(ohlc_df, frequency='daily'): """ 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 Parameters ---------- ohlc_df: DataFrame DataFrame with DatetimeIndex and Open, High, Low and Close (OHLC) prices from which to compute the high-low spread estimates. frequency: str, {'daily', 'weekly', 'monthly'}, default 'daily' daily: daily bid-offer spread estimate. weekly: weekly bid-offer spread estimate, resampled over a weekly frequency as the mean of daily estimates. monthly: monthly bid-offer spread estimate, resampled over a monthly frequency as the mean of daily estimates. Returns ------- S: Series Datetimeindex and time series of high-low spread estimates. """ # define vars: mid, 2 day high and 2 day low vars mid, high_2d, low_2d = (ohlc_df.high + ohlc_df.low)/2, ohlc_df.high.rolling(2).max(), ohlc_df.low.rolling(2).min() # compute adjustment for overnight price moves ohlc_df['gap_up'], ohlc_df['gap_down'] = ohlc_df.low - ohlc_df.close.shift(1), ohlc_df.high - ohlc_df.close.shift(1) # adjustment for gap up ohlc_df['high_adj'], ohlc_df['low_adj'] = np.where(ohlc_df.gap_up > 0, ohlc_df.high - ohlc_df.gap_up, ohlc_df.high), np.where(ohlc_df.gap_up > 0, ohlc_df.low - ohlc_df.gap_up, ohlc_df.low) # adjustment for gap down ohlc_df['high_adj'], ohlc_df['low_adj'] = np.where(ohlc_df.gap_down < 0, ohlc_df.high - ohlc_df.gap_down, ohlc_df.high), np.where(ohlc_df.gap_down < 0, ohlc_df.low - ohlc_df.gap_down, ohlc_df.low) # B beta B = (np.log(ohlc_df.high_adj/ohlc_df.low_adj))**2 + (np.log(ohlc_df.high_adj.shift(1)/ohlc_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 == 'weekly': S = S.resample('W').mean() if frequency == 'monthly': S = S.resample('M').mean() # drop NaNs S.dropna(inplace=True) return S