RAW TO RELIABLE / CASE STUDY / PROJECT 01
SEC Financial Data Platform
Real SEC company facts move through Python ingestion, validation, Snowflake and dbt marts into anomaly flags and a Tableau dashboard; a Kafka and Spark stream processes filing events.
95 of 100 requested company records were ingested; 64,619 filtered financial facts feed tested models, rule-based anomaly flags, and an annual Tableau dashboard. A separate Kafka and Spark path handles filing events.
Focus areas
- Batch ingestion
- Financial-data normalization
- Statement classification
- Data quality
- dbt financial marts
- Rule-based anomaly detection
- Filing-event streaming
Technology stack
01 / Executive Overview
Executive Overview
This completed portfolio system turns real SEC EDGAR Company Facts JSON into comparable financial datasets. Python ingested and archived source responses in S3, flattened and filtered financial facts, and loaded a Snowflake RAW table. Great Expectations validated the historical raw archive and checks the warehouse table and final marts; dbt creates financial and anomaly models consumed by Tableau.
A second pipeline produces SEC filing events through Kafka. Spark Structured Streaming validates and deduplicates them, quarantines invalid events, and merges valid micro-batches into a separate Snowflake table. Its output has a dbt staging model; it is not the source of the financial dashboard.
Historical batch
Company Facts ingestion, filtering, Snowflake load, and validation completed.
Analytical output
dbt financial marts, anomaly flags, and annual Tableau dashboard completed.
Filing-event stream
Kafka, Spark, quarantine, Snowflake MERGE, and dbt staging validated.
02 / Problem
Problem
SEC company facts contain repeated comparative periods, varying XBRL tags, amended filings, and both duration and point-in-time values. Direct aggregation can double count periods or compare unlike metrics.
The pipeline keeps the source records, selects filing-aware facts, validates each analytical boundary, and produces consistent company-period metrics and explicit anomaly flags for review.
Engineering problem dimensions
- 01Issuer-specific XBRL tags
- 02Quarterly versus annual reporting grain
- 03Duration versus instant facts
- 04Comparative filings and amendments
- 05Failed or empty company responses
- 06Duplicate and invalid filing events
03 / Scope & Limits
Scope & Limits
The historical ingestion run is labeled 2024 Q1, but each Company Facts response contains historical facts. The dashboard shows annual periods through 2025; the archive label is not a filter on financial reporting years.
- Requested CIKs
- 100
- Ingested companies
- 95
- Failed requests
- 5
- Filtered financial facts
- 64,619
Known limitations
- The first-100-CIK sample does not cover all SEC issuers.
- Five requests failed in the recorded ingestion summary; the source does not classify their causes.
- Mapped metrics and accepted filing forms limit coverage of issuer-specific disclosures.
- Anomaly flags use fixed rules and require analytical review.
- The S3 archive was exported to local cold storage and AWS resources were decommissioned; rerunning raw validation requires restoring that archive or a new source load.
04 / Architecture
Architecture
Historical batch data flow
Filing-event data flow
Quality & orchestration boundaries
05 / Ingestion & Flow
Ingestion & Flow
- 01Implemented
Historical batch
Ingest SEC facts
Python requests the first 100 CIKs from SEC company_tickers.json, then fetches each Company Facts JSON response. A 0.15-second pause spaces requests; individual failures are logged and skipped.
- 02Implemented
Raw boundary
Preserve and inspect
The original JSON and ingestion summary were archived in S3. Great Expectations checks object counts, sizes, parseability, identity fields, and summary counts. The S3 archive was later exported and AWS decommissioned.
- 03Implemented
Python staging
Normalize financial facts
Pandas flattens XBRL facts, maps selected tags to metrics, classifies statements and durations, and retains quarterly 10-Q income facts, annual 10-K income facts, and instant balance-sheet facts.
- 04Implemented
Snowflake + dbt
Load and model
A Python loader writes the filtered CSV to Snowflake SEC_FINANCIALS. dbt views standardize and canonicalize periods; table marts expose company financials and anomaly flags.
- 05Implemented
Airflow quality gate
Orchestrate the analytical build
The manually triggered Docker Airflow DAG verifies the project mount, validates the warehouse, runs dbt build, then validates marts. Ingestion and the streaming path are outside this DAG.
- 06Implemented
Separate streaming path
Process filing events
Kafka feeds Spark Structured Streaming. Invalid events go to Parquet quarantine; valid events use watermark deduplication and a Snowflake MERGE keyed by accession number, then enter dbt staging.
06 / Warehouse & Modeling
Warehouse & Modeling
Snowflake RAW holds the loaded financial table and filing-event table. dbt separates staging views, intermediate views, and materialized marts; this project does not implement a separate CORE schema or a Snowflake VARIANT landing layer.
RAW source
ImplementedSEC_FINANCIALS contains the filtered flat financial records; SEC_FILING_EVENTS_STREAM holds validated filing-event rows after MERGE.
dbt staging
Implementedstg_sec_financials and stg_sec_filing_events select and name fields from the two Snowflake sources.
- cik
- entity_name
- metric_name
- value
- form
- filed_date
- accn
dbt intermediate
ImplementedIncome and balance-sheet models select canonical facts by company, metric, and period, prioritizing non-restatements and using filing date and accession as tie-breakers. Quarterly metrics organize analysis grain.
dbt marts
Implementeddim_companies, fct_company_financials, and fct_financial_anomalies serve company-period analysis, ratios, and rule-based flags.
Implemented analytical models
- Company dimension
- Company financial fact
- Financial anomaly fact
- Filing-event staging model
Duration semantics
- Income facts from 10-Q forms use quarter duration; 10-K forms use full-year duration.
- Balance-sheet facts use instant values.
- Later comparative filings are ranked to avoid duplicate analytical periods.
07 / Data Quality
Data Quality
The archived S3 data was validated before AWS decommissioning. Current warehouse and mart checks, dbt tests, and the separate streaming validation path cover the downstream system.
Validate source structure and reporting context before trusting a financial comparison.
Raw archive
Great Expectations checks 90–100 JSON objects, CIK format, parse success, entity name, facts structure, size, and ingestion counts.
Warehouse
The SEC_FINANCIALS suite checks exact row count, accepted metrics, USD units, forms, non-null identifiers and values, statement types, durations, and period states. The README reports 10 of 10 expectations passed.
dbt
The project defines 37 data tests across its models; the filing-event staging model reports 13 passing checks with no warnings or errors.
Marts
Two Great Expectations suites check required identifiers and periods, ratio ranges, anomaly flags, and business rules.
Streaming
Spark labels invalid events for Parquet quarantine; valid events use accession deduplication, a persistent checkpoint, and idempotent Snowflake MERGE.
08 / Engineering Decisions
Engineering Decisions
01
Preserve SEC responses
- Decision
- Archive Company Facts JSON before flattening.
- Rationale
- Original XBRL tags and filing context remain available when mappings or period rules change. AWS storage was later decommissioned after export.
02
Select canonical facts
- Decision
- Rank repeated company-metric-period records before mart construction.
- Rationale
- Later comparative filings otherwise duplicate previously reported periods in BI aggregations.
03
Keep reporting grains distinct
- Decision
- Filter income facts by filing form and duration; keep balance-sheet facts at an instant.
- Rationale
- Annual, quarterly, and point-in-time values cannot be compared as one grain.
04
Use liabilities fallback
- Decision
- Prefer reported liabilities and derive assets minus equity only when absent.
- Rationale
- Issuers expose different tag sets; the fallback improves coverage while preserving directly reported values.
05
Gate the dbt build
- Decision
- Run warehouse validation before dbt and mart validation after it.
- Rationale
- The Airflow task order stops downstream modeling when an upstream check fails.
06
Make streaming writes idempotent
- Decision
- Deduplicate in Spark and MERGE on accession number in Snowflake.
- Rationale
- A resent filing event was verified to remain a single warehouse record.
09 / Trade-offs & Limits
Trade-offs & Limits
01
Sample coverage
The historical run requested only the first 100 CIKs and succeeded for 95. Its five failures have no documented cause classification or automatic retry.
02
Historical AWS layer
S3 was used for raw storage, then exported to local cold storage and decommissioned. It is historical architecture, not a current runtime dependency.
03
Manual batch trigger
The Airflow DAG has schedule=None and starts after the data is loaded. It orchestrates warehouse validation, dbt, and mart validation, not SEC ingestion.
04
Full batch reload
The Python warehouse loader truncates SEC_FINANCIALS before writing the filtered CSV. It does not implement an incremental financial-fact load.
05
Rule-based signals
Revenue, margin, and debt flags use fixed thresholds. They identify review candidates; they do not diagnose financial causes.
10 / Analytics & Output
Analytics & Output
95 archived Company Facts responses and a recorded 100/95/5 ingestion summary
64,619 filtered financial fact rows loaded for warehouse modeling
Company financial mart with revenue, income, assets, liabilities, equity, margins, and debt-to-assets ratio
Anomaly mart flags revenue drops over 30%, absolute net-margin shifts of at least 0.20, and debt-ratio rises of at least 0.15 versus the prior period
Tableau annual dashboard with company and year filters, revenue and income trends, margins, and balance-sheet leverage
Separate validated filing-event table and dbt staging model

Final Tableau dashboard using Snowflake financial marts. The screenshot shows one company selection and annual reporting periods.
11 / Validation & Result
Validation & Result
The source README reports a successful four-task Airflow run: mount check, warehouse validation, dbt build, and mart validation.
The warehouse suite reports 10 of 10 expectations passed; the streaming dbt staging checks report 13 PASS, 0 WARN, 0 ERROR.
A new filing event reached Snowflake without restarting Spark; resending its accession number left one row.
The source repository contains the final Tableau dashboard image and a sanitized public workbook definition.
12 / Future Improvements
Future Improvements
13 / Technology Stack
Technology Stack
- Source & ingestion
- SEC EDGAR Company Facts APIImplemented
- Python / PandasImplemented
- Historical raw storage
- AWS S3 (decommissioned)Implemented
- JSON / CSVImplemented
- Warehouse & modeling
- SnowflakeImplemented
- dbt Core / SQLImplemented
- Quality & orchestration
- Great ExpectationsImplemented
- Apache Airflow / DockerImplemented
- Filing-event stream
- KafkaImplemented
- Spark Structured Streaming / ParquetImplemented
- Analytics
- TableauImplemented