| name | run2_quarter_comparison |
| description | Compare holdings across quarters to identify increased/decreased investments |
Quarter-to-Quarter Holdings Comparison
Overview
Compare a fund's holdings between two quarters (e.g., Q2 to Q3) to identify which stocks received increased or decreased investment.
Core Workflow
Step 1: Get Accession Numbers for Both Quarters
import pandas as pd
coverpage_q2 = pd.read_csv('/root/2025-q2/COVERPAGE.tsv', sep='\t')
fund_q2 = coverpage_q2[coverpage_q2['FILINGMANAGER_NAME'].str.contains('Berkshire', case=False)]
accession_q2 = fund_q2['ACCESSION_NUMBER'].values[0]
coverpage_q3 = pd.read_csv('/root/2025-q3/COVERPAGE.tsv', sep='\t')
fund_q3 = coverpage_q3[coverpage_q3['FILINGMANAGER_NAME'].str.contains('Berkshire', case=False)]
accession_q3 = fund_q3['ACCESSION_NUMBER'].values[0]
Step 2: Load Holdings for Both Quarters
infotable_q2 = pd.read_csv('/root/2025-q2/INFOTABLE.tsv', sep='\t', low_memory=False)
infotable_q3 = pd.read_csv('/root/2025-q3/INFOTABLE.tsv', sep='\t', low_memory=False)
fund_q2_holdings = infotable_q2[infotable_q2['ACCESSION_NUMBER'] == accession_q2]
fund_q3_holdings = infotable_q3[infotable_q3['ACCESSION_NUMBER'] == accession_q3]
Step 3: Aggregate by CUSIP
q2_summary = fund_q2_holdings.groupby('CUSIP').agg({
'VALUE': 'sum',
'NAMEOFISSUER': 'first',
'SSHPRNAMT': 'sum'
}).reset_index()
q2_summary.columns = ['CUSIP', 'VALUE_Q2', 'NAMEOFISSUER_Q2', 'SHARES_Q2']
q3_summary = fund_q3_holdings.groupby('CUSIP').agg({
'VALUE': 'sum',
'NAMEOFISSUER': 'first',
'SSHPRNAMT': 'sum'
}).reset_index()
q3_summary.columns = ['CUSIP', 'VALUE_Q3', 'NAMEOFISSUER_Q3', 'SHARES_Q3']
Step 4: Merge and Calculate Changes
comparison = pd.merge(q2_summary, q3_summary, on='CUSIP', how='outer')
comparison['VALUE_Q2'] = comparison['VALUE_Q2'].fillna(0)
comparison['VALUE_Q3'] = comparison['VALUE_Q3'].fillna(0)
comparison['VALUE_CHANGE'] = comparison['VALUE_Q3'] - comparison['VALUE_Q2']
comparison['SHARE_CHANGE'] = comparison['SHARES_Q3'].fillna(0) - comparison['SHARES_Q2'].fillna(0)
Step 5: Identify Top Changes
top_5_increases = comparison[comparison['VALUE_CHANGE'] > 0].nlargest(5, 'VALUE_CHANGE')
top_5_decreases = comparison[comparison['VALUE_CHANGE'] < 0].nsmallest(5, 'VALUE_CHANGE')
increased_cusips = top_5_increases['CUSIP'].tolist()
decreased_cusips = top_5_decreases['CUSIP'].tolist()
print("Top 5 Increased Positions:")
print(top_5_increases[['CUSIP', 'NAMEOFISSUER_Q2', 'VALUE_Q2', 'VALUE_Q3', 'VALUE_CHANGE']])
Important Considerations
Handling Missing Companies
comparison['NAMEOFISSUER'] = comparison['NAMEOFISSUER_Q2'].fillna(comparison['NAMEOFISSUER_Q3'])
new_positions = comparison[comparison['VALUE_Q2'] == 0][comparison['VALUE_CHANGE'] > 0]
exited_positions = comparison[comparison['VALUE_Q3'] == 0][comparison['VALUE_CHANGE'] < 0]
Duplicate CUSIP Handling
Some fund filings may have the same CUSIP listed multiple times (different options, derivative types, etc.). The aggregation via groupby().agg() handles this automatically by summing.
Data Type Warnings
infotable = pd.read_csv(
'INFOTABLE.tsv',
sep='\t',
low_memory=False,
dtype={'ACCESSION_NUMBER': 'str', 'CUSIP': 'str', 'VALUE': 'int64'}
)
Complete Example: Berkshire Q2 to Q3
import pandas as pd
infotable_q2 = pd.read_csv('/root/2025-q2/INFOTABLE.tsv', sep='\t', low_memory=False)
infotable_q3 = pd.read_csv('/root/2025-q3/INFOTABLE.tsv', sep='\t', low_memory=False)
coverpage_q2 = pd.read_csv('/root/2025-q2/COVERPAGE.tsv', sep='\t')
coverpage_q3 = pd.read_csv('/root/2025-q3/COVERPAGE.tsv', sep='\t')
accession_q2 = coverpage_q2[coverpage_q2['FILINGMANAGER_NAME'] == 'Berkshire Hathaway Inc']['ACCESSION_NUMBER'].values[0]
accession_q3 = coverpage_q3[coverpage_q3['FILINGMANAGER_NAME'] == 'Berkshire Hathaway Inc']['ACCESSION_NUMBER'].values[0]
q2_holdings = infotable_q2[infotable_q2['ACCESSION_NUMBER'] == accession_q2]
q3_holdings = infotable_q3[infotable_q3['ACCESSION_NUMBER'] == accession_q3]
q2_agg = q2_holdings.groupby('CUSIP')['VALUE'].sum().reset_index()
q2_agg.columns = ['CUSIP', 'VALUE_Q2']
q3_agg = q3_holdings.groupby('CUSIP')['VALUE'].sum().reset_index()
q3_agg.columns = ['CUSIP', 'VALUE_Q3']
comparison = pd.merge(q2_agg, q3_agg, on='CUSIP', how='outer')
comparison['VALUE_Q2'] = comparison['VALUE_Q2'].fillna(0)
comparison['VALUE_Q3'] = comparison[].fillna()
comparison[] = comparison[] - comparison[]
top_5 = comparison[comparison[] > ].nlargest(, )
cusips = top_5[].tolist()
(cusips)
Output Format
Result should be a list of CUSIPs ordered by value change (descending):
{
"top_5_cusips": ["CUSIP1", "CUSIP2", "CUSIP3", "CUSIP4", "CUSIP5"],
"changes": [
{"cusip": "CUSIP1", "value_change": 4338397000000},
{"cusip": "CUSIP2", "value_change": 3208516000000}
]
}
Validation Checklist