Roll Model에서 지적된 단점 중 하나는 관찰된 가격 계열인 가 close price에만 종속된다는 점이었다. Beckers는 고가-저가에 근거한 변동성 추정량이 종가만을 사용한 변동성 추정량보다 더 정확하다는 것을 보였다. Parkinson은 Geometric Brownian Motion(GBM)을 따르는 연속 시계열 가격에 대해 다음을 유도하였다.
여기서 , 이다. 이를 통해 변동성 은 관측된 high-low price를 기초로 하여 안정적으로 추정할 수 있다.
Corwin, Schultz(2012)는 Beckers(1983)의 연구를 바탕으로 high-low price로부터 bis-ask spread 추정량을 제안하였다. 그 추정량은 두 가지 원칙에 기반을 두고 있다.
Corwin, Schultz는 bid-ask spread를 가격의 퍼센티지로 다음과 같이 계산할 수 있다는 것을 보였다.
여기서
class CorwinSchultz :
def __init__(self, high : pd.Series, low : pd.Series) -> None:
self.high = high
self.low = low
def beta(self, window : int) -> pd.Series:
ret = np.log(self.high / self.low)
high_low_ret = ret ** 2
beta = high_low_ret.rolling(window=2).sum()
beta = beta.rolling(window=window).mean()
return beta
def gamma(self) -> pd.Series:
high_max = self.high.rolling(window = 2).max()
low_min = self.low.rolling(window = 2).min()
gamma = np.log(high_max / low_min) ** 2
return gamma
def alpha(self, window : int) -> pd.Series:
den = 3 - 2 * 2 ** .5
alpha = (2 ** .5 - 1) * (self.beta(window = window) ** .5) / den
alpha -= (self.gamma() / den) ** .5
alpha[alpha < 0] = 0
return alpha
def corwin_schultz_estimator(self, window : int = 20) -> pd.Series :
alpha_ = self.alpha(window = window)
spread = 2 * (np.exp(alpha_) - 1) / (1 + np.exp(alpha_))
start_time = pd.Series(self.high.index[0:spread.shape[0]], index=spread.index)
spread = pd.concat([spread, start_time], axis=1)
spread.columns = ['Spread', 'Start_Time']
return spread.Spread
def becker_parkinson_vol(self, window: int = 20) -> pd.Series:
Beta = self.beta(window = window)
Gamma = self.gamma()
k2 = (8 / np.pi) ** 0.5
den = 3 - 2 * 2 ** .5
sigma = (2 ** -0.5 - 1) * Beta ** 0.5 / (k2 * den)
sigma += (Gamma / (k2 ** 2 * den)) ** 0.5
sigma[sigma < 0] = 0
return sigma