Initial commit
This commit is contained in:
106
venv/lib/python3.10/site-packages/edgar/funds/thirteenf.py
Normal file
106
venv/lib/python3.10/site-packages/edgar/funds/thirteenf.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
13F filing module for investment funds.
|
||||
|
||||
This module provides classes and functions for working with 13F filings
|
||||
that report investment fund portfolio holdings.
|
||||
"""
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
# Define constants
|
||||
THIRTEENF_FORMS = ['13F-HR', "13F-HR/A", "13F-NT", "13F-NT/A", "13F-CTR", "13F-CTR/A"]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# We'll define these functions without directly importing them at the module level
|
||||
# to avoid circular imports
|
||||
|
||||
def get_ThirteenF():
|
||||
"""Dynamically import ThirteenF to avoid circular imports."""
|
||||
from edgar.thirteenf import ThirteenF as OriginalThirteenF
|
||||
return OriginalThirteenF
|
||||
|
||||
# Create property-like functions that provide lazy loading
|
||||
def ThirteenF():
|
||||
"""Get the ThirteenF class, dynamically importing it to avoid circular imports."""
|
||||
return get_ThirteenF()
|
||||
|
||||
def get_thirteenf_portfolio(filing) -> pd.DataFrame:
|
||||
"""
|
||||
Extract portfolio holdings from a 13F filing.
|
||||
|
||||
Args:
|
||||
filing: The 13F filing to extract data from
|
||||
|
||||
Returns:
|
||||
DataFrame containing portfolio holdings
|
||||
"""
|
||||
try:
|
||||
# Create a ThirteenF from the filing
|
||||
thirteenf_class = get_ThirteenF()
|
||||
thirteenf = thirteenf_class(filing, use_latest_period_of_report=True)
|
||||
|
||||
# Check if the filing has an information table
|
||||
if not thirteenf.has_infotable():
|
||||
log.info("Filing %s does not have an information table", filing.accession_no)
|
||||
return pd.DataFrame()
|
||||
|
||||
# Extract the information table
|
||||
infotable = thirteenf.infotable
|
||||
if infotable is None:
|
||||
log.warning("Could not extract information table from filing %s", filing.accession_no)
|
||||
return pd.DataFrame()
|
||||
|
||||
# Convert to DataFrame
|
||||
df = pd.DataFrame(infotable)
|
||||
|
||||
# Clean up and organize data
|
||||
if not df.empty:
|
||||
# Update column names for consistency
|
||||
if 'nameOfIssuer' in df.columns:
|
||||
df = df.rename(columns={
|
||||
'nameOfIssuer': 'name',
|
||||
'titleOfClass': 'title',
|
||||
'cusip': 'cusip',
|
||||
'value': 'value_usd',
|
||||
'sshPrnamt': 'shares',
|
||||
'sshPrnamtType': 'share_type',
|
||||
'investmentDiscretion': 'investment_discretion',
|
||||
'votingAuthority': 'voting_authority'
|
||||
})
|
||||
|
||||
# Add ticker mapping if possible
|
||||
try:
|
||||
from edgar.reference import cusip_ticker_mapping
|
||||
cusip_map = cusip_ticker_mapping(allow_duplicate_cusips=False)
|
||||
df['ticker'] = df['cusip'].map(cusip_map.Ticker)
|
||||
except Exception as e:
|
||||
log.warning("Error adding ticker mappings: %s", e)
|
||||
df['ticker'] = None
|
||||
|
||||
# Calculate percent of portfolio
|
||||
if 'value_usd' in df.columns:
|
||||
total_value = df['value_usd'].sum()
|
||||
if total_value > 0:
|
||||
df['pct_value'] = df['value_usd'] / total_value * 100
|
||||
else:
|
||||
df['pct_value'] = 0
|
||||
|
||||
# Sort by value
|
||||
df = df.sort_values('value_usd', ascending=False).reset_index(drop=True)
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
log.warning("Error extracting holdings from 13F filing: %s", e)
|
||||
|
||||
# Return empty DataFrame if extraction failed
|
||||
return pd.DataFrame()
|
||||
|
||||
# Functions for export
|
||||
__all__ = [
|
||||
'ThirteenF',
|
||||
'THIRTEENF_FORMS',
|
||||
'get_thirteenf_portfolio',
|
||||
]
|
||||
Reference in New Issue
Block a user