Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save krishvishal/4bc56c3410e17f16d87fa82f6586028a to your computer and use it in GitHub Desktop.
Save krishvishal/4bc56c3410e17f16d87fa82f6586028a to your computer and use it in GitHub Desktop.

Revisions

  1. @nicklatin nicklatin revised this gist Dec 23, 2021. No changes.
  2. @nicklatin nicklatin revised this gist Dec 23, 2021. 1 changed file with 24 additions and 20 deletions.
    44 changes: 24 additions & 20 deletions high_low_spread_estimator.py
    Original file line number Diff line number Diff line change
    @@ -1,34 +1,36 @@
    # 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'):

    # high-low spread estimator (hlse)
    def hlse(ohlc_df, frequency='daily'):
    """
    Computes the high-low spread estimator, an estimate of bid-offer spreads, described by Corwin & Schultz (2011)
    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
    ----------
    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
    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
    time series of high-low spread estimate
    S: Series
    Datetimeindex and time series of high-low spread estimates.
    """

    # 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()
    # 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()

    # 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)
    # 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(df.high_adj/df.low_adj))**2 + (np.log(df.high_adj.shift(1)/df.low_adj.shift(1)))**2
    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
    @@ -38,9 +40,11 @@ def hlse(df, frequency='daily'):
    # 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
    return S
  3. @nicklatin nicklatin created this gist Dec 13, 2021.
    46 changes: 46 additions & 0 deletions high_low_spread_estimator.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,46 @@
    # 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