earnings with known institutional holdings
import os
import requests
import polars as pl
from rich import print
Let's start with something we have have experience with: the earnings endpoints.
- https://api.unusualwhales.com/docs#/operations/PublicApi.EarningsController.afterhours
- https://api.unusualwhales.com/docs#/operations/PublicApi.EarningsController.premarket
We will collect tomorrow's earnings (Tue Nov 26th) for both afterhours and premarket and thin out the responses a bit for a more concise summary:
uw_token = os.environ['UW_TOKEN']
headers = {'Accept': 'application/json, text/plain', 'Authorization': uw_token}
# Collect Monday's earnings data (both afterhours and premarket)
earnings_ah_params = {
'date': '2024-11-26'
}
earnings_ah_url = 'https://api.unusualwhales.com/api/earnings/afterhours'
earnings_ah_r = requests.get(earnings_ah_url, headers=headers, params=earnings_ah_params)
earnings_pm_params = {
'date': '2024-11-26'
}
earnings_pm_url = 'https://api.unusualwhales.com/api/earnings/premarket'
earnings_pm_r = requests.get(earnings_pm_url, headers=headers, params=earnings_pm_params)
# Create dataframes with each response then concatenate them into a single dataframe
raw_ah_df = pl.DataFrame(earnings_ah_r.json()['data'])
raw_pm_df = pl.DataFrame(earnings_pm_r.json()['data'])
raw_earnings_df = pl.concat([raw_ah_df, raw_pm_df])
raw_earnings_df
preferred_order = [
'symbol',
'full_name',
'sector',
'is_s_p_500',
'ending_fiscal_quarter_dt',
'report_date_dt',
'report_time',
'street_mean_est',
'pre_earnings_close',
'expected_move_perc',
'expected_move',
'expected_high',
'expected_low'
]
earnings_df = (
raw_earnings_df
.filter(pl.col('has_options') == True)
.with_columns(
pl.col('ending_fiscal_quarter').str.strptime(pl.Date, '%Y-%m-%d').alias('ending_fiscal_quarter_dt'),
pl.col('pre_earnings_date').str.strptime(pl.Date, '%Y-%m-%d').alias('pre_earnings_date_dt'),
pl.col('report_date').str.strptime(pl.Date, '%Y-%m-%d').alias('report_date_dt'),
pl.col('expected_move').cast(pl.Float64),
pl.col('expected_move_perc').cast(pl.Float64),
pl.col('post_earnings_close').cast(pl.Float64),
pl.col('pre_earnings_close').cast(pl.Float64),
pl.col('reaction').cast(pl.Float64),
pl.col('street_mean_est').cast(pl.Float64)
)
.with_columns(
(pl.col('pre_earnings_close') + pl.col('expected_move')).alias('expected_high'),
(pl.col('pre_earnings_close') - pl.col('expected_move')).alias('expected_low')
)
.select(preferred_order)
.sort('expected_move_perc', descending=True)
)
earnings_df
symbol | full_name | sector | is_s_p_500 | ending_fiscal_quarter_dt | report_date_dt | report_time | street_mean_est | pre_earnings_close | expected_move_perc | expected_move | expected_high | expected_low |
---|---|---|---|---|---|---|---|---|---|---|---|---|
str | str | str | bool | date | date | str | f64 | f64 | f64 | f64 | f64 | f64 |
"ZH" | "ZHIHU" | "Communication … | false | 2024-09-30 | 2024-11-26 | "premarket" | null | 3.68 | 0.44788 | 1.65 | 5.33 | 2.03 |
"DDD" | "3D SYSTEMS" | "Technology" | false | 2024-09-30 | 2024-11-26 | "postmarket" | -0.09 | 3.43 | 0.167289 | 0.57 | 4.0 | 2.86 |
"EMBC" | "EMBECTA" | "Healthcare" | false | 2024-09-30 | 2024-11-26 | "premarket" | 0.4 | 14.41 | 0.157259 | 2.27 | 16.68 | 12.14 |
"MDWD" | "MEDIWOUND" | "Healthcare" | false | 2024-09-30 | 2024-11-26 | "premarket" | -0.48 | 17.33 | 0.152562 | 2.64 | 19.97 | 14.69 |
"TITN" | "TITAN MACHINER… | "Industrials" | false | 2024-10-31 | 2024-11-26 | "premarket" | 0.07 | 15.42 | 0.143327 | 2.21 | 17.63 | 13.21 |
… | … | … | … | … | … | … | … | … | … | … | … | … |
"ADSK" | "AUTODESK" | "Technology" | true | 2024-10-31 | 2024-11-26 | "postmarket" | 2.11 | 319.39 | 0.05546 | 17.71 | 337.1 | 301.68 |
"SJM" | "J M SMUCKER" | "Consumer Defen… | true | 2024-10-31 | 2024-11-26 | "premarket" | 2.51 | 113.62 | 0.054674 | 6.21 | 119.83 | 107.41 |
"M" | "MACY'S" | "Consumer Cycli… | false | 2024-10-31 | 2024-11-26 | "premarket" | -0.01 | 15.94 | 0.054649 | 0.87 | 16.81 | 15.07 |
"MANU" | "MANCHESTER UNI… | "Consumer Cycli… | false | 2024-09-30 | 2024-11-26 | "premarket" | -0.29 | 17.21 | 0.047792 | 0.82 | 18.03 | 16.39 |
"ADI" | "ANALOG DEVICES… | "Technology" | true | 2024-10-31 | 2024-11-26 | "premarket" | 1.64 | 223.58 | 0.043165 | 9.65 | 233.23 | 213.93 |
OK, 32 records, let's take a closer look at the tickers and full names to see if anything looks familiar or interesting.
earnings_tickers = earnings_df['symbol'].to_list()
full_names = earnings_df['full_name'].to_list()
tickers_and_names = {ticker: name for ticker, name in zip(earnings_tickers, full_names)}
print(tickers_and_names)
{ 'ZH': 'ZHIHU', 'DDD': '3D SYSTEMS', 'EMBC': 'EMBECTA', 'MDWD': 'MEDIWOUND', 'TITN': 'TITAN MACHINERY', 'NOAH': 'NOAH HOLDINGS', 'PD': 'PAGERDUTY', 'ARWR': 'ARROWHEAD PHARMACEUTICALS', 'IREN': 'IRIS ENERGY', 'GES': 'GUESS', 'ANF': 'ABERCROMBIE & FITCH', 'KSS': 'KOHLS', 'AMBA': 'AMBARELLA', 'NTNX': 'NUTANIX', 'DKS': "DICK'S SPORTING GOODS", 'AMWD': 'AMERICAN WOODMARK', 'URBN': 'URBAN OUTFITTERS', 'HTHT': 'H WORLD GROUP', 'DELL': 'DELL TECHNOLOGIES', 'WDAY': 'WORKDAY', 'JWN': 'NORDSTROM', 'TEN': 'Tsakos Energy', 'BURL': 'BURLINGTON STORES', 'CRWD': 'CROWDSTRIKE HOLDINGS', 'BBY': 'BEST BUY CO', 'HPQ': 'HP', 'YY': 'JOYY', 'ADSK': 'AUTODESK', 'SJM': 'J M SMUCKER', 'M': "MACY'S", 'MANU': 'MANCHESTER UNITED', 'ADI': 'ANALOG DEVICES' }
DELL has seen quite a journey since early August, from $88 to $144 in under 4 months.
Let's experiment with a new endpoint, from the Institutional collection:
https://api.unusualwhales.com/docs#/operations/PublicApi.InstitutionController.ownership
We pass in a ticker
and get back institutions that
have a footprint there, with some variations all applying to the
latest quarter (ending Sep 30th):
- Closing a previously-existing position
- Opening a new position
- Adding to an existing position
- Holding an existing position steady
Let's bust a move:
# Secret tip from the guy who manually identified key institutional players:
# this list of tags will limit the enormous universe of institutions down
# to a punchy group of interesting firms.
tags = [
'13d_activist', 'activist', 'value_investor', 'small_cap', 'biotech',
'credit', 'technology', 'tiger_club', 'energy', 'esg', 'event'
]
dfs = []
tickers_with_no_io = []
for idx, ticker in enumerate(earnings_tickers):
io_url = f'https://api.unusualwhales.com/api/institution/{ticker}/ownership'
io_params = {
'tags[]': tags,
}
io_r = requests.get(io_url, headers=headers, params=io_params)
if len(io_r.json()['data']) > 0:
raw_df = pl.DataFrame(io_r.json()['data'])
df = (
raw_df
.with_columns(
pl.lit(ticker).alias('ticker'),
pl.col('shares_outstanding').cast(pl.String).fill_null('')
)
)
dfs.append(df)
schema = str(df.schema)
else:
tickers_with_no_io.append(ticker)
io_df = pl.concat(dfs)
OK, after a little fancy footwork to account for some
international stocks where UW does not have outstanding share
count (yet), we have combined all the responses into a single
polars dataframe named io_dfs
.
Before we move on forward, let's do a quick check on to see if any of the tickers did not have institutional ownership:
print(tickers_with_no_io)
['ZH']
Nothing of concern there with the single ticker showing no
institutional ownership, ZH
.
Let's pick DELL
for the rest of this
example:
we want to see which institutions closed their
DELL
positions, which institutions opened new
DELL
positions, and which ones added to existing
positions... and maybe at the end we can look at "static"
positions too.
closed_dell_df = (
io_df
.filter(
(pl.col('ticker') == 'DELL') &
(pl.col('units') == 0)
)
.sort('units_changed', descending=False)
)
closed_dell_df
avg_price | first_buy | historical_units | inst_share_value | inst_value | name | people | report_date | shares_outstanding | short_name | tags | units | units_changed | value | ticker |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | str | list[i64] | str | str | str | list[str] | str | str | str | list[str] | i64 | i64 | i64 | str |
null | "2022-09-30" | [0, 2388359, … 4508189] | "27759423751" | "39343262001" | "POINT72 ASSET … | ["Steven Cohen"] | "2024-09-30" | "307762769" | "Point72 Asset … | ["activist"] | 0 | -2388359 | 0 | "DELL" |
null | "2024-06-30" | [0, 126279, … 0] | "404051553" | "422582142" | "LIGHT STREET C… | ["Glen Kacher"] | "2024-09-30" | "307762769" | "Light Street C… | ["activist", "tiger_club", "technology"] | 0 | -126279 | 0 | "DELL" |
null | "2024-06-30" | [0, 34720, … 0] | "14055650749" | "17658794959" | "BRIDGEWATER AS… | ["Ray Dalio"] | "2024-09-30" | "307762769" | "Bridgewater As… | ["13d_activist"] | 0 | -34720 | 0 | "DELL" |
null | "2024-06-30" | [0, 19703, … 0] | "3810553458" | "4617003629" | "MAGNETAR FINAN… | ["Alec Litowitz", "Ross Laser"] | "2024-09-30" | "307762769" | "Magnetar Finan… | ["13d_activist", "energy", … "credit"] | 0 | -19703 | 0 | "DELL" |
null | "2024-06-30" | [0, 3780, … 0] | "447103807" | "486683812" | "CANNELL CAPITA… | ["Carlo Cannell"] | "2024-09-30" | "307762769" | "Cannell Capita… | ["activist", "small_cap"] | 0 | -3780 | 0 | "DELL" |
Who closed their existing DELL
positions as of
EOQ Sep 30th?
- Point72 (Steven Cohen)
- Light Street ("Tiger Cub" Glen Kacher)
- Bridgewater (Ray Dalio, has since retired)
- Magnetar (Alec Litowitz and Ross Laser)
- Cannell (Carlo Cannell)
We can see that Point72 has closed a large (notionally-speaking anyway) holding: the firm sold approximately 2.39 million shares this past quarter on a position they opened in the quarter ending 2022-09-30... impressive trade and worth noting that they closed ALL their holdings.
Let's look at the other side of the coin, firms that opened new positions this past quarter:
new_dell_df = (
io_df
.filter(
(pl.col('ticker') == 'DELL') &
(pl.col('units') > 0) &
(pl.col('first_buy') == '2024-09-30')
)
)
new_dell_df
avg_price | first_buy | historical_units | inst_share_value | inst_value | name | people | report_date | shares_outstanding | short_name | tags | units | units_changed | value | ticker |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | str | list[i64] | str | str | str | list[str] | str | str | str | list[str] | i64 | i64 | i64 | str |
"115.53" | "2024-09-30" | [176200, 0, … 0] | "56601576463.0" | "56635136796.0" | "ADAGE CAPITAL … | ["Robert Atchinson", "Phillip Gross"] | "2024-09-30" | "307762769" | "Adage Capital … | ["13d_activist"] | 176200 | 176200 | 20886748 | "DELL" |
Who opened new longs in DELL
as of EOQ Sep
30th?
- Adage (Robert Atchinson and Philip Gross)
Interesting, we can see Adage bought approximately 176,000
shares of DELL
this past quarter. Adage is
incredibly secretive, but their track record is known
industry-wide: when the manager of Harvard University's takes a
new position, it's noteworthy.
Let's see if any institutions added to existing positions in
DELL
this past quarter:
(after all, there was a significant pullback into the early August correlation one sell-off)
add_dell_df = (
io_df
.filter(
(pl.col('ticker') == 'DELL') &
(pl.col('units_changed') > 0) &
(pl.col('first_buy') != '2024-09-30')
)
)
add_dell_df
avg_price | first_buy | historical_units | inst_share_value | inst_value | name | people | report_date | shares_outstanding | short_name | tags | units | units_changed | value | ticker |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | str | list[i64] | str | str | str | list[str] | str | str | str | list[str] | i64 | i64 | i64 | str |
"82.67" | "2023-06-30" | [2146777, 1447573, … 0] | "17794627077" | "58348942078" | "ALKEON CAPITAL… | ["Panayotis “Takis” Sparaggis"] | "2024-09-30" | "307762769" | "" | ["technology"] | 2146777 | 699204 | 254478946 | "DELL" |
"93" | "2022-09-30" | [646196, 342054, … 146624] | "66460868115.0" | "116492369102.0… | "D. E. SHAW & C… | ["David Shaw"] | "2024-09-30" | "307762769" | "D.E. Shaw" | ["13d_activist"] | 646196 | 304142 | 76600074 | "DELL" |
"107.39" | "2024-03-31" | [312870, 256325, … 0] | "126914340970" | "127163204011" | "CLEARBRIDGE IN… | ["Terrence Murphy"] | "2024-09-30" | "307762769" | "Clearbridge In… | ["activist"] | 312870 | 56545 | 37087610 | "DELL" |
"123.29" | "2024-03-31" | [33966, 15712, … 0] | "5128678280" | "5128708760" | "MAVERICK CAPIT… | ["Lee Ainslie"] | "2024-09-30" | "307762769" | "Maverick Capit… | ["activist", "tiger_club"] | 33966 | 18254 | 4026330 | "DELL" |
null | "2021-06-30" | [23391, 21696, … 846571] | "36463931599" | "61306204232" | "BALYASNY ASSET… | ["Dmitry Balyasny", "Scott Schroeder", "Taylor O'Malley"] | "2024-09-30" | "307762769" | "Balyasny Asset… | ["activist"] | 23391 | 1695 | 2772769 | "DELL" |
"106.1" | "2023-12-31" | [1128, 266, … 0] | "79316910" | "252652870" | "SACHETTA, LLC" | [] | "2024-09-30" | "307762769" | "Sachetta" | ["activist"] | 1128 | 862 | 133714 | "DELL" |
Who added to existing DELL
positions as of EOQ
Sep 30th?
- Alkeon (Panoyotis 'Takis' Sparaggis)
- D.E. Shaw (David Shaw)
- Clearbridge (Terrence Murphy)
- Maverick ("Tiger Cub" Lee Ainslie)
- Balyasny (Dmitry Balyasny)
I'm quite interested in Alkeon's add here as a Technology-specialist. They have built up their size to nearly Point72 in nominal terms, and it's an interesting contrast to see them increasing their position by roughly +50%.
Let's round this out by identifying any institutions who
continue to hold DELL
with no change to the
overall position:
flat_dell_df = (
io_df
.filter(
(pl.col('ticker') == 'DELL') &
(pl.col('units_changed') == 0) &
(pl.col('first_buy') != '2024-09-30')
)
)
flat_dell_df
avg_price | first_buy | historical_units | inst_share_value | inst_value | name | people | report_date | shares_outstanding | short_name | tags | units | units_changed | value | ticker |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str | str | list[i64] | str | str | str | list[str] | str | str | str | list[str] | i64 | i64 | i64 | str |
Empty!
Turns out DELL
was a good pick for this review,
since it's clearly a polarizing ticker: there was not a single
fund in our set of activists and specialists with a static
holding in DELL
.