equity-aggregator 0.1.4__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.github/workflows/publish-pypi.yml +4 -1
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/PKG-INFO +2 -2
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/README.md +1 -1
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/pyproject.toml +1 -1
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/README.md +18 -12
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_enrich.py +211 -15
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/uv.lock +1 -1
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.dockerignore +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.env_example +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.github/workflows/publish-build-release.yml +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.github/workflows/validate-push.yml +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.gitignore +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.python-version +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/Dockerfile +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/LICENCE.txt +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/docker-compose.yml +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/intrinio_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/lseg_equities.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/sec_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/stock_analysis_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/tiprank_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/tradingview_equities.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/xetra_equities.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/yfinance_quote_summary_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/yfinance_search_equity.json +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/examples/data_integrity_analysis.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/_utils/_client.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/_utils/_record_types.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/intrinio.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/lseg.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/sec.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/stock_analysis.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/tradingview.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/xetra.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/api.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/download.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/gleif.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/fuzzy.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/json.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/quote_summary.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/search.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/auth.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/ranking.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/transport.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/yfinance.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/reference_lookup/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/reference_lookup/exchange_rate_api.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/data_sources/reference_lookup/openfigi.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/dispatcher.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/main.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/cli/signals.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/_utils/_load_converter.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/_utils/_merge.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/_utils/_merge_config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/_utils/_strategies.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/resolve.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/runner.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/seed.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/canonicalise.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/convert.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/enrich.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/group.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/identify.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/pipeline/transforms/parse.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/retrieval/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/domain/retrieval/retrieval.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/logging_config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/canonical.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/feed_validators.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/gleif_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/intrinio_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/lseg_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/sec_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/stock_analysis_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/tradingview_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/xetra_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/feeds/yfinance_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/raw.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/types.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/schemas/validators.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/_utils.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/cache.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/data_store.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/export.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/storage/metadata.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/README.md +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/conftest.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/live/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/_utils/test_client.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/_helpers.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_api.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_download.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_gleif.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_helpers.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_fuzzy.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_json.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_quote_summary.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_search.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_auth.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_ranking.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_session.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_transport.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/reference_lookup/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/reference_lookup/test_exchange_rate_api.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/adapters/data_sources/reference_lookup/test_openfigi.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/cli/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/cli/test_config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/cli/test_dispatcher.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/cli/test_main.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/cli/test_parser.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/_utils/test__merge.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/_utils/test__merge_config.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/_utils/test__strategies.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/_utils/test_load_converter.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/test_resolve.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_canonicalise.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_convert.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_group.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_identify.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/pipeline/transforms/test_parse.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/domain/retrieval/test_retrieval.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_feed_validators.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_gleif_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_intrinio_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_lseg_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_sec_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_stock_analysis_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_tradingview_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_xetra_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/feeds/test_yfinance_feed_data.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/test_canonical.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/test_raw.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/test_types.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/schemas/test_validators.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/__init__.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/test__utils.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/test_cache.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/test_data_store.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/test_export.py +0 -0
- {equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/tests/unit/storage/test_metadata.py +0 -0
|
@@ -77,6 +77,9 @@ jobs:
|
|
|
77
77
|
sed -i "3s/.*/version = \"$NEW_VERSION\"/" pyproject.toml
|
|
78
78
|
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
79
79
|
|
|
80
|
+
- name: Update lockfile
|
|
81
|
+
run: uv lock
|
|
82
|
+
|
|
80
83
|
- name: Configure git
|
|
81
84
|
run: |
|
|
82
85
|
git config user.name "github-actions[bot]"
|
|
@@ -84,7 +87,7 @@ jobs:
|
|
|
84
87
|
|
|
85
88
|
- name: Commit and tag version bump
|
|
86
89
|
run: |
|
|
87
|
-
git add pyproject.toml
|
|
90
|
+
git add pyproject.toml uv.lock
|
|
88
91
|
git commit -m "chore: bump version to ${{ steps.bump_version.outputs.new_version }} [skip ci]"
|
|
89
92
|
git tag "v${{ steps.bump_version.outputs.new_version }}"
|
|
90
93
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: equity-aggregator
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Financial equity data aggregation toolkit
|
|
5
5
|
Project-URL: Homepage, https://github.com/gregorykelleher/equity-aggregator
|
|
6
6
|
Project-URL: Repository, https://github.com/gregorykelleher/equity-aggregator
|
|
@@ -45,7 +45,7 @@ Discovery feeds provide raw equity data from primary market sources:
|
|
|
45
45
|
| 🇺🇸 SEC | United States | Securities and Exchange Commission - US-listed equities |
|
|
46
46
|
| 🇺🇸 Stock Analysis | United States | Stock Analysis - Global equities |
|
|
47
47
|
| 🇺🇸 TradingView | United States | TradingView - US-listed equities |
|
|
48
|
-
| 🇩🇪 XETRA |
|
|
48
|
+
| 🇩🇪 XETRA | International | Deutsche Börse electronic trading platform - Global listed equities |
|
|
49
49
|
|
|
50
50
|
### Enrichment Feeds
|
|
51
51
|
|
|
@@ -23,7 +23,7 @@ Discovery feeds provide raw equity data from primary market sources:
|
|
|
23
23
|
| 🇺🇸 SEC | United States | Securities and Exchange Commission - US-listed equities |
|
|
24
24
|
| 🇺🇸 Stock Analysis | United States | Stock Analysis - Global equities |
|
|
25
25
|
| 🇺🇸 TradingView | United States | TradingView - US-listed equities |
|
|
26
|
-
| 🇩🇪 XETRA |
|
|
26
|
+
| 🇩🇪 XETRA | International | Deutsche Börse electronic trading platform - Global listed equities |
|
|
27
27
|
|
|
28
28
|
### Enrichment Feeds
|
|
29
29
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
The equity aggregator is a sophisticated financial data processing system that aggregates equity information from multiple discovery sources (LSEG, SEC, Stock Analysis, TradingView, XETRA) and enriches it with supplementary data from Yahoo Finance and
|
|
5
|
+
The equity aggregator is a sophisticated financial data processing system that aggregates equity information from multiple discovery sources (LSEG, SEC, Stock Analysis, TradingView, XETRA and Intrinio) and enriches it with supplementary data from Yahoo Finance and the Global LEI Foundation.
|
|
6
6
|
|
|
7
7
|
## Architecture & Design
|
|
8
8
|
|
|
@@ -15,6 +15,7 @@ src/equity_aggregator/
|
|
|
15
15
|
├── cli/ # Presentation Layer - User Interface
|
|
16
16
|
├── domain/ # Business Logic Layer - Core Domain
|
|
17
17
|
│ ├── pipeline/ # Aggregation pipeline orchestration
|
|
18
|
+
│ ├── retrieval/ # Canonical equity download and retrieval
|
|
18
19
|
│ └── _utils/ # Domain-specific utilities
|
|
19
20
|
├── adapters/ # Infrastructure Layer - External Integrations
|
|
20
21
|
│ └── data_sources/ # Data source adapters
|
|
@@ -39,7 +40,7 @@ Raw Data Sources → Parse → Convert → Identify → Group → Enrich → Can
|
|
|
39
40
|
|
|
40
41
|
Orchestrates parallel data fetching from discovery feeds:
|
|
41
42
|
|
|
42
|
-
- Fetches data from LSEG, SEC, Stock Analysis, TradingView, and
|
|
43
|
+
- Fetches data from LSEG, SEC, Stock Analysis, TradingView, XETRA and Intrinio concurrently
|
|
43
44
|
- Combines all feed data into a single stream for processing
|
|
44
45
|
- Maintains feed source metadata for downstream processing
|
|
45
46
|
|
|
@@ -64,14 +65,13 @@ Standardises financial data to USD reference currency:
|
|
|
64
65
|
Enriches records with global identification metadata:
|
|
65
66
|
|
|
66
67
|
- Queries OpenFIGI API for FIGI identifiers
|
|
67
|
-
- Adds CUSIP, ISIN, and other standard identifiers
|
|
68
68
|
- Creates globally unique equity identities
|
|
69
69
|
|
|
70
70
|
#### 5. **Group**
|
|
71
71
|
|
|
72
72
|
Groups equities by Share Class FIGI:
|
|
73
73
|
|
|
74
|
-
- Groups records with identical
|
|
74
|
+
- Groups records with identical Share Class FIGI values
|
|
75
75
|
- Preserves all discovery feed source data for later merging
|
|
76
76
|
- Each group represents the same equity from multiple discovery sources
|
|
77
77
|
- Yields groups as `list[RawEquity]` for enrichment processing
|
|
@@ -80,11 +80,10 @@ Groups equities by Share Class FIGI:
|
|
|
80
80
|
|
|
81
81
|
Fetches enrichment data and performs comprehensive single merge:
|
|
82
82
|
|
|
83
|
-
-
|
|
84
|
-
- Queries enrichment feeds (Yahoo Finance,
|
|
83
|
+
- Fetches representative identifiers from discovery data sources
|
|
84
|
+
- Queries enrichment feeds (Yahoo Finance, Global LEI Foundation) using these identifiers
|
|
85
85
|
- Performs single merge of all sources (discovery + enrichment) for optimal data quality
|
|
86
|
-
-
|
|
87
|
-
- Applies controlled concurrency to respect API limits
|
|
86
|
+
- Applies controlled concurrency to enrichment feeds to respect API limits
|
|
88
87
|
|
|
89
88
|
#### 7. **Canonicalise**
|
|
90
89
|
|
|
@@ -102,7 +101,7 @@ The pipeline uses asynchronous operations to process thousands of equity records
|
|
|
102
101
|
|
|
103
102
|
**Parallel Data Fetching**
|
|
104
103
|
|
|
105
|
-
- All discovery feeds (LSEG, SEC, Stock Analysis, TradingView, XETRA) are fetched simultaneously
|
|
104
|
+
- All discovery feeds (LSEG, SEC, Stock Analysis, TradingView, XETRA, Intrinio) are fetched simultaneously
|
|
106
105
|
|
|
107
106
|
**Streaming Pipeline**
|
|
108
107
|
|
|
@@ -110,7 +109,9 @@ The pipeline uses asynchronous operations to process thousands of equity records
|
|
|
110
109
|
|
|
111
110
|
**Controlled Concurrency**
|
|
112
111
|
|
|
113
|
-
- External API calls (OpenFIGI, Yahoo Finance,
|
|
112
|
+
- External API calls (OpenFIGI, Yahoo Finance, GLEIF) use semaphores to limit concurrent requests and respect rate limits
|
|
113
|
+
- Each enrichment feed has a configurable concurrency limit
|
|
114
|
+
- Fetch operations include timeout protection to prevent indefinite blocking
|
|
114
115
|
|
|
115
116
|
**Non-blocking Operations**
|
|
116
117
|
|
|
@@ -145,11 +146,14 @@ schemas/
|
|
|
145
146
|
├── raw.py # RawEquity - intermediate pipeline format
|
|
146
147
|
├── canonical.py # CanonicalEquity - final standardised format
|
|
147
148
|
├── types.py # Type definitions and validators
|
|
149
|
+
├── validators.py # Reusable validators for identifiers and financial data
|
|
148
150
|
└── feeds/ # Feed-specific data models
|
|
149
151
|
├── lseg_feed_data.py
|
|
150
152
|
├── sec_feed_data.py
|
|
151
153
|
├── stock_analysis_feed_data.py
|
|
152
154
|
├── tradingview_feed_data.py
|
|
155
|
+
├── gleif_feed_data.py
|
|
156
|
+
├── feed_validators.py
|
|
153
157
|
├── xetra_feed_data.py
|
|
154
158
|
├── yfinance_feed_data.py
|
|
155
159
|
└── intrinio_feed_data.py
|
|
@@ -158,6 +162,7 @@ schemas/
|
|
|
158
162
|
### Critical Role of Schemas
|
|
159
163
|
|
|
160
164
|
#### 1. **Data Validation at Boundaries**
|
|
165
|
+
|
|
161
166
|
Each feed has a dedicated Pydantic schema that:
|
|
162
167
|
- Validates incoming data structure and types
|
|
163
168
|
- Normalises field names and formats
|
|
@@ -204,6 +209,7 @@ class LsegFeedData(BaseModel):
|
|
|
204
209
|
- **Stock Analysis**: US equities with comprehensive financial metrics
|
|
205
210
|
- **TradingView**: US equities with comprehensive financial metrics
|
|
206
211
|
- **XETRA**: Deutsche Börse Stock Exchange
|
|
212
|
+
- **Intrinio**: US financial data API providing company, securities, and real-time quote data
|
|
207
213
|
|
|
208
214
|
**Characteristics**:
|
|
209
215
|
|
|
@@ -213,12 +219,12 @@ class LsegFeedData(BaseModel):
|
|
|
213
219
|
### Enrichment Feeds (Supplementary Sources)
|
|
214
220
|
|
|
215
221
|
- **Yahoo Finance**: Market data and financial metrics
|
|
216
|
-
- **
|
|
222
|
+
- **Global LEI Foundation**: ISIN->LEI mapping for Legal Entity Identifier enrichment
|
|
217
223
|
|
|
218
224
|
**Characteristics**
|
|
219
225
|
|
|
220
226
|
- Provides additional financial metrics (market cap, analyst ratings, etc.)
|
|
221
|
-
- Uses representative identifiers from discovery sources for
|
|
227
|
+
- Uses representative identifiers from discovery sources for look-up
|
|
222
228
|
- Applied after grouping but before final merge
|
|
223
229
|
|
|
224
230
|
## Equity Aggregator Components
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# transforms/test_enrich.py
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import
|
|
5
|
-
from
|
|
4
|
+
from collections.abc import AsyncIterable, AsyncIterator
|
|
5
|
+
from contextlib import AsyncExitStack, asynccontextmanager
|
|
6
6
|
from decimal import Decimal
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
@@ -10,8 +10,13 @@ import pytest
|
|
|
10
10
|
from equity_aggregator.domain._utils import EquityIdentifiers, extract_identifiers
|
|
11
11
|
from equity_aggregator.domain.pipeline.transforms.enrich import (
|
|
12
12
|
EnrichmentFeed,
|
|
13
|
+
FeedSpec,
|
|
13
14
|
_enrich_equity_group,
|
|
14
15
|
_enrich_from_feed,
|
|
16
|
+
_init_feed,
|
|
17
|
+
_open_feeds,
|
|
18
|
+
_process_stream,
|
|
19
|
+
_rate_limited,
|
|
15
20
|
_safe_fetch,
|
|
16
21
|
_to_usd,
|
|
17
22
|
_validate,
|
|
@@ -49,6 +54,30 @@ class ErrorFeedData:
|
|
|
49
54
|
raise _ValidationError("validation failed")
|
|
50
55
|
|
|
51
56
|
|
|
57
|
+
class StubFeed:
|
|
58
|
+
"""
|
|
59
|
+
Test feed that returns a fixed response.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, response: dict[str, object]) -> None:
|
|
63
|
+
self.response = response
|
|
64
|
+
|
|
65
|
+
async def fetch_equity(self, **kwargs: object) -> dict[str, object]:
|
|
66
|
+
return self.response
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def stub_feed_factory(response: dict[str, object]) -> object:
|
|
70
|
+
"""
|
|
71
|
+
Create an async context manager factory for StubFeed.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@asynccontextmanager
|
|
75
|
+
async def factory() -> AsyncIterator[StubFeed]:
|
|
76
|
+
yield StubFeed(response)
|
|
77
|
+
|
|
78
|
+
return factory
|
|
79
|
+
|
|
80
|
+
|
|
52
81
|
def test_extract_identifiers_single_source() -> None:
|
|
53
82
|
"""
|
|
54
83
|
ARRANGE: single RawEquity with identifiers
|
|
@@ -301,21 +330,30 @@ def test_safe_fetch_success_returns_dict() -> None:
|
|
|
301
330
|
assert actual == {"foo": "bar"}
|
|
302
331
|
|
|
303
332
|
|
|
304
|
-
def
|
|
333
|
+
def test_process_stream_empty_stream_yields_nothing() -> None:
|
|
305
334
|
"""
|
|
306
|
-
ARRANGE: async stream that never yields
|
|
307
|
-
ACT: run
|
|
335
|
+
ARRANGE: async stream that never yields and test feed
|
|
336
|
+
ACT: run _process_stream
|
|
308
337
|
ASSERT: yields empty list
|
|
309
338
|
"""
|
|
310
339
|
|
|
311
|
-
|
|
340
|
+
async def good_fetcher(
|
|
341
|
+
symbol: str,
|
|
342
|
+
name: str,
|
|
343
|
+
isin: str | None,
|
|
344
|
+
cusip: str | None,
|
|
345
|
+
**kwargs: object,
|
|
346
|
+
) -> dict[str, object]:
|
|
347
|
+
return {}
|
|
348
|
+
|
|
349
|
+
mock_feed = EnrichmentFeed(fetch=good_fetcher, model=GoodFeedData)
|
|
312
350
|
|
|
313
351
|
async def empty_src() -> AsyncIterable[list[RawEquity]]:
|
|
314
352
|
if False:
|
|
315
353
|
yield
|
|
316
354
|
|
|
317
355
|
async def runner() -> list[RawEquity]:
|
|
318
|
-
return [equity async for equity in
|
|
356
|
+
return [equity async for equity in _process_stream(empty_src(), (mock_feed,))]
|
|
319
357
|
|
|
320
358
|
actual = asyncio.run(runner())
|
|
321
359
|
assert actual == []
|
|
@@ -728,15 +766,12 @@ def test_to_usd_converts_currency() -> None:
|
|
|
728
766
|
assert actual.currency == "USD"
|
|
729
767
|
|
|
730
768
|
|
|
731
|
-
|
|
732
|
-
def test_enrich_passes_through_groups() -> None:
|
|
769
|
+
def test_process_stream_passes_through_groups() -> None:
|
|
733
770
|
"""
|
|
734
|
-
ARRANGE: async stream of equity groups
|
|
735
|
-
ACT: run
|
|
736
|
-
ASSERT: yields merged equities
|
|
771
|
+
ARRANGE: async stream of equity groups and test feeds
|
|
772
|
+
ACT: run _process_stream over that stream
|
|
773
|
+
ASSERT: yields merged equities for each group
|
|
737
774
|
"""
|
|
738
|
-
os.environ.setdefault("INTRINIO_API_KEY", "DUMMY_KEY")
|
|
739
|
-
|
|
740
775
|
first_equity = RawEquity(
|
|
741
776
|
name="ONE",
|
|
742
777
|
symbol="ONE",
|
|
@@ -757,12 +792,30 @@ def test_enrich_passes_through_groups() -> None:
|
|
|
757
792
|
market_cap=Decimal("200"),
|
|
758
793
|
)
|
|
759
794
|
|
|
795
|
+
async def good_fetcher(
|
|
796
|
+
symbol: str,
|
|
797
|
+
name: str,
|
|
798
|
+
isin: str | None,
|
|
799
|
+
cusip: str | None,
|
|
800
|
+
**kwargs: object,
|
|
801
|
+
) -> dict[str, object]:
|
|
802
|
+
return {
|
|
803
|
+
"name": name,
|
|
804
|
+
"symbol": symbol,
|
|
805
|
+
"mics": ["XLON"],
|
|
806
|
+
"currency": "USD",
|
|
807
|
+
"last_price": Decimal("10"),
|
|
808
|
+
"market_cap": Decimal("1000"),
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
mock_feed = EnrichmentFeed(fetch=good_fetcher, model=GoodFeedData)
|
|
812
|
+
|
|
760
813
|
async def source() -> AsyncIterable[list[RawEquity]]:
|
|
761
814
|
yield [first_equity]
|
|
762
815
|
yield [second_equity]
|
|
763
816
|
|
|
764
817
|
async def runner() -> list[RawEquity]:
|
|
765
|
-
return [equity async for equity in
|
|
818
|
+
return [equity async for equity in _process_stream(source(), (mock_feed,))]
|
|
766
819
|
|
|
767
820
|
actual = asyncio.run(runner())
|
|
768
821
|
|
|
@@ -840,3 +893,146 @@ def test_enrich_from_feed_returns_validated_when_currency_is_none() -> None:
|
|
|
840
893
|
actual = asyncio.run(_enrich_from_feed(identifiers, mock_feed))
|
|
841
894
|
|
|
842
895
|
assert actual.lei == "G1MSYD2DQZX30V5DMX12"
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def test_rate_limited_passes_through_result() -> None:
|
|
899
|
+
"""
|
|
900
|
+
ARRANGE: async function wrapped with _rate_limited
|
|
901
|
+
ACT: call the wrapped function
|
|
902
|
+
ASSERT: returns the original function's result
|
|
903
|
+
"""
|
|
904
|
+
|
|
905
|
+
async def simple_fetcher(**kwargs: object) -> dict[str, object]:
|
|
906
|
+
return {"result": "success"}
|
|
907
|
+
|
|
908
|
+
semaphore = asyncio.Semaphore(10)
|
|
909
|
+
wrapped = _rate_limited(simple_fetcher, semaphore)
|
|
910
|
+
|
|
911
|
+
actual = asyncio.run(wrapped(symbol="TEST"))
|
|
912
|
+
|
|
913
|
+
assert actual == {"result": "success"}
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def test_rate_limited_serialises_calls_with_semaphore_of_one() -> None:
|
|
917
|
+
"""
|
|
918
|
+
ARRANGE: semaphore with limit of 1, two concurrent calls
|
|
919
|
+
ACT: start both calls simultaneously
|
|
920
|
+
ASSERT: first call completes before second call starts
|
|
921
|
+
"""
|
|
922
|
+
call_order: list[str] = []
|
|
923
|
+
|
|
924
|
+
async def tracking_fetcher(order: str, **kwargs: object) -> dict[str, object]:
|
|
925
|
+
call_order.append(f"{order}_start")
|
|
926
|
+
await asyncio.sleep(0.01)
|
|
927
|
+
call_order.append(f"{order}_end")
|
|
928
|
+
return {}
|
|
929
|
+
|
|
930
|
+
semaphore = asyncio.Semaphore(1)
|
|
931
|
+
wrapped = _rate_limited(tracking_fetcher, semaphore)
|
|
932
|
+
|
|
933
|
+
async def runner() -> None:
|
|
934
|
+
await asyncio.gather(
|
|
935
|
+
wrapped(order="first"),
|
|
936
|
+
wrapped(order="second"),
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
asyncio.run(runner())
|
|
940
|
+
|
|
941
|
+
assert call_order.index("first_end") < call_order.index("second_start")
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def test_rate_limited_raises_timeout_error() -> None:
|
|
945
|
+
"""
|
|
946
|
+
ARRANGE: slow function with short timeout
|
|
947
|
+
ACT: call the wrapped function
|
|
948
|
+
ASSERT: raises TimeoutError
|
|
949
|
+
"""
|
|
950
|
+
|
|
951
|
+
async def slow_fetcher(**kwargs: object) -> dict[str, object]:
|
|
952
|
+
await asyncio.sleep(0.1)
|
|
953
|
+
return {}
|
|
954
|
+
|
|
955
|
+
semaphore = asyncio.Semaphore(10)
|
|
956
|
+
wrapped = _rate_limited(slow_fetcher, semaphore, timeout=0.01)
|
|
957
|
+
|
|
958
|
+
with pytest.raises(TimeoutError):
|
|
959
|
+
asyncio.run(wrapped())
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
def test_open_feeds_yields_single_feed_for_single_spec() -> None:
|
|
963
|
+
"""
|
|
964
|
+
ARRANGE: single FeedSpec with stub factory
|
|
965
|
+
ACT: open feeds using _open_feeds
|
|
966
|
+
ASSERT: yields tuple containing one feed
|
|
967
|
+
"""
|
|
968
|
+
specs = (FeedSpec(stub_feed_factory({}), GoodFeedData, 5),)
|
|
969
|
+
|
|
970
|
+
async def runner() -> int:
|
|
971
|
+
async with _open_feeds(specs) as feeds:
|
|
972
|
+
return len(feeds)
|
|
973
|
+
|
|
974
|
+
actual = asyncio.run(runner())
|
|
975
|
+
|
|
976
|
+
assert actual == 1
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def test_open_feeds_fetch_returns_stub_response() -> None:
|
|
980
|
+
"""
|
|
981
|
+
ARRANGE: FeedSpec with stub factory returning specific data
|
|
982
|
+
ACT: open feeds and call fetch
|
|
983
|
+
ASSERT: returns the stub response
|
|
984
|
+
"""
|
|
985
|
+
specs = (FeedSpec(stub_feed_factory({"key": "value"}), GoodFeedData, 5),)
|
|
986
|
+
|
|
987
|
+
async def runner() -> dict[str, object]:
|
|
988
|
+
async with _open_feeds(specs) as feeds:
|
|
989
|
+
return await feeds[0].fetch(symbol="X", name="X")
|
|
990
|
+
|
|
991
|
+
actual = asyncio.run(runner())
|
|
992
|
+
|
|
993
|
+
assert actual == {"key": "value"}
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def test_init_feed_returns_functional_enrichment_feed() -> None:
|
|
997
|
+
"""
|
|
998
|
+
ARRANGE: FeedSpec with stub factory
|
|
999
|
+
ACT: initialise feed using _init_feed
|
|
1000
|
+
ASSERT: fetch returns the stub response
|
|
1001
|
+
"""
|
|
1002
|
+
spec = FeedSpec(stub_feed_factory({"result": "ok"}), GoodFeedData, 5)
|
|
1003
|
+
|
|
1004
|
+
async def runner() -> dict[str, object]:
|
|
1005
|
+
async with AsyncExitStack() as stack:
|
|
1006
|
+
feed = await _init_feed(spec, stack)
|
|
1007
|
+
return await feed.fetch(symbol="X", name="X")
|
|
1008
|
+
|
|
1009
|
+
actual = asyncio.run(runner())
|
|
1010
|
+
|
|
1011
|
+
assert actual == {"result": "ok"}
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
def test_enrich_yields_merged_equity() -> None:
|
|
1015
|
+
"""
|
|
1016
|
+
ARRANGE: async stream with one equity group
|
|
1017
|
+
ACT: run enrich() over that stream
|
|
1018
|
+
ASSERT: yields the merged equity (enrichment failures are handled gracefully)
|
|
1019
|
+
"""
|
|
1020
|
+
equity = RawEquity(
|
|
1021
|
+
name="TEST",
|
|
1022
|
+
symbol="TEST",
|
|
1023
|
+
share_class_figi="BBG000TEST01",
|
|
1024
|
+
mics=["XNAS"],
|
|
1025
|
+
currency="USD",
|
|
1026
|
+
last_price=Decimal("100"),
|
|
1027
|
+
market_cap=Decimal("1000000"),
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
async def source() -> AsyncIterable[list[RawEquity]]:
|
|
1031
|
+
yield [equity]
|
|
1032
|
+
|
|
1033
|
+
async def runner() -> list[RawEquity]:
|
|
1034
|
+
return [e async for e in enrich(source())]
|
|
1035
|
+
|
|
1036
|
+
actual = asyncio.run(runner())
|
|
1037
|
+
|
|
1038
|
+
assert actual[0].symbol == "TEST"
|
|
File without changes
|
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/.github/workflows/publish-build-release.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/lseg_equities.json
RENAMED
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/sec_equity.json
RENAMED
|
File without changes
|
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/tiprank_equity.json
RENAMED
|
File without changes
|
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/documentation/reference_data/xetra_equities.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{equity_aggregator-0.1.4 → equity_aggregator-0.1.5}/src/equity_aggregator/adapters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|