• Home
  • About

Measuring Gap Opening Contributions in Stock Returns Using Python

Programming
Programming/Python
Published

September 29, 2024

This post is the continuation of a previous post: Calculating the Weight of Gap Openings in a Stock Using SQL; the subject is the same but the application is via python programming language.

The stock data includes dividend adjustments.

The pandas package is used throughout the application.

import pandas as pd
MSFT = pd.read_csv("MSFT, 1D adjusted with dividents.csv")
# select necessary columns and rename column "time" as "date"
MSFT_open_close = MSFT[['time', 'open', 'close']].rename(columns={'time': 'date'})

# filter year 2023
MSFT_open_close = MSFT_open_close[(MSFT_open_close['date'] >= '2023-01-01') & (MSFT_open_close['date'] < '2024-01-01')]

The structure of the table MSFT_open_close is as follows:

MSFT_open_close.head()
date open close
0 2023-01-03 239.633909 236.183528
1 2023-01-04 228.982089 225.852101
2 2023-01-05 223.979036 219.158361
3 2023-01-06 219.838579 221.741218
4 2023-01-09 223.239669 223.900171


Let’s do a warm-up round to calculate the columns for the daily changes, the lagged closings, and the opening gap.

# calculate daily difference
MSFT_open_close['intraday_diff'] = MSFT_open_close['close'] - MSFT_open_close['open']

# convert date column to datetime
MSFT_open_close['date'] = pd.to_datetime(MSFT_open_close['date'])

# create lagged close column
MSFT_open_close['close_lag_1'] = MSFT_open_close['close'].shift(1)

# calculate opening gap
MSFT_open_close['opening_gap'] = MSFT_open_close['open'] - MSFT_open_close['close_lag_1']
MSFT_open_close.head()
date open close intraday_diff close_lag_1 opening_gap
0 2023-01-03 239.633909 236.183528 -3.450381 NaN NaN
1 2023-01-04 228.982089 225.852101 -3.129989 236.183528 -7.201439
2 2023-01-05 223.979036 219.158361 -4.820676 225.852101 -1.873064
3 2023-01-06 219.838579 221.741218 1.902639 219.158361 0.680218
4 2023-01-09 223.239669 223.900171 0.660502 221.741218 1.498451


Now, it’s time to add the rest of the calculations step by step.

# perform aggregations
aggregations = pd.DataFrame({
    'beginning_of_period': [MSFT_open_close['date'].min()],
    'end_of_period': [MSFT_open_close['date'].max()],
    'sum_gap_openings': [MSFT_open_close['opening_gap'].sum(skipna=True)],
    'sum_intraday_diff': [MSFT_open_close['intraday_diff'].sum()],
    'opening_period': [MSFT_open_close.loc[MSFT_open_close['date'] == MSFT_open_close['date'].min(), 'open'].values[0]],
    'closing_period': [MSFT_open_close.loc[MSFT_open_close['date'] == MSFT_open_close['date'].max(), 'close'].values[0]]
})
aggregations.head()
beginning_of_period end_of_period sum_gap_openings sum_intraday_diff opening_period closing_period
0 2023-01-03 2023-12-29 72.650797 61.711294 239.633909 373.996


And the final calculations…

aggregations['pct_change_period'] = (aggregations['closing_period'] / aggregations['opening_period'] - 1).round(2)
aggregations['pct_gap_openings'] = (aggregations['sum_gap_openings'] / (aggregations['closing_period'] - aggregations['opening_period'])).round(2)
aggregations['pct_intraday_changes'] = (aggregations['sum_intraday_diff'] / (aggregations['closing_period'] - aggregations['opening_period'])).round(2)
aggregations[['opening_period', 'closing_period', 'pct_change_period', 'sum_gap_openings', 'sum_intraday_diff', 'pct_gap_openings', 'pct_intraday_changes']]
opening_period closing_period pct_change_period sum_gap_openings sum_intraday_diff pct_gap_openings pct_intraday_changes
0 239.633909 373.996 0.56 72.650797 61.711294 0.54 0.46


Conclusion

In 2023, MSFT increased by 56%. Within the total increase in 2023, the weight of gap openings was 54%, and the weight of intraday changes was 46%.

As an extreme example, if one buys at the opening price at the beginning of each trading day and sells at the closing price, 54% of the total rise during the year would be missed compared to buying at the beginning of 2023 and selling at the end. In other words, 54% of the annual increase would be missed; this means missing out on a (56% x 54% = ) 30% profit relative to the index value at the beginning of the year. This shows that gap openings can have a significant impact in the long term.

Built with Quarto and Netlify