Maneuvering WRDS Data
0. Setup WRDS Python API
Did you know that there’s a WRDS python API?
Download the package and run pip install wrds in your terminal on Mac or Anaconda prompt in PC.
1. Import WRDS Package
Just like how you import other common python packages:
import wrds2. Establish Connection
You will need your WRDS username and password (yes, the ones you use for logging onto WRDS website) to establish the connection. Once you run this code, you will be prompted to enter those info. In the code below, I name the wrds connection as “conn”. And you will see this referred to repeatedly in later parts of this exercise. Of course, you can name it any other way to your liking.
conn = wrds.Connection()If you see the following message after running the code above, it means you’ve successfully established the connection!
Enter your WRDS username [your_username]:
Enter your password:
WRDS recommends setting up a .pgpass file.
Create .pgpass file now [y/n]?: y
Created .pgpass file successfully.
You can create this file yourself at any time with the create_pgpass_file() function.
Loading library list...
Done
Once the .pgpass file is set up, later connections skip the username and password prompts and only show the last two lines.
3. Explore Your Library and Table
For SAS programmers, we tend to refer to databases on WRDS as “libraries”, and specific data items within that database “datasets”. In python language, we refer to these as “libraries” and “tables”. For example, pricing database CRSP is a library, and the monthly stock file CRSP.MSF_V2 is a table.
To explore your database subscription on WRDS, use the list_libraries() function.
conn.list_libraries()The output is a “list” containing all libraries your institution has subscription to.
To explore the datasets under any particular database, or library, use the list_tables() function. Of course, you will need to specify which library’s tables you want to display. You can do so by adding ‘library =’ parameter in the list_tables() function. Below is an example of listing all datasets/tables under the Compstat library.
conn.list_tables(library='comp')The output from this command line is again a “list” containing all tables under the umbrella of Compustat library.
4. Query Data from WRDS Server
There are several ways to extract data from WRDS. I will go over three scenarios, from the most straightforward method of get_table(), to more sophisticated raw_sql() that accommodates conditioning statements as well as merging several data sources.
4.1 get_table() method
Let’s start with the basic get_table() method. This method is handy for getting data from one single dataset. You will need to specify which library and table to “get”. The get_table() method has feature that enables slicing data by number of rows or column names to include.
# Extract first 5 obs from comp.company
company = conn.get_table(library='comp', table='company', obs=5)In the example above, we are getting the comp.company table by specifying library name (comp) and table name (company). The last component ‘obs=5’ states only the first 5 rows will be extracted.
Instead of getting the entire wide table of comp.company, you can also narrow down to the specific columns to extract from the source table.
# Narrow down the specific columns to extract
company_narrow = conn.get_table(library='comp', table='company',
columns = ['conm', 'gvkey', 'cik'], obs=5)Here we impose additional slicing by naming the columns we would like to extract: [‘conm’, ‘gvkey’, ‘cik’]. And the output dataframe company_narrow contains 5 records and 3 columns, as expected.
| conm | gvkey | cik | |
|---|---|---|---|
| 0 | A & E PLASTIK PAK INC | 001000 | None |
| 1 | A & M FOOD SERVICES INC | 001001 | 0000723576 |
| 2 | AAI CORP | 001002 | 0001306124 |
| 3 | A.A. IMPORTING CO INC | 001003 | 0000730052 |
| 4 | AAR CORP | 001004 | 0000001750 |
4.2 raw_sql() method for subsetting data
If instead of querying the entire table, you want to impose a “where” condition to subset the data, raw_sql() method provides that functionality. The syntax is fairly standard SQL style.
# Select one stock's monthly price
# from January 2019 to June 2026
apple = conn.raw_sql("""
select distinct permno, mthcaldt, mthprc, mthret, shrout
from crsp_m_stock.msf_v2
where permno = 14593
and mthcaldt between '01/01/2019' and '06/30/2026'
order by mthcaldt
""", date_cols=['mthcaldt'])The example above extracts several columns from the CRSP monthly stock file in the new CIZ format (msf_v2, here from the monthly-updated crsp_m_stock library), and imposes two conditioning statements: permno=14593 is to zoom in on one particular stock, and date range condition narrows the output to data from January 2019 to June 2026. Notice the last date_cols=[‘mthcaldt’] component in the code. This indicates that the “mthcaldt” column (the monthly calendar date) should follow datetime format.
First 12 of the 90 monthly records returned:
| permno | mthcaldt | mthprc | mthret | shrout | |
|---|---|---|---|---|---|
| 0 | 14593 | 2019-01-31 | 166.44 | 0.055154 | 4715280 |
| 1 | 14593 | 2019-02-28 | 173.15 | 0.044771 | 4715280 |
| 2 | 14593 | 2019-03-29 | 189.95 | 0.097026 | 4715280 |
| 3 | 14593 | 2019-04-30 | 200.67 | 0.056436 | 4601075 |
| 4 | 14593 | 2019-05-31 | 175.07 | -0.124166 | 4601075 |
| 5 | 14593 | 2019-06-28 | 197.92 | 0.130519 | 4531395 |
| 6 | 14593 | 2019-07-31 | 213.04 | 0.076395 | 4519180 |
| 7 | 14593 | 2019-08-30 | 208.74 | -0.016430 | 4519180 |
| 8 | 14593 | 2019-09-30 | 223.97 | 0.072962 | 4443236 |
| 9 | 14593 | 2019-10-31 | 248.76 | 0.110684 | 4443265 |
| 10 | 14593 | 2019-11-29 | 267.25 | 0.077517 | 4443265 |
| 11 | 14593 | 2019-12-31 | 293.65 | 0.098784 | 4384959 |
4.3 raw_sql() method for joining multiple datasets
So far we have only queried data from single data source. If a researcher needs to query data from multiple data sources, raw_sql() method offers this capacity. For SAS programmers, this is essentially the “proc sql” procedure, just that it is now actually true SQL.
I demonstrate this using the example below. This exercise creates a dataframe apple_fund reporting fiscal year end balance sheet item (total asset - at) and closing price (prccm) by joining two different data sources: fundamental data through comp.funda (fundamental annual data) and pricing data through comp.secm (monthly pricing data).
# Joining data from two sources
# And imposing conditioning statement
apple_fund = conn.raw_sql("""
select a.gvkey, a.iid, a.datadate, a.tic, a.conm,
a.at, b.prccm, b.cshoq
from comp.funda a
inner join comp.secm b
on a.gvkey = b.gvkey
and a.iid = b.iid
and a.datadate = b.datadate
where a.tic = 'AAPL'
and a.datadate>='01/01/2010'
and a.datafmt = 'STD'
and a.consol = 'C'
and a.indfmt = 'INDL'
order by a.datadate
""", date_cols=['datadate'])We join the two data sources by matching on the common keys. In this case, gvkey+iid combination and fiscal date. Conditioning statements are part of the SQL statement by specifying company ticker, date range, and several other compustat data requirements. Last by not least, the code indicates that the column ‘datadate’ should be formatted in the date format.
The 16 fiscal years returned:
| gvkey | iid | datadate | tic | conm | at | prccm | cshoq | |
|---|---|---|---|---|---|---|---|---|
| 0 | 001690 | 01 | 2010-09-30 | AAPL | APPLE INC | 75183.0 | 283.750 | 915.970 |
| 1 | 001690 | 01 | 2011-09-30 | AAPL | APPLE INC | 116371.0 | 381.320 | 929.277 |
| 2 | 001690 | 01 | 2012-09-30 | AAPL | APPLE INC | 176064.0 | 667.105 | 939.208 |
| 3 | 001690 | 01 | 2013-09-30 | AAPL | APPLE INC | 207000.0 | 476.750 | 899.213 |
| 4 | 001690 | 01 | 2014-09-30 | AAPL | APPLE INC | 231839.0 | 100.750 | 5866.161 |
| 5 | 001690 | 01 | 2015-09-30 | AAPL | APPLE INC | 290479.0 | 110.300 | 5578.753 |
| 6 | 001690 | 01 | 2016-09-30 | AAPL | APPLE INC | 321686.0 | 113.050 | 5336.166 |
| 7 | 001690 | 01 | 2017-09-30 | AAPL | APPLE INC | 375319.0 | 154.120 | 5126.201 |
| 8 | 001690 | 01 | 2018-09-30 | AAPL | APPLE INC | 365725.0 | 225.740 | 4754.986 |
| 9 | 001690 | 01 | 2019-09-30 | AAPL | APPLE INC | 338516.0 | 223.970 | 4443.236 |
| 10 | 001690 | 01 | 2020-09-30 | AAPL | APPLE INC | 323888.0 | 115.810 | 16976.763 |
| 11 | 001690 | 01 | 2021-09-30 | AAPL | APPLE INC | 351002.0 | 141.500 | 16426.786 |
| 12 | 001690 | 01 | 2022-09-30 | AAPL | APPLE INC | 352755.0 | 138.200 | 15943.425 |
| 13 | 001690 | 01 | 2023-09-30 | AAPL | APPLE INC | 352583.0 | 171.210 | 15550.061 |
| 14 | 001690 | 01 | 2024-09-30 | AAPL | APPLE INC | 364980.0 | 233.000 | 15116.786 |
| 15 | 001690 | 01 | 2025-09-30 | AAPL | APPLE INC | 359241.0 | 254.630 | 14773.260 |
5. Saving Your Output
Now that you’ve run some simple python code on WRDS server, you might wonder how to save your output to your local computer. Python Pandas package supports flexible output format:
- pickle for further python work
- csv or excel
- even SAS data format!
5.1 Python native pickle file
If you intend to continue your data work in the Python environment, and hence would like to save some intermediate results to avoid data crunching all over again, pickle is a good output format.
import pandas as pd
# pickle the dataframe
apple_fund.to_pickle("/your local directory/apple_fund.pkl")5.2 Excel file formats
You can export the outputs to various excel file formats, such as xlsx or csv. This gives you the flexibility of integrating the output into other programming environments.
# export the dataframe to csv format
apple_fund.to_csv('/your local directory/apple_fund.csv')
# export the dataframe to xlsx format
apple_fund.to_excel('/your local directory/apple_fund.xlsx')5.3 STATA file format
Pandas has also built in an easy connection to STATA file format through the to_stata() method.
# export the dataframe to dta format for STATA
apple_fund.to_stata('/your local directory/apple_fund.dta')5.4 SAS datasets!
Did you know that you can save Pandas dataframe directly to SAS native format as well? While it is not as straightforward as the other file formats (SAS, we want the df.to_sas() function!), this is possible through a Python package SASPY. As there are several configurations required to get your local SAS communicate smoothly with python, I won’t show any examples here. You are encouraged to explore the examples in saspy documentation.
6. Ending Note
Hopefully by now you feel comfortable finding your Python way in the data jungle on WRDS server. You can click the “Full Code” button to download the entire code in ipynb format. I will elaborate some of the basic python analytics techniques in the next section.