Metadata-Version: 2.4
Name: secfin
Version: 1.0.0
Summary: Fetch company financial records from SEC EDGAR
Author: Jacob Wright
License-Expression: MIT
Project-URL: Homepage, https://github.com/jacobwright32/secfin
Project-URL: Bug Tracker, https://github.com/jacobwright32/secfin/issues
Keywords: sec,edgar,finance,financial-data,stocks,investing,fundamentals
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Requires-Dist: pandas>=2.0
Requires-Dist: aiohttp>=3.8
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# secfin

Fetch company financial records from SEC EDGAR.

## Installation

```bash
pip install secfin
```

Or install from source:
```bash
pip install -e .
```

## Quick Start

```python
from secfin import SECClient, Company

# Initialize client (SEC requires User-Agent with contact info)
client = SECClient(user_agent="MyApp contact@email.com")

# Get company financials
company = Company("AAPL", client)

# Income Statement
income = company.get_income_statement()
print(f"Revenue: {income.revenue.latest()}")
print(f"Net Income: {income.net_income.latest()}")

# Balance Sheet
balance = company.get_balance_sheet()
print(f"Total Assets: {balance.total_assets.latest()}")
print(f"Cash: {balance.cash.latest()}")

# Cash Flow Statement
cashflow = company.get_cash_flow()
print(f"Operating Cash Flow: {cashflow.operating_cash_flow.latest()}")
print(f"Free Cash Flow: {cashflow.free_cash_flow.latest()}")
```

## Features

- **Income Statement**: Revenue, Cost of Revenue, Gross Profit, R&D, SG&A, Operating Income, Net Income, EPS, Shares Outstanding
- **Balance Sheet**: Assets, Liabilities, Equity, Cash, Inventory, Debt
- **Cash Flow**: Operating, Investing, Financing cash flows, CapEx, Dividends, Free Cash Flow
- **Analytics**: TTM calculations, YoY growth, CAGR, margins, ratios
- **Caching**: Automatic disk caching to reduce API calls
- **Async Support**: Async batch fetching for improved performance

## Data Export

Convert to pandas DataFrame:

```python
# Get historical data as DataFrame
df = company.get_income_statement().to_dataframe()
print(df)
```

Get a summary of all financials:

```python
summary = company.summary()
print(summary)
```

## Quarterly vs Annual Data

```python
# Annual data (10-K filings)
annual_income = company.get_income_statement(period="annual")

# Quarterly data (10-Q filings)
quarterly_income = company.get_income_statement(period="quarterly")
```

## Financial Analytics

```python
income = company.get_income_statement()

# Trailing Twelve Months (TTM)
ttm_revenue = income.revenue.ttm()

# Year-over-Year Growth
yoy_growth = income.revenue.growth()  # Returns decimal (0.1 = 10%)

# Compound Annual Growth Rate
cagr_5yr = income.revenue.cagr(years=5)

# Margins
gross_margin = income.gross_margin()
operating_margin = income.operating_margin()
net_margin = income.net_margin()

# Balance Sheet Ratios
balance = company.get_balance_sheet()
current_ratio = balance.current_ratio()
debt_to_equity = balance.debt_to_equity()
```

## Caching

Responses are cached locally to reduce API calls (default: 24 hours).

```python
# Disable caching
client = SECClient("MyApp contact@email.com", cache_enabled=False)

# Custom cache TTL (1 hour)
client = SECClient("MyApp contact@email.com", cache_ttl=3600)

# Clear cache
client.clear_cache()

# View cache stats
print(client.cache_stats())
# {'files': 10, 'size_bytes': 5000000, 'size_mb': 4.77}
```

## Async Batch Fetching

For faster fetching of large ticker lists:

```python
import asyncio
from secfin import SECClient, AsyncBatchFetcher

client = SECClient("MyApp contact@email.com")
fetcher = AsyncBatchFetcher(client, max_concurrent=5)

# Async fetch
results = asyncio.run(fetcher.fetch_tickers(["AAPL", "MSFT", "GOOGL"]))

# Or use the sync wrapper
from secfin import run_async_fetch
results = run_async_fetch(client, ["AAPL", "MSFT", "GOOGL"])
```

## Earnings Dates

Get historical and estimated future earnings dates:

```python
company = Company("AAPL", client)

# Last earnings date
last = company.last_earnings()
print(f"Last Filing: {last['date']} ({last['form_type']})")

# Next earnings (estimated)
next_e = company.next_earnings()
print(f"Next Expected: {next_e['date']} ({next_e['fiscal_quarter']})")

# Historical earnings dates
history = company.earnings_history(limit=10)
for h in history:
    print(f"{h['date']} | {h['form_type']} | {h['fiscal_quarter']}")

# Full earnings calendar
calendar = company.get_earnings_calendar()
print(f"Fiscal Year End: {calendar.fiscal_year_end}")
df = calendar.get_historical_df()
```

### Example Output
```
Last Earnings:
  Filing Date: 2026-01-30
  Period End: 2025-12-27
  Form Type: 10-Q
  Quarter: Q1

Next Earnings (Estimated):
  Expected Date: 2026-04-30
  Form Type: 10-Q
  Quarter: Q2
```

## Rate Limiting

The client automatically respects SEC's rate limit (10 requests/second).

## Batch Fetching - Index Constituents

Fetch financials for entire indices:

```python
from secfin import SECClient, BatchFetcher

client = SECClient("MyApp contact@email.com")
fetcher = BatchFetcher(client)

# Fetch all S&P 500 companies (~500 companies)
sp500_data = fetcher.fetch_sp500()

# Fetch all NASDAQ 100 companies (~100 companies)
nasdaq_data = fetcher.fetch_nasdaq100()

# Fetch custom list of tickers
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "META"]
custom_data = fetcher.fetch_tickers(tickers)
```

### Progress Tracking

```python
def my_progress(current, total, ticker, status):
    print(f"Progress: {current}/{total} - {ticker}: {status}")

fetcher = BatchFetcher(client, on_progress=my_progress)
results = fetcher.fetch_nasdaq100()
```

### Export to DataFrame

```python
# Get summary DataFrame with key metrics
summary_df = fetcher.summary_dataframe(results)
print(summary_df)

# Get specific metric across all companies
revenue_df = fetcher.to_dataframe(results, "income_statement", "revenue")
print(revenue_df)
```

### Available Indices

| Index | Method | Companies |
|-------|--------|-----------|
| S&P 500 | `fetch_sp500()` | ~500 |
| NASDAQ 100 | `fetch_nasdaq100()` | ~100 |
| Russell 2000 | `fetch_russell2000(tickers)` | ~2000 (requires ticker list) |

### Russell 2000

Russell 2000 constituent list is not freely available. You need to provide your own list:

```python
# Load your Russell 2000 tickers
russell_tickers = [...]  # Your list of ~2000 tickers

results = fetcher.fetch_russell2000(tickers=russell_tickers)
```

## Requirements

- Python 3.9+
- requests
- pandas

## License

MIT
