Skip to content

Instantly share code, notes, and snippets.

@lionelyoung
Created May 6, 2017 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lionelyoung/ab27f5a751cb1c75e20273a052d7d671 to your computer and use it in GitHub Desktop.
Save lionelyoung/ab27f5a751cb1c75e20273a052d7d671 to your computer and use it in GitHub Desktop.
Calculate the last trading day given a contract_month
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