Reputation: 19486
I'm writing a program that processes contracts that expire different months.
For example, a corn futures contract which expires in December 2016 would be called:
CZ2016
where C means 'corn', Z means December (it's custom).
Internally I represent everything as (year, month)
.
Each contract type has a different format, and ideally I need to be able to go in both directions.
Currently I have written some elaborate string processing to do this, but it's a mess.
What is the most pythonic way to do this?
Upvotes: 0
Views: 88
Reputation: 15310
Here's something quick(ish):
#!python3
import collections
import time
class FuturesContract:
Commodity = collections.namedtuple('Commodity',
'symbol future exchange delivery_months')
Commodities = [
# Source: http://www.mrci.com/web/online-explanation-pages/symbols-a-codes.html
# !IMPORTANT! Sorted by length of symbol!!!
Commodity('ITCO', 'Brent Crude', 'ICE', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('AD', 'Australian Dollar', 'CME', 'H,M,U,Z'),
Commodity('BP', 'British Pound', 'CME', 'H,M,U,Z'),
Commodity('CD', 'Canadian Dollar', 'CME', 'H,M,U,Z'),
Commodity('DX', 'US Dollar Index', 'ICE', 'H,M,U,Z'),
Commodity('EU', 'EuroFx', 'CME', 'H,M,U,Z'),
Commodity('JY', 'Japanese Yen', 'CME', 'H,M,U,Z'),
Commodity('SF', 'Swiss Franc', 'CME', 'H,M,U,Z'),
Commodity('CL', 'Crude Oil', 'NYM', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('HO', 'NY Harbor ULSD/Heating Oil', 'NYM', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('HU', 'Unleaded Gas', 'NYM', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('NG', 'Natural Gas', 'NYM', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('RB', 'RBOB Gasoline', 'NYM', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('BO', 'Soybean Oil', 'CBOT', 'F,H,K,N,Q,U,V,Z'),
Commodity('KW', 'Kansas City Wheat', 'KCBT', 'H,K,N,U,Z'),
Commodity('MW', 'Minneapolis Wheat', 'MGE', 'H,K,N,U,Z'),
Commodity('SM', 'Soybean Meal', 'CBOT', 'F,H,K,N,Q,U,V,Z'),
Commodity('DJ', 'Dow Jones Industrials', 'CBOT', 'H,M,U,Z'),
Commodity('KV', 'Value Line (Discontinued)', 'KCBT', 'H,M,U,Z'),
Commodity('MV', 'Value Line (Mini)', 'KCBT', 'H,M,U,Z'),
Commodity('ND', 'Nasdaq-100', 'CME', 'H,M,U,Z'),
Commodity('RL', 'Russell 2000 (Discontinued)', 'CME', 'H,M,U,Z'),
Commodity('SP', 'S & P 500', 'CME', 'H,M,U,Z'),
Commodity('YU', 'NYSE Composite (Discontinued)', 'NYFE', 'H,M,U,Z'),
Commodity('ED', 'Eurodollars', 'CME', 'H,M,U,Z'),
Commodity('FV', '5-Yr T-Notes', 'CBOT', 'H,M,U,Z'),
Commodity('MB', 'Municipal Bonds', 'CBOT', 'H,M,U,Z'),
Commodity('TU', '2-Yr T-Notes', 'CBOT', 'H,M,U,Z'),
Commodity('TY', '10-Yr T-Notes', 'CBOT', 'H,M,U,Z'),
Commodity('US', '30-Yr T-Bonds', 'CBOT', 'H,M,U,Z'),
Commodity('FC', 'Feeder Cattle', 'CME', 'F,H,J,K,Q,U,V,X'),
Commodity('LC', 'Live Cattle', 'CME', 'G,J,M,Q,V,Z'),
Commodity('LH', 'Lean Hogs', 'CME', 'G,J,K,M,N,Q,V,Z'),
Commodity('LE', 'Lean Hogs', 'CME', 'G,J,K,M,N,Q,V,Z'),
Commodity('PB', 'Pork Bellies', 'CME', 'G,H,K,N,Q'),
Commodity('DA', 'Milk Class III', 'CME', 'F,G,H,J,K,M,N,Q,U,V,X,Z'),
Commodity('GC', 'Gold', 'CMX', 'G,J,M,Q,V,Z'),
Commodity('HG', 'Copper', 'CMX', 'H,K,N,U,Z'),
Commodity('PL', 'Platinum', 'NYM', 'F,J,N,V'),
Commodity('SI', 'Silver', 'CMX', 'H,K,N,U,Z'),
Commodity('RR', 'Rice', 'CBOT', 'F,H,K,N,U,X'),
Commodity('CC', 'Cocoa', 'ICE', 'H,K,N,U,Z'),
Commodity('CT', 'Cotton', 'ICE', 'H,K,N,V,Z'),
Commodity('KC', 'Coffee', 'ICE', 'H,K,N,U,Z'),
Commodity('LB', 'Lumber', 'CME', 'F,H,K,N,U,X'),
Commodity('JO', 'Orange Juice', 'ICE', 'F,H,K,N,U,X'),
Commodity('SB', 'Sugar #11', 'ICE', 'H,K,N,V'),
Commodity('C', 'Corn', 'CBOT', 'F,H,K,N,U,X,Z'),
Commodity('O', 'Oats', 'CBOT', 'H,K,N,U,Z'),
Commodity('S', 'Soybeans', 'CBOT', 'F,H,K,N,Q,U,X'),
Commodity('W', 'Wheat', 'CBOT', 'H,K,N,U,Z'),
]
Months = {
1: 'January',
2: 'February',
3: 'March',
4: 'April',
5: 'May',
6: 'June',
7: 'July',
8: 'August',
9: 'September',
10: 'October',
11: 'November',
12: 'December',
'F': 'January',
'G': 'February',
'H': 'March',
'J': 'April',
'K': 'May',
'M': 'June',
'N': 'July',
'Q': 'August',
'U': 'September',
'V': 'October',
'X': 'November',
'Z': 'December',
}
MonthCodes = {
'January': 'F',
'February': 'G',
'March': 'H',
'April': 'J',
'May': 'K',
'June': 'M',
'July': 'N',
'August': 'Q',
'September': 'U',
'October': 'V',
'November': 'X',
'December': 'Z',
}
def __init__(self, details=None):
if details is None:
now = time.localtime()
month = now.tm_mon
year = now.tm_year
self.commodity = None
else:
for c in self.Commodities:
if details.startswith(c.symbol):
self.commodity = c
symlen = len(c.symbol)
month = details[symlen:symlen+1]
year = details[symlen+1:]
if len(year) == 2:
year = '20'+year
year = int(year)
break
else:
raise ValueError("Unparseable details given: "+details)
month = self.Months[month]
if self.commodity is not None:
month_code = self.MonthCodes[month]
if month_code not in self.commodity.delivery_months:
raise ValueError('Invalid delivery month for commodity: '+month)
self.month = month
self.year = year
def __str__(self):
c = self.commodity
return 'Unspecified' if c is None else c.symbol + self.MonthCodes[self.month] + str(self.year)
def explain(self):
c = self.commodity
return [ 'A FuturesContract for:',
'\t{}: {} (on the {} exchange)'.format(c.symbol, c.future, c.exchange),
'\tDated: {} ({}), {}'.format(self.month, self.MonthCodes[self.month], self.year),
]
cz2016 = FuturesContract('CZ2016')
print('\n'.join(cz2016.explain()))
print('\n')
print('Commodity:', cz2016.commodity)
print('Month:', cz2016.month)
print('Year:', cz2016.year)
print("\nHas __str__:", cz2016)
print("\n\n")
print("Future LBQ2014: (should be invalid, because Q):")
fc = FuturesContract('LBQ2014')
Upvotes: 3