Initial commit
This commit is contained in:
587
venv/lib/python3.10/site-packages/edgar/xbrl/docs/XBRL.md
Normal file
587
venv/lib/python3.10/site-packages/edgar/xbrl/docs/XBRL.md
Normal file
@@ -0,0 +1,587 @@
|
||||
# XBRL Class Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The `XBRL` class is the primary interface for working with XBRL (eXtensible Business Reporting Language) financial data from SEC filings. It provides structured access to financial statements, facts, and related data extracted from filings like 10-K, 10-Q, and 8-K reports.
|
||||
|
||||
XBRL documents contain:
|
||||
- **Financial statements** (Income Statement, Balance Sheet, Cash Flow, etc.)
|
||||
- **Facts** - Individual data points with values, periods, and dimensions
|
||||
- **Contexts** - Time periods and dimensional information
|
||||
- **Presentation** - How facts are organized into statements
|
||||
|
||||
## Getting XBRL Data
|
||||
|
||||
### From a Filing
|
||||
|
||||
```python
|
||||
# Get XBRL from any filing with financial data
|
||||
filing = company.get_filings(form="10-K").latest()
|
||||
xbrl = filing.xbrl()
|
||||
```
|
||||
|
||||
### Quick Check
|
||||
|
||||
```python
|
||||
# Print XBRL to see what's available
|
||||
print(xbrl)
|
||||
# Shows: company info, available statements, periods, and usage examples
|
||||
```
|
||||
|
||||
## Accessing Financial Statements
|
||||
|
||||
### Core Statement Methods
|
||||
|
||||
The XBRL class provides convenient methods for accessing standard financial statements:
|
||||
|
||||
```python
|
||||
# Access core financial statements
|
||||
income = xbrl.statements.income_statement()
|
||||
balance = xbrl.statements.balance_sheet()
|
||||
cashflow = xbrl.statements.cash_flow_statement()
|
||||
equity = xbrl.statements.statement_of_equity()
|
||||
comprehensive = xbrl.statements.comprehensive_income()
|
||||
```
|
||||
|
||||
### Access by Name
|
||||
|
||||
You can access any statement by its exact name as it appears in the filing:
|
||||
|
||||
```python
|
||||
# List all available statements
|
||||
print(xbrl.statements)
|
||||
|
||||
# Access specific statement by name
|
||||
cover_page = xbrl.statements['CoverPage']
|
||||
disclosure = xbrl.statements['CONDENSED CONSOLIDATED BALANCE SHEETS Unaudited']
|
||||
```
|
||||
|
||||
### Access by Index
|
||||
|
||||
Statements can also be accessed by their index position:
|
||||
|
||||
```python
|
||||
# Get statement by index (0-based)
|
||||
first_statement = xbrl.statements[0]
|
||||
sixth_statement = xbrl.statements[6]
|
||||
```
|
||||
|
||||
## Working with Periods
|
||||
|
||||
### Current Period Only
|
||||
|
||||
To work with just the most recent period's data:
|
||||
|
||||
```python
|
||||
# Get current period XBRL view
|
||||
current = xbrl.current_period
|
||||
|
||||
# Access statements for current period
|
||||
current_income = current.income_statement()
|
||||
current_balance = current.balance_sheet()
|
||||
```
|
||||
|
||||
### Multi-Period Statements
|
||||
|
||||
By default, statements include multiple periods for comparison:
|
||||
|
||||
```python
|
||||
# Get income statement with comparative periods
|
||||
income = xbrl.statements.income_statement()
|
||||
# Typically includes current year/quarter and prior periods
|
||||
|
||||
# Convert to DataFrame to see all periods
|
||||
df = income.to_dataframe()
|
||||
print(df.columns) # Shows all available periods
|
||||
```
|
||||
|
||||
### Available Periods
|
||||
|
||||
```python
|
||||
# See what periods are available
|
||||
for period in xbrl.reporting_periods:
|
||||
print(f"Period: {period['label']}, Key: {period['key']}")
|
||||
```
|
||||
|
||||
## Querying Facts
|
||||
|
||||
The `.facts` property provides a powerful query interface for finding specific data points:
|
||||
|
||||
### Basic Fact Queries
|
||||
|
||||
```python
|
||||
# Get all revenue facts
|
||||
revenue_facts = xbrl.facts.query().by_concept('Revenue').to_dataframe()
|
||||
|
||||
# Get net income facts
|
||||
net_income = xbrl.facts.query().by_concept('NetIncome').to_dataframe()
|
||||
|
||||
# Search by label instead of concept name
|
||||
revenue = xbrl.facts.query().by_label('Revenue').to_dataframe()
|
||||
```
|
||||
|
||||
### Filter by Period
|
||||
|
||||
```python
|
||||
# Get facts for a specific period
|
||||
period_key = "duration_2024-01-01_2024-12-31"
|
||||
facts_2024 = xbrl.facts.query().by_period_key(period_key).to_dataframe()
|
||||
|
||||
# Filter by fiscal year
|
||||
facts_fy2024 = xbrl.facts.query().by_fiscal_year(2024).to_dataframe()
|
||||
|
||||
# Filter by fiscal period
|
||||
q1_facts = xbrl.facts.query().by_fiscal_period("Q1").to_dataframe()
|
||||
```
|
||||
|
||||
### Filter by Statement Type
|
||||
|
||||
```python
|
||||
# Get all income statement facts
|
||||
income_facts = xbrl.facts.query().by_statement_type("IncomeStatement").to_dataframe()
|
||||
|
||||
# Get all balance sheet facts
|
||||
balance_facts = xbrl.facts.query().by_statement_type("BalanceSheet").to_dataframe()
|
||||
```
|
||||
|
||||
### Chaining Filters
|
||||
|
||||
```python
|
||||
# Combine multiple filters
|
||||
revenue_2024 = (xbrl.facts.query()
|
||||
.by_concept('Revenue')
|
||||
.by_fiscal_year(2024)
|
||||
.by_period_type('duration')
|
||||
.to_dataframe())
|
||||
```
|
||||
|
||||
### Pattern Matching
|
||||
|
||||
```python
|
||||
# Find all concepts matching a pattern (case-insensitive)
|
||||
asset_facts = xbrl.facts.query().by_concept('Asset', exact=False).to_dataframe()
|
||||
|
||||
# Search labels with pattern
|
||||
liability_facts = xbrl.facts.query().by_label('liabilities', exact=False).to_dataframe()
|
||||
```
|
||||
|
||||
## Converting to DataFrames
|
||||
|
||||
### Statement to DataFrame
|
||||
|
||||
```python
|
||||
# Convert any statement to pandas DataFrame
|
||||
income = xbrl.statements.income_statement()
|
||||
df = income.to_dataframe()
|
||||
|
||||
# DataFrame has:
|
||||
# - One row per line item
|
||||
# - One column per period
|
||||
# - Index is the concept/label
|
||||
```
|
||||
|
||||
### Facts to DataFrame
|
||||
|
||||
```python
|
||||
# Query returns DataFrame directly
|
||||
df = xbrl.facts.query().by_concept('Revenue').to_dataframe()
|
||||
|
||||
# DataFrame columns:
|
||||
# - concept: XBRL concept name
|
||||
# - label: Human-readable label
|
||||
# - value: Fact value
|
||||
# - period: Period identifier
|
||||
# - start: Period start date (for duration)
|
||||
# - end: Period end date
|
||||
# - unit: Unit of measure (e.g., USD)
|
||||
# - dimensions: Dimensional breakdowns (if any)
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Finding Specific Disclosures
|
||||
|
||||
```python
|
||||
# Get statements organized by category
|
||||
categories = xbrl.statements.get_statements_by_category()
|
||||
|
||||
# View all disclosures
|
||||
disclosures = categories['disclosure']
|
||||
for disc in disclosures:
|
||||
print(f"{disc['index']}: {disc['title']}")
|
||||
|
||||
# View all notes
|
||||
notes = categories['note']
|
||||
for note in notes:
|
||||
print(f"{note['index']}: {note['title']}")
|
||||
|
||||
# Get core financial statements
|
||||
core_statements = categories['statement']
|
||||
|
||||
# Or list all statements to find specific ones
|
||||
all_statements = xbrl.get_all_statements()
|
||||
for stmt in all_statements:
|
||||
print(f"{stmt['type']}: {stmt['title']}")
|
||||
|
||||
# Access by exact name or index
|
||||
risk_factors = xbrl.statements['RiskFactorsDisclosure']
|
||||
# Or by index from the category list
|
||||
first_disclosure = xbrl.statements[disclosures[0]['index']]
|
||||
```
|
||||
|
||||
### Cross-Period Analysis
|
||||
|
||||
```python
|
||||
# Get multi-period income statement
|
||||
income = xbrl.statements.income_statement()
|
||||
df = income.to_dataframe()
|
||||
|
||||
# Calculate year-over-year growth
|
||||
if len(df.columns) >= 2:
|
||||
current = df.iloc[:, 0]
|
||||
prior = df.iloc[:, 1]
|
||||
growth = ((current - prior) / prior * 100).round(2)
|
||||
print(f"Revenue growth: {growth.loc['Revenue']}%")
|
||||
```
|
||||
|
||||
### Working with Dimensions
|
||||
|
||||
```python
|
||||
# Query facts with specific dimensional breakdowns
|
||||
segment_revenue = (xbrl.facts.query()
|
||||
.by_concept('Revenue')
|
||||
.by_dimension('Segment', 'ProductSegment')
|
||||
.to_dataframe())
|
||||
|
||||
# Group by dimensions
|
||||
segment_totals = segment_revenue.groupby('dimensions')['value'].sum()
|
||||
```
|
||||
|
||||
### Custom Fact Filtering
|
||||
|
||||
```python
|
||||
# Use custom filter function
|
||||
large_amounts = xbrl.facts.query().by_value(lambda v: abs(v) > 1000000).to_dataframe()
|
||||
|
||||
# Custom filter with lambda
|
||||
recent_facts = xbrl.facts.query().by_custom(
|
||||
lambda fact: fact['end'] >= '2024-01-01'
|
||||
).to_dataframe()
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Extract Revenue from Income Statement
|
||||
|
||||
```python
|
||||
# Method 1: Via statement
|
||||
income = xbrl.statements.income_statement()
|
||||
df = income.to_dataframe()
|
||||
revenue = df.loc['Revenue']
|
||||
|
||||
# Method 2: Via facts query
|
||||
revenue_facts = xbrl.facts.query().by_concept('Revenues').to_dataframe()
|
||||
latest_revenue = revenue_facts.iloc[0]['value']
|
||||
```
|
||||
|
||||
### Compare Current vs Prior Year
|
||||
|
||||
```python
|
||||
# Get current period data
|
||||
current = xbrl.current_period
|
||||
current_income = current.income_statement()
|
||||
current_df = current_income.to_dataframe()
|
||||
|
||||
# Get full multi-period data
|
||||
full_income = xbrl.statements.income_statement()
|
||||
full_df = full_income.to_dataframe()
|
||||
|
||||
# Compare
|
||||
if len(full_df.columns) >= 2:
|
||||
comparison = pd.DataFrame({
|
||||
'Current': full_df.iloc[:, 0],
|
||||
'Prior': full_df.iloc[:, 1],
|
||||
'Change': full_df.iloc[:, 0] - full_df.iloc[:, 1]
|
||||
})
|
||||
print(comparison)
|
||||
```
|
||||
|
||||
### Extract Specific Disclosure Data
|
||||
|
||||
```python
|
||||
# Find debt-related disclosures
|
||||
all_statements = xbrl.get_all_statements()
|
||||
debt_statements = [s for s in all_statements if 'debt' in s['title'].lower()]
|
||||
|
||||
# Access first debt disclosure
|
||||
if debt_statements:
|
||||
debt_disclosure = xbrl.statements[debt_statements[0]['type']]
|
||||
debt_df = debt_disclosure.to_dataframe()
|
||||
```
|
||||
|
||||
### Export All Core Statements
|
||||
|
||||
```python
|
||||
# Export all core financial statements to CSV
|
||||
statements_to_export = {
|
||||
'income_statement': xbrl.statements.income_statement(),
|
||||
'balance_sheet': xbrl.statements.balance_sheet(),
|
||||
'cash_flow': xbrl.statements.cash_flow_statement(),
|
||||
}
|
||||
|
||||
for name, stmt in statements_to_export.items():
|
||||
if stmt:
|
||||
df = stmt.to_dataframe()
|
||||
df.to_csv(f"{name}.csv")
|
||||
```
|
||||
|
||||
### Build Custom Financial Summary
|
||||
|
||||
```python
|
||||
# Extract key metrics from multiple statements
|
||||
metrics = {}
|
||||
|
||||
# Revenue and profit from income statement
|
||||
income = xbrl.statements.income_statement()
|
||||
income_df = income.to_dataframe()
|
||||
metrics['Revenue'] = income_df.loc['Revenue', income_df.columns[0]]
|
||||
metrics['Net Income'] = income_df.loc['Net Income', income_df.columns[0]]
|
||||
|
||||
# Assets from balance sheet
|
||||
balance = xbrl.statements.balance_sheet()
|
||||
balance_df = balance.to_dataframe()
|
||||
metrics['Total Assets'] = balance_df.loc['Assets', balance_df.columns[0]]
|
||||
|
||||
# Cash flow from operations
|
||||
cashflow = xbrl.statements.cash_flow_statement()
|
||||
cashflow_df = cashflow.to_dataframe()
|
||||
metrics['Operating Cash Flow'] = cashflow_df.loc['Operating Activities', cashflow_df.columns[0]]
|
||||
|
||||
# Create summary DataFrame
|
||||
summary = pd.DataFrame([metrics])
|
||||
print(summary)
|
||||
```
|
||||
|
||||
## Entity Information
|
||||
|
||||
### Access Filing Metadata
|
||||
|
||||
```python
|
||||
# Get entity and filing information
|
||||
entity_info = xbrl.entity_info
|
||||
|
||||
print(f"Company: {entity_info.get('entity_name')}")
|
||||
print(f"Ticker: {entity_info.get('trading_symbol')}")
|
||||
print(f"CIK: {entity_info.get('entity_identifier')}")
|
||||
print(f"Form: {entity_info.get('document_type')}")
|
||||
print(f"Fiscal Year: {entity_info.get('document_fiscal_year_focus')}")
|
||||
print(f"Fiscal Period: {entity_info.get('document_fiscal_period_focus')}")
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Missing Statements
|
||||
|
||||
```python
|
||||
from edgar.xbrl.xbrl import StatementNotFound
|
||||
|
||||
try:
|
||||
equity = xbrl.statements.statement_of_equity()
|
||||
except StatementNotFound:
|
||||
print("Statement of equity not available in this filing")
|
||||
equity = None
|
||||
```
|
||||
|
||||
### Empty Query Results
|
||||
|
||||
```python
|
||||
# Query returns empty DataFrame if no matches
|
||||
results = xbrl.facts.query().by_concept('NonexistentConcept').to_dataframe()
|
||||
|
||||
if results.empty:
|
||||
print("No facts found matching query")
|
||||
```
|
||||
|
||||
### Handling Multiple Formats
|
||||
|
||||
```python
|
||||
# Some companies use different concept names
|
||||
revenue_concepts = ['Revenue', 'Revenues', 'SalesRevenue', 'RevenueFromContractWithCustomer']
|
||||
|
||||
for concept in revenue_concepts:
|
||||
revenue = xbrl.facts.query().by_concept(concept).to_dataframe()
|
||||
if not revenue.empty:
|
||||
print(f"Found revenue under concept: {concept}")
|
||||
break
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Caching
|
||||
|
||||
```python
|
||||
# Facts are cached after first access
|
||||
facts = xbrl.facts # First call - loads data
|
||||
facts2 = xbrl.facts # Subsequent calls use cache
|
||||
```
|
||||
|
||||
### Limiting Results
|
||||
|
||||
```python
|
||||
# Use limit() to reduce memory usage for large result sets
|
||||
sample_facts = xbrl.facts.query().limit(100).to_dataframe()
|
||||
```
|
||||
|
||||
### Efficient Filtering
|
||||
|
||||
```python
|
||||
# Apply specific filters early in the query chain
|
||||
# Good: specific filters first
|
||||
revenue = (xbrl.facts.query()
|
||||
.by_statement_type("IncomeStatement") # Narrow down first
|
||||
.by_concept("Revenue") # Then more specific
|
||||
.to_dataframe())
|
||||
|
||||
# Less efficient: broad query then filter
|
||||
all_facts = xbrl.facts.query().to_dataframe()
|
||||
revenue = all_facts[all_facts['concept'] == 'Revenue']
|
||||
```
|
||||
|
||||
## Data Structure Reference
|
||||
|
||||
### Key Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `statements` | Statements | Access to financial statements |
|
||||
| `facts` | FactsView | Query interface for facts |
|
||||
| `entity_info` | dict | Company and filing metadata |
|
||||
| `reporting_periods` | list | Available time periods |
|
||||
| `contexts` | dict | XBRL contexts (periods + dimensions) |
|
||||
| `units` | dict | Units of measure |
|
||||
| `current_period` | CurrentPeriodView | Current period only |
|
||||
|
||||
### Fact DataFrame Columns
|
||||
|
||||
When you convert facts to a DataFrame using `.to_dataframe()`, you get:
|
||||
|
||||
- `concept`: XBRL element name (e.g., 'Revenues', 'Assets')
|
||||
- `label`: Human-readable label
|
||||
- `value`: Fact value (numeric or text)
|
||||
- `period`: Period identifier
|
||||
- `start`: Period start date (for duration periods)
|
||||
- `end`: Period end date
|
||||
- `unit`: Unit of measure (e.g., 'USD', 'shares')
|
||||
- `dimensions`: Dictionary of dimensional breakdowns
|
||||
- `decimals`: Precision indicator
|
||||
|
||||
## Integration with Other Classes
|
||||
|
||||
### With Filing
|
||||
|
||||
```python
|
||||
# XBRL comes from filing
|
||||
filing = company.get_filings(form="10-K").latest()
|
||||
xbrl = filing.xbrl()
|
||||
|
||||
# Access back to filing if needed
|
||||
# (Store reference if you need it)
|
||||
```
|
||||
|
||||
### With Company
|
||||
|
||||
```python
|
||||
# Get multiple filings and compare XBRL data
|
||||
filings = company.get_filings(form="10-Q", count=4)
|
||||
|
||||
revenue_trend = []
|
||||
for filing in filings:
|
||||
xbrl = filing.xbrl()
|
||||
revenue = xbrl.facts.query().by_concept('Revenue').to_dataframe()
|
||||
if not revenue.empty:
|
||||
revenue_trend.append({
|
||||
'filing_date': filing.filing_date,
|
||||
'revenue': revenue.iloc[0]['value']
|
||||
})
|
||||
|
||||
trend_df = pd.DataFrame(revenue_trend)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Check statement availability** before accessing:
|
||||
```python
|
||||
print(xbrl) # See what's available
|
||||
```
|
||||
|
||||
2. **Use query chaining** for complex filters:
|
||||
```python
|
||||
results = (xbrl.facts.query()
|
||||
.by_statement_type("IncomeStatement")
|
||||
.by_fiscal_year(2024)
|
||||
.by_period_type("duration")
|
||||
.to_dataframe())
|
||||
```
|
||||
|
||||
3. **Handle missing data gracefully**:
|
||||
```python
|
||||
try:
|
||||
stmt = xbrl.statements.equity_statement()
|
||||
except StatementNotFound:
|
||||
stmt = None
|
||||
```
|
||||
|
||||
4. **Convert to DataFrame for analysis**:
|
||||
```python
|
||||
df = statement.to_dataframe() # Easier to work with
|
||||
```
|
||||
|
||||
5. **Use current_period for latest data**:
|
||||
```python
|
||||
current = xbrl.current_period
|
||||
latest_income = current.income_statement()
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Statement not found"
|
||||
|
||||
**Cause**: Statement doesn't exist in this filing or uses non-standard name
|
||||
|
||||
**Solution**:
|
||||
```python
|
||||
# List all available statements
|
||||
print(xbrl.statements)
|
||||
|
||||
# Or check available types
|
||||
all_statements = xbrl.get_all_statements()
|
||||
statement_types = [s['type'] for s in all_statements]
|
||||
```
|
||||
|
||||
### "No facts found"
|
||||
|
||||
**Cause**: Concept name doesn't match or no data for period
|
||||
|
||||
**Solution**:
|
||||
```python
|
||||
# Try pattern matching
|
||||
results = xbrl.facts.query().by_concept('Revenue', exact=False).to_dataframe()
|
||||
|
||||
# Or search by label
|
||||
results = xbrl.facts.query().by_label('revenue').to_dataframe()
|
||||
```
|
||||
|
||||
### "Empty DataFrame"
|
||||
|
||||
**Cause**: Period filter too restrictive or no data available
|
||||
|
||||
**Solution**:
|
||||
```python
|
||||
# Check available periods
|
||||
print(xbrl.reporting_periods)
|
||||
|
||||
# Query without period filter
|
||||
all_revenue = xbrl.facts.query().by_concept('Revenue').to_dataframe()
|
||||
```
|
||||
|
||||
This comprehensive guide covers the essential patterns for working with XBRL data in edgartools. For more examples, see the Filing and Statement documentation.
|
||||
Reference in New Issue
Block a user