1111import json
1212import pandas as pd
1313
14- _CALCBENCH_USER_NAME = os .environ .get ("CALCBENCH_USERNAME" )
15- _CALCBENCH_PASSWORD = os .environ .get ("CALCBENCH_PASSWORD" )
16- _CALCBENCH_API_URL_BASE = "https://www.calcbench.com/api/{0}"
17- _CALCBENCH_LOGON_URL = 'https://www.calcbench.com/account/LogOnAjax'
18- _SSL_VERIFY = True
1914
20- _SESSION = None
15+ SESSION_STUFF = {'calcbench_user_name' : os .environ .get ("CALCBENCH_USERNAME" ),
16+ 'calcbench_password' : os .environ .get ("CALCBENCH_PASSWORD" ),
17+ 'api_url_base' : "https://www.calcbench.com/api/{0}" ,
18+ 'logon_url' : 'https://www.calcbench.com/account/LogOnAjax' ,
19+ 'ssl_verify' : True ,
20+ 'session' : None ,
21+ }
22+
2123
2224def _calcbench_session ():
23- global _SESSION
24- if not _SESSION :
25- if not (_CALCBENCH_PASSWORD and _CALCBENCH_USER_NAME ):
25+ session = SESSION_STUFF .get ('session' )
26+ if not session :
27+ user_name = SESSION_STUFF .get ('calcbench_user_name' )
28+ password = SESSION_STUFF .get ('calcbench_password' )
29+ if not (user_name and password ):
2630 raise ValueError ("No credentials supplied, either call set_credentials or set \
2731 CALCBENCH_USERNAME and CALCBENCH_PASSWORD environment variables." )
28- _session = requests .Session ()
29- r = _session .post (_CALCBENCH_LOGON_URL ,
30- {'email' : _CALCBENCH_USER_NAME ,
31- 'strng' : _CALCBENCH_PASSWORD ,
32+ session = requests .Session ()
33+ r = session .post (SESSION_STUFF [ 'logon_url' ] ,
34+ {'email' : user_name ,
35+ 'strng' : password ,
3236 'rememberMe' : 'true' },
33- verify = _SSL_VERIFY )
37+ verify = SESSION_STUFF [ 'ssl_verify' ] )
3438 r .raise_for_status ()
3539 if r .text != 'true' :
3640 raise ValueError ('Incorrect Credentials, use the email and password you use to login to Calcbench.' )
3741 else :
38- _SESSION = _session
39- return _SESSION
42+ SESSION_STUFF ['session' ] = session
43+
44+ return session
4045
4146def set_credentials (cb_username , cb_password ):
4247 '''Set your calcbench credentials.
4348
4449 username is the email address you use to login to calcbench.com.
4550 '''
46- global _CALCBENCH_USER_NAME
47- global _CALCBENCH_PASSWORD
48- _CALCBENCH_USER_NAME = cb_username
49- _CALCBENCH_PASSWORD = cb_password
51+ SESSION_STUFF ['calcbench_user_name' ] = cb_username
52+ SESSION_STUFF ['calcbench_password' ] = cb_password
5053 _calcbench_session () #Make sure credentials work.
5154
5255
53- def normalized_dataframe (company_identifiers ,
54- metrics ,
55- start_year ,
56- start_period ,
57- end_year ,
58- end_period ):
56+ def normalized_dataframe (company_identifiers = [],
57+ metrics = [],
58+ start_year = None ,
59+ start_period = None ,
60+ end_year = None ,
61+ end_period = None ,
62+ entire_universe = False ):
5963 '''Normalized data.
6064
6165 Get normalized data from Calcbench. Each point is normalized by economic concept and time period.
@@ -71,7 +75,8 @@ def normalized_dataframe(company_identifiers,
7175 A Pandas.Dataframe with the periods as the index and columns indexed by metric and ticker
7276 '''
7377
74- data = normalized_raw (company_identifiers , metrics , start_year , start_period , end_year , end_period )
78+ data = normalized_raw (company_identifiers , metrics , start_year ,
79+ start_period , end_year , end_period , entire_universe )
7580 if not data :
7681 return pd .DataFrame ()
7782 quarterly = start_period and end_period
@@ -91,19 +96,32 @@ def normalized_dataframe(company_identifiers,
9196 data = pd .DataFrame (data )
9297 data .set_index (keys = ['ticker' , 'metric' , 'period' ],
9398 inplace = True )
94- data = data .unstack ('metric' )['value' ]
95- data = data .unstack ('ticker' )
99+ data = data .unstack ('ticker' )['value' ]
96100 data = data [metrics ]
97101 return data
98102
103+
104+
105+ def _build_quarter_period (data_point ):
106+ return pd .Period (year = data_point .pop ('calendar_year' ),
107+ quarter = data_point .pop ('calendar_period' ),
108+ freq = 'q' )
109+
110+ def _build_annual_period (data_point ):
111+ data_point .pop ('calendar_period' )
112+ return pd .Period (year = data_point .pop ('calendar_year' ), freq = 'a' )
113+
114+
115+
99116normalized_data = normalized_dataframe # used to call it normalized_data.
100117
101- def normalized_raw (company_identifiers ,
102- metrics ,
103- start_year ,
104- start_period ,
105- end_year ,
106- end_period ):
118+ def normalized_raw (company_identifiers = [],
119+ metrics = [],
120+ start_year = None ,
121+ start_period = None ,
122+ end_year = None ,
123+ end_period = None ,
124+ entire_universe = False ):
107125 '''
108126 Normalized data.
109127
@@ -138,21 +156,24 @@ def normalized_raw(company_identifiers,
138156 },
139157 ]
140158 '''
159+ if bool (company_identifiers ) == entire_universe :
160+ raise ValueError ("Must pass either company_identifiers or entire_universe=True" )
141161
142162
143- url = _CALCBENCH_API_URL_BASE .format ("/NormalizedValues" )
163+ url = SESSION_STUFF [ 'api_url_base' ] .format ("/NormalizedValues" )
144164 payload = {"start_year" : start_year ,
145165 'start_period' : start_period ,
146166 'end_year' : end_year ,
147167 'end_period' : end_period ,
148168 'company_identifiers' : list (company_identifiers ),
149169 'metrics' : metrics ,
170+ 'entire_universe' : entire_universe
150171 }
151172 response = _calcbench_session ().post (
152173 url ,
153174 data = json .dumps (payload ),
154175 headers = {'content-type' : 'application/json' },
155- verify = _SSL_VERIFY
176+ verify = SESSION_STUFF [ 'ssl_verify' ]
156177 )
157178 response .raise_for_status ()
158179 data = response .json ()
@@ -208,7 +229,7 @@ def as_reported_raw(company_identifier,
208229 ...]
209230 }
210231 '''
211- url = _CALCBENCH_API_URL_BASE .format ("asReported" )
232+ url = SESSION_STUFF [ 'api_url_base' ] .format ("asReported" )
212233 payload = {'companyIdentifier' : company_identifier ,
213234 'statementType' : statement_type ,
214235 'periodType' : period_type ,
@@ -217,7 +238,7 @@ def as_reported_raw(company_identifier,
217238 response = _calcbench_session ().get (url ,
218239 params = payload ,
219240 headers = {'content-type' : 'application/json' },
220- verify = _SSL_VERIFY )
241+ verify = SESSION_STUFF [ 'ssl_verify' ] )
221242 response .raise_for_status ()
222243 data = response .json ()
223244 return data
@@ -257,40 +278,29 @@ def breakouts_raw(company_identifiers=None, metrics=[], start_year=None,
257278 'endPeriod' : end_period ,
258279 'periodType' : period_type },
259280 'pageParameters' : {'metrics' : metrics }}
260- url = _CALCBENCH_API_URL_BASE .format ('breakouts' )
281+ url = SESSION_STUFF [ 'api_url_base' ] .format ('breakouts' )
261282 response = _calcbench_session ().post (url ,
262283 data = json .dumps (payload ),
263284 headers = {'content-type' : 'application/json' },
264- verify = _SSL_VERIFY )
285+ verify = SESSION_STUFF [ 'ssl_verify' ] )
265286 response .raise_for_status ()
266287 data = response .json ()
267288 return data
268289
269-
270-
271- def _build_quarter_period (data_point ):
272- return pd .Period (year = data_point .pop ('calendar_year' ),
273- quarter = data_point .pop ('calendar_period' ),
274- freq = 'q' )
275-
276- def _build_annual_period (data_point ):
277- data_point .pop ('calendar_period' )
278- return pd .Period (year = data_point .pop ('calendar_year' ), freq = 'a' )
279290
280-
281291def tickers (SIC_codes = [], index = None , universe = False ):
282292 '''Return a list of tickers in the peer-group'''
283293 companies = _companies (SIC_codes , index , universe )
284294 tickers = [co ['ticker' ] for co in companies ]
285295 return tickers
286296
287- def companies (SIC_codes = [], index = None , universe = False ):
297+ def companies (SIC_codes = [], index = None , entire_universe = False ):
288298 '''Return a DataFrame with company details'''
289- companies = _companies (SIC_codes , index , universe )
299+ companies = _companies (SIC_codes , index , entire_universe )
290300 return pd .DataFrame (companies )
291301
292- def _companies (SIC_codes , index , universe = False ):
293- if not (SIC_codes or index or universe ):
302+ def _companies (SIC_codes , index , entire_universe = False ):
303+ if not (SIC_codes or index or entire_universe ):
294304 raise ValueError ('Must supply SIC_code or index' )
295305 query = "universe=true"
296306 if index :
@@ -299,28 +309,34 @@ def _companies(SIC_codes, index, universe=False):
299309 query = "index={0}" .format (index )
300310 elif SIC_codes :
301311 query = '&' .join ("SICCodes={0}" .format (SIC_code ) for SIC_code in SIC_codes )
302- url = _CALCBENCH_API_URL_BASE .format ("companies?" + query )
303- r = _calcbench_session ().get (url , verify = _SSL_VERIFY )
312+ url = SESSION_STUFF [ 'api_url_base' ] .format ("companies?" + query )
313+ r = _calcbench_session ().get (url , verify = SESSION_STUFF [ 'ssl_verify' ] )
304314 r .raise_for_status ()
305315 return r .json ()
306316
307317
308318def _test_locally ():
309- global _CALCBENCH_API_URL_BASE
310- global _CALCBENCH_LOGON_URL
311- global _SSL_VERIFY
312- _CALCBENCH_API_URL_BASE = "https://localhost:444/api/{0}"
313- _CALCBENCH_LOGON_URL = 'https://localhost:444/account/LogOnAjax'
314- _SSL_VERIFY = False
315- print (companies (all_companies = True ))
319+ SESSION_STUFF ['api_url_base' ] = "https://localhost:444/api/{0}"
320+ SESSION_STUFF ['logon_url' ] = 'https://localhost:444/account/LogOnAjax'
321+ SESSION_STUFF ['ssl_verify' ] = False
322+
323+
324+ normalized_data (entire_universe = True ,
325+ metrics = ['revenue' , 'assets' , ],
326+ start_year = 2010 ,
327+ start_period = 1 ,
328+ end_year = 2014 ,
329+ end_period = 4 )
330+ print (companies (entire_universe = True ))
316331 print (breakouts_raw (['msft' ], ['operatingSegmentRevenue' ]))
317332
318333if __name__ == '__main__' :
319- data = normalized_data (company_identifiers = ['ibm' , 'msft' ],
334+ _test_locally ()
335+ data = normalized_data (entire_universe = True ,
320336 metrics = ['revenue' , 'assets' , ],
321337 start_year = 2010 ,
322338 start_period = 1 ,
323339 end_year = 2014 ,
324340 end_period = 4 )
325341 print (data )
326-
342+
0 commit comments