The Variance Day Model
- Volatility is defined as the standard deviation of price returns.
- Variance is the square of the standard deviation.
- Variance is proportional to time as \(\sigma^2 \tau\)
In a previous post, we’ve established that
- Time passes too fast over weekends in a calendar day model
- Time passes too slow over weekends in a business day model
- The truth is somewhere in between.
Colloquially, one could say that:
- a calendar day model assigns each day of the year 1/365 of the total variance.
- a business day model assigns each business day of the year 1/252 of the variance.
Variance Schedule
Before we begin, I want to emphasize that problems in finance don’t always have a unique and objectively best solution. There aren’t theories of everything that can predict events with a 100% accuracy like in physics and mathematics. So there is plenty of room for creativity within this template.
In this simple model, we assign:
- a variance weight of 1 to business days
- a variance weight of 0.25 to all weekends and holidays.
We start by creating a pandas DataFrame containing all the dates of the calendar year.
import pandas as pd
# Generate date range
date_range = pd.date_range(start='2023-01-01', end='2023-12-31')
# Create a DataFrame with the date range
df = pd.DataFrame({'Date': date_range})
We can then assign the variance weights to each day in 2023 like so:
# US holidays in 2023
holidays = [
date(2023, 1, 2), # New Year's Day observed
date(2023, 1, 16), # Martin Luther King Jr. Day
date(2023, 2, 20), # Washington's Birthday
date(2023, 4, 7), # Good Friday
date(2023, 5, 29), # Memorial Day
date(2023, 6, 19), # Juneteenth National Independence Day
date(2023, 7, 3), # Independence Day observed
date(2023, 7, 4), # Independence Day
date(2023, 9, 4), # Labor Day
date(2023, 11, 23), # Thanksgiving Day
date(2023, 11, 24), # Day after Thanksgiving
date(2023, 12, 25), # Christmas Day
]
# Assign weights based on whether the date corresponds to a weekday or weekend
df['Weight'] = df['Date'].apply(lambda x: 1 if x.weekday() < 5 else 0.25)
# Update the 'Weight' column to include holidays
df.loc[df['Date'].isin(holidays), 'Weight'] = 0.25
After determining the assigned weights, the next step involves calculating the duration of a year (how much time is a year?). This calculation is essential as it provides the necessary input, the time to expiration in years, for any standard option pricing model. We will call this the tenor. In a calendar day model, the tenor is 365. In our case, the number should hang around 280.
# Calculate the time between every calendar date and December 31st.
df['DTE'] = df['Weight'][::-1].cumsum()[::-1]
# Time between Jan1st and Dec31st is the tenor
tenor = df['DTE'].iloc[0] # ~= 280
Try and see what you get for the year 2024. Anyway, save this dataframe as a csv file because you will need it everytime you want to price an option.
Finally, to determine the time to expiration in years, you only have to find the difference in the ‘DTE’ value between the expiration date and the current date. You can open the csv file and manually calculate that difference or, if you have thousands of options to price, you do it programmatically:
def calculate_time_until_expiration(variance_weights_df, current_date, event_date, tenor):
# Normalize dates
event_date = pd.to_datetime(event_date).normalize()
current_date = pd.to_datetime(current_date).normalize()
days_to_event = variance_weights_df[
variance_weights_df['Date'] == event_date
]['DTE'].iloc[0]
days_to_current = variance_weights_df[
variance_weights_df['Date'] == current_date
]['DTE'].iloc[0]
# Ensure that the current date's DTE is greater than
# the event date's DTE
return (days_to_current - days_to_event + 0.5) / tenor
As we know, time doesn’t pass uniformly across the calendar. Since the regular trading hours start at 9:30am ET and end at 4:00pm ET, the time you plug into your model could be VERY sensitive to the time left before the close. In our simple model, I chose to add an increment of 0.5 to make sure that the time that the function returns isn’t 0. Roughly, that 0.5 equates to half a trading day left.
Finally, we obtain the elusive variance time by normalizing that time with the tenor. I leave it to the reader as an exercise to write a vectorized function capable of taking in an entire options chain instead of one option at a time.
Conclusion
So now that we got a more accurate representation of time, the next step is to think about how we can use it to our advantage. In a following post, we will explore how one can use variance time to calibrate the volatilities as implied by the prices of the options.