Linking IBES and CRSP (ICLINK)

IBES TICKER to CRSP PERMNO link table

Source
WRDS SAS macro ICLINK (CIZ format)
Data
IBES identifier file ibes.id; CRSP names file stocknames_v2 CIZ format
Output
IBES TICKER–CRSP PERMNO links, scored from 0 (best) to 6
Author
Qingyi (Freda) Song Drechsler

This Python code builds a linkage between IBES data (containing information on company earnings and analysts forecasts) and CRSP data (containing price and return information). It builds the linkage in two layers:

As CUSIPs are more reliable company identifiers, we first try to match as much as possible through it. For the remaining ones that are not matched through CUSIP, we turn to TICKER as last resort. To impose additional layer of quality check, I add a company name matching layer on top of matching through CUSIPs and TICKERs. Name matching is done through RapidFuzz package (the successor to FuzzyWuzzy), but there can be many other fuzzy name matching methods.

Python version of the WRDS SAS macro ICLINK (CIZ format). It builds a link table between IBES TICKER and CRSP PERMNO in two layers: first through CUSIPs, then through exchange tickers for the firms that remain unmatched. Company-name similarity adds a layer of quality check, and each link gets a score from 0 (best) to 6.

CRSP identifiers come from the CIZ names file stocknames_v2, where the company name is issuernm and the historical CUSIP is cusip (comnam and ncusip in the legacy stocknames file). The CRSP monthly update on WRDS (crsp_m_stock) is used.

#####################################
# ICLINK: Link CRSP and IBES        #
# CRSP CIZ Format                   #
# Qingyi (Freda) Song Drechsler     #
# Date: June 2019                   #
# Updated: October 2026             #
#####################################

# This program replicates the SAS macro ICLINK_CIZ
# to create a linking table between CRSP and IBES
# Output is a score reflecting the quality of the link
# Score = 0 (best link) to Score = 6 (worst link)
#
# More explanation on score system:
# - 0: BEST match: using (cusip, cusip dates and company names)
#          or (exchange ticker, company names and 6-digit cusip)
# - 1: Cusips and cusip dates match but company names do not match
# - 2: Cusips and company names match but cusip dates do not match
# - 3: Cusips match but cusip dates and company names do not match
# - 4: tickers and 6-digit cusips match but company names do not match
# - 5: tickers and company names match but 6-digit cusips do not match
# - 6: tickers match but company names and 6-digit cusips do not match

import wrds
import pandas as pd
import numpy as np
from rapidfuzz import fuzz, utils
import plotly.graph_objects as go
import plotly.io as pio

pio.renderers.default = 'notebook_connected'

# CRSP monthly update
crsp_lib = 'crsp_m_stock'

###################
# Connect to WRDS #
###################
conn=wrds.Connection()
Loading library list...
Done