Created
May 6, 2017 17:39
-
-
Save lionelyoung/ab27f5a751cb1c75e20273a052d7d671 to your computer and use it in GitHub Desktop.
Calculate the last trading day given a contract_month
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
from dateutil.relativedelta import relativedelta | |
import pandas as pd | |
from pandas.tseries.offsets import BDay | |
def crude_futures_lasttradingday(contract_month): | |
"""Trading in the current delivery month shall cease on the third business | |
day prior to the twenty-fifth calendar day of the month preceding the | |
delivery month. If the twenty-fifth calendar day of the month is a | |
non-business day, trading shall cease on the third business day prior to the | |
last business day preceding the twenty-fifth calendar day. In the event that | |
the official Exchange holiday schedule changes subsequent to the listing of | |
a Crude Oil futures, the originally listed expiration date shall remain in | |
effect. In the event that the originally listed expiration day is declared a | |
holiday, expiration will move to the business day immediately prior. | |
Translation: | |
- subtract 3 working days from the 25th if is a business day (take note of holidays) | |
- subtract 4 working days if 25th is a non-business day (take note of holidays) | |
contract_month is in YYYYMM format, to match IB API""" | |
# TODO: Store holidays in db table instead of hardcoding | |
# http://www.cmegroup.com/tools-information/holiday-calendar.html#cmeClearport | |
holidays = [ | |
datetime.datetime(2017,1,16), | |
datetime.datetime(2017,2,20), | |
datetime.datetime(2017,4,14), | |
datetime.datetime(2017,5,29), | |
datetime.datetime(2017,7,4), | |
datetime.datetime(2017,9,4), | |
datetime.datetime(2017,10,9), | |
datetime.datetime(2017,11,23), | |
datetime.datetime(2017,12,25), | |
datetime.datetime(2018,1,1), | |
] | |
contract_month_dt = datetime.datetime.strptime(contract_month, "%Y%m") | |
expiration_month_dt = contract_month_dt - relativedelta(months=1) | |
logger.debug("contract_month_dt is {} & expiration_month_dt is {}".format(contract_month_dt, expiration_month_dt)) | |
# http://www.cmegroup.com/trading/energy/crude-oil/light-sweet-crude_contract_specifications.html | |
# subtract 3 working days from the 25th | |
# subtract 4 working days if 25th is a non-business day | |
# if the 25th is a business day | |
if expiration_month_dt.replace(day=25).weekday() < 5: | |
last_trading_day_dt = expiration_month_dt.replace(day=25) - BDay(3) | |
# if the 25th is NOT a business day | |
else: | |
last_trading_day_dt = expiration_month_dt.replace(day=25) - BDay(4) | |
# Find holiday in expiration month and take note of the ones that land on weekdays and between the calc date and 25th | |
holiday_months = [(dt.month, dt) for dt in holidays] | |
holidays_in_expiration_month_on_weekdays = [dt for current_month, dt in holiday_months | |
if current_month == expiration_month_dt.month | |
and dt.weekday() < 5 | |
and dt.day <= 25 | |
and dt.day > last_trading_day_dt.day] | |
# if a holiday is between this last trading day and lands on a weekday, take another Bday off | |
last_trading_day_dt = last_trading_day_dt - BDay(len(holidays_in_expiration_month_on_weekdays)) | |
return last_trading_day_dt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment