rsheftel / pandas_market_calendars

Exchange calendars to use with pandas for trading applications
MIT License
803 stars 173 forks source link

Missing post-market ETF trading on early scheduled NYSE closing #193

Closed rmpinchback closed 2 years ago

rmpinchback commented 2 years ago

I believe I ran into a mismatch between what NYSE.schedule() returns and what you can see as actual ETF transaction history in the aftermarket on Thanksgiving. This is using pandas-market-calendars 3.4. ` import pandas as pd import pandas_market_calendars as mcal

NYSE: mcal.MarketCalendar = mcal.get_calendar('NYSE')

schedule: pd.DataFrame = NYSE.schedule(start_date='2020-11-27', end_date='2020-11-28', start='pre', end='post', tz=NYSE.tz) ` That schedule will cut off at 1pm, with no post-market activity. If you get actual SPY transactions, e.g. via the 1-min data on AlphaVantage, there is post-market activity recorded until 5pm. I'm not 100% sure if this post-market is limited to ETFs, that's just what I happen to be working with.

rmpinchback commented 2 years ago

Oh, and note that this isn't specific to just that year. I ran into it with with 2021-11-26 as well.

glossner commented 2 years ago

@rmpinchback - the NYSE calendar doesn't currently support changing the "post" market time when there is an early close. Indeed, the post market time should be from 1pm to 5pm.

I'll mark this as feature request. We also very much appreciate pull requests for enhancements.

Stryder-Git commented 2 years ago

The functionality regarding special times of anything that is not market_open/market_close is currently limited.

Although, there are two ways that these can be handled differently.

An example:

import pandas_market_calendars as mcal
import pandas as pd
from pandas.tseries.holiday import AbstractHolidayCalendar
from datetime import time

## The easiest way

nyse = mcal.get_calendar('NYSE')
schedule = nyse.schedule('2020-11-27', '2020-11-28', start='pre', end='post', force_special_times= False, tz=nyse.tz)
schedule.iloc[0]

>>
pre            2020-11-27 04:00:00-05:00
market_open    2020-11-27 09:30:00-05:00
market_close   2020-11-27 13:00:00-05:00
post           2020-11-27 20:00:00-05:00  # <-- keeps regular market times
Name: 2020-11-27 00:00:00, dtype: datetime64[ns, America/New_York]

## The harder way

class OtherNyse(nyse.__class__):
    @property
    def special_post(self):
        return [(time(17), AbstractHolidayCalendar(rules=[
            mcal.holidays_nyse.DayAfterThanksgiving1pmEarlyCloseInOrAfter1993])
                 )]

other = OtherNyse()
sched = other.schedule(start_date='2020-11-27', end_date='2020-11-28', start='pre', end='post', tz=other.tz, force_special_times= False)
sched.iloc[0]

>>
pre            2020-11-27 04:00:00-05:00
market_open    2020-11-27 09:30:00-05:00
market_close   2020-11-27 13:00:00-05:00
post           2020-11-27 17:00:00-05:00  # <-- will use the special_post times 
Name: 2020-11-27 00:00:00, dtype: datetime64[ns, America/New_York]

If force_special_times = True, it will still force the special times of market_open/marketclose, so be sure to set it to False when defining special times. Of course all dates that are not in the special.. methods will just have the regular times.

This is definitely something that can be improved, so if you have any thoughts on it, we'd be glad to hear them. Also, If you wanted to do the effort of defining special times, we would greatly appreciate a PR to share it with us.

Thanks!