Initial Debugging Completed and Execution Successful

This commit is contained in:
2025-09-21 08:55:26 -04:00
parent 27e9112592
commit afb2c79a4c
7501 changed files with 1330669 additions and 18 deletions

View File

@@ -1,40 +1,73 @@
# pip install sec-api pandas python-dotenv
from sec_api import ThirteenFApi
from sec_api import QueryApi
import pandas as pd
from smtplib import SMTP
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from dotenv import load_dotenv
import os
import uuid
# Load environment variables from .env file
# Load environment variables
load_dotenv()
# Email configuration from .env
# Configuration from .env
EMAIL_SENDER = os.getenv('EMAIL_SENDER')
EMAIL_RECIPIENT = os.getenv('EMAIL_RECIPIENT')
EMAIL_PASSWORD = os.getenv('EMAIL_PASSWORD')
SMTP_SERVER = os.getenv('SMTP_SERVER', 'smtp.gmail.com')
SMTP_PORT = int(os.getenv('SMTP_PORT', 587))
SEC_API_KEY = os.getenv('SEC_API_KEY') # Get free at sec-api.io
SEC_API_KEY = os.getenv('SEC_API_KEY')
if not SEC_API_KEY:
raise ValueError("SEC_API_KEY not set in .env file")
# Initialize SEC API
api = ThirteenFApi(SEC_API_KEY)
query_api = QueryApi(api_key=SEC_API_KEY)
# Point72 CIK
CIK = '0001603466'
# Get latest two 13F filings
filings = api.get_filings(cik=CIK)
filings = sorted(filings, key=lambda x: x['periodOfReport'], reverse=True)[:2]
# Query for latest two 13F-HR filings
query = {
"query": f'cik:{CIK} formType:"13F-HR"',
"sort": [{"filedAt": {"order": "desc"}}],
"size": 2
}
response = query_api.get_filings(query)
filings = response.get('filings', [])
if len(filings) < 2:
raise Exception(f"Not enough filings found for comparison: {len(filings)} found")
latest = filings[0]
prev = filings[1]
# Fetch holdings
latest_df = pd.DataFrame(api.get_holdings(accessionNo=latest['accessionNo']))
prev_df = pd.DataFrame(api.get_holdings(accessionNo=prev['accessionNo']))
latest_holdings = latest.get('holdings', [])
prev_holdings = prev.get('holdings', [])
if not latest_holdings or not prev_holdings:
raise Exception("No holdings data found in filings")
# Convert holdings to DataFrame
latest_df = pd.DataFrame(latest_holdings)
prev_df = pd.DataFrame(prev_holdings)
# Extract share amount from shrsOrPrnAmt dictionary
def extract_shares(row):
if isinstance(row, dict):
return row.get('sshPrnamt', 0)
return row
if 'shrsOrPrnAmt' in latest_df.columns:
latest_df['shrsOrPrnAmt'] = latest_df['shrsOrPrnAmt'].apply(extract_shares)
prev_df['shrsOrPrnAmt'] = prev_df['shrsOrPrnAmt'].apply(extract_shares)
# Verify required columns
required_cols = ['cusip', 'nameOfIssuer', 'shrsOrPrnAmt', 'value']
for col in required_cols:
if col not in latest_df.columns or col not in prev_df.columns:
raise KeyError(f"Column {col} missing in holdings data")
# Set index for comparison
key_col = 'cusip'
@@ -50,21 +83,28 @@ removals = prev_df[~prev_df.index.isin(latest_df.index)]
# Changes >10%
both = latest_df.index.intersection(prev_df.index)
changes = latest_df.loc[both].join(prev_df.loc[both], lsuffix='_new', rsuffix='_old')
changes['share_change'] = changes['shrsOrPrnamt_new'] - changes['shrsOrPrnamt_old']
changes = changes[abs(changes['share_change']) / changes['shrsOrPrnamt_old'] > 0.1]
# Ensure share columns are numeric
changes['shrsOrPrnAmt_new'] = pd.to_numeric(changes['shrsOrPrnAmt_new'], errors='coerce')
changes['shrsOrPrnAmt_old'] = pd.to_numeric(changes['shrsOrPrnAmt_old'], errors='coerce')
# Calculate share change
changes['share_change'] = changes['shrsOrPrnAmt_new'] - changes['shrsOrPrnAmt_old']
changes = changes[changes['share_change'].notna()] # Remove rows with NaN changes
changes = changes[abs(changes['share_change']) / changes['shrsOrPrnAmt_old'].replace(0, 1) > 0.1]
# Summary
summary = f"""
Point72 13F Changes {prev['periodOfReport']} to {latest['periodOfReport']}
Additions ({len(additions)}):
{additions[['nameOfIssuer', 'shrsOrPrnamt', 'value']].to_string()}
{additions[['nameOfIssuer', 'shrsOrPrnAmt', 'value']].to_string() if not additions.empty else 'None'}
Removals ({len(removals)}):
{removals[['nameOfIssuer', 'shrsOrPrnamt', 'value']].to_string()}
{removals[['nameOfIssuer', 'shrsOrPrnAmt', 'value']].to_string() if not removals.empty else 'None'}
Changes ({len(changes)}):
{changes[['nameOfIssuer_new', 'share_change']].to_string()}
{changes[['nameOfIssuer_new', 'share_change']].to_string() if not changes.empty else 'None'}
"""
# Email
@@ -81,5 +121,3 @@ server.send_message(msg)
server.quit()
print("Email sent!")