equity-aggregator 0.1.5__tar.gz → 0.1.7__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.5 → equity_aggregator-0.1.7}/.github/workflows/publish-build-release.yml +5 -1
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/PKG-INFO +1 -1
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/pyproject.toml +1 -1
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +34 -22
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +89 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/discovery_feeds/sec/test_sec.py +76 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +87 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +90 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +79 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/enrichment_feeds/gleif/test_gleif.py +81 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +87 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/reference_lookup/__init__.py +1 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/reference_lookup/exchange_rate_api/__init__.py +1 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/reference_lookup/exchange_rate_api/test_exchange_rate_api.py +89 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/reference_lookup/openfigi/__init__.py +1 -0
- equity_aggregator-0.1.7/tests/live/adapters/data_sources/reference_lookup/openfigi/test_openfigi.py +76 -0
- equity_aggregator-0.1.7/tests/unit/adapters/data_sources/enrichment_feeds/gleif/__init__.py +1 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/uv.lock +1 -1
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -299
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -189
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -260
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -332
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -259
- equity_aggregator-0.1.5/tests/live/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -247
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.dockerignore +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.env_example +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.github/workflows/publish-pypi.yml +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.github/workflows/validate-push.yml +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.gitignore +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.python-version +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/Dockerfile +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/LICENCE.txt +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/README.md +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/docker-compose.yml +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/intrinio_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/lseg_equities.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/sec_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/stock_analysis_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/tiprank_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/tradingview_equities.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/xetra_equities.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/yfinance_quote_summary_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/documentation/reference_data/yfinance_search_equity.json +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/examples/data_integrity_analysis.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/README.md +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/_utils/_client.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/_utils/_record_types.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/intrinio.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/lseg.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/sec.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/stock_analysis.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/tradingview.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/xetra.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/api.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/download.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/gleif.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/fuzzy.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/json.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/quote_summary.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/search.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/auth.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/ranking.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/transport.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/yfinance.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/reference_lookup/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/reference_lookup/exchange_rate_api.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/adapters/data_sources/reference_lookup/openfigi.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/dispatcher.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/main.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/cli/signals.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/_utils/_load_converter.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/_utils/_merge.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/_utils/_merge_config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/_utils/_strategies.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/resolve.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/runner.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/seed.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/canonicalise.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/convert.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/enrich.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/group.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/identify.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/pipeline/transforms/parse.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/retrieval/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/domain/retrieval/retrieval.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/logging_config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/canonical.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/feed_validators.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/gleif_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/intrinio_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/lseg_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/sec_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/stock_analysis_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/tradingview_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/xetra_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/feeds/yfinance_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/raw.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/types.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/schemas/validators.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/_utils.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/cache.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/data_store.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/export.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/src/equity_aggregator/storage/metadata.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/README.md +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/conftest.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5/tests/unit → equity_aggregator-0.1.7/tests/live}/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/live/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/_utils/test_client.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/_helpers.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_api.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_download.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_gleif.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_helpers.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_backoff.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_fuzzy.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_json.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_quote_summary.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_search.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_auth.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_ranking.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_session.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_transport.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/reference_lookup/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/reference_lookup/test_exchange_rate_api.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/adapters/data_sources/reference_lookup/test_openfigi.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/cli/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/cli/test_config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/cli/test_dispatcher.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/cli/test_main.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/cli/test_parser.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/_utils/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/_utils/test__merge.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/_utils/test__merge_config.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/_utils/test__strategies.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/_utils/test_load_converter.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/test_resolve.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_canonicalise.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_convert.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_enrich.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_group.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_identify.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/pipeline/transforms/test_parse.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/domain/retrieval/test_retrieval.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_feed_validators.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_gleif_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_intrinio_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_lseg_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_sec_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_stock_analysis_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_tradingview_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_xetra_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/feeds/test_yfinance_feed_data.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/test_canonical.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/test_raw.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/test_types.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/schemas/test_validators.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/__init__.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/test__utils.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/test_cache.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/test_data_store.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/test_export.py +0 -0
- {equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/tests/unit/storage/test_metadata.py +0 -0
{equity_aggregator-0.1.5 → equity_aggregator-0.1.7}/.github/workflows/publish-build-release.yml
RENAMED
|
@@ -40,7 +40,11 @@ jobs:
|
|
|
40
40
|
|
|
41
41
|
- name: Create .env file
|
|
42
42
|
run: |
|
|
43
|
-
|
|
43
|
+
cat <<EOF > .env
|
|
44
|
+
EXCHANGE_RATE_API_KEY=${{ secrets.EXCHANGE_RATE_API_KEY }}
|
|
45
|
+
INTRINIO_API_KEY=${{ secrets.INTRINIO_API_KEY }}
|
|
46
|
+
OPENFIGI_API_KEY=${{ secrets.OPENFIGI_API_KEY }}
|
|
47
|
+
EOF
|
|
44
48
|
chmod 600 .env
|
|
45
49
|
test -s .env || { echo "::error ::empty .env"; exit 1; }
|
|
46
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: equity-aggregator
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -29,38 +29,28 @@ def _get_api_key() -> str:
|
|
|
29
29
|
return os.getenv("INTRINIO_API_KEY", "")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
@pytest.fixture(scope="module")
|
|
33
|
+
def intrinio_companies_response() -> httpx.Response:
|
|
33
34
|
"""
|
|
34
|
-
|
|
35
|
-
ACT: send GET request
|
|
36
|
-
ASSERT: returns HTTP 200
|
|
35
|
+
Fetch Intrinio companies API response once and share across all tests.
|
|
37
36
|
"""
|
|
38
37
|
params = {"api_key": _get_api_key(), "page_size": "10"}
|
|
38
|
+
with httpx.Client() as client:
|
|
39
|
+
return client.get(_INTRINIO_COMPANIES_URL, params=params)
|
|
39
40
|
|
|
40
|
-
async with httpx.AsyncClient() as client:
|
|
41
|
-
response = await client.get(_INTRINIO_COMPANIES_URL, params=params)
|
|
42
|
-
|
|
43
|
-
assert response.status_code == _HTTP_OK
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
@pytest.fixture(scope="module")
|
|
43
|
+
def intrinio_security_record(intrinio_companies_response: httpx.Response) -> dict:
|
|
47
44
|
"""
|
|
48
|
-
|
|
49
|
-
ACT: validate with IntrinioFeedData schema
|
|
50
|
-
ASSERT: no ValidationError raised
|
|
45
|
+
Fetch a security record from Intrinio for schema validation.
|
|
51
46
|
"""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
async with httpx.AsyncClient() as client:
|
|
55
|
-
response = await client.get(_INTRINIO_COMPANIES_URL, params=params)
|
|
56
|
-
|
|
57
|
-
records, _ = parse_companies_response(response.json())
|
|
47
|
+
records, _ = parse_companies_response(intrinio_companies_response.json())
|
|
58
48
|
first_company = records[0]
|
|
59
49
|
ticker = first_company["company_ticker"]
|
|
60
50
|
securities_url = f"{_INTRINIO_COMPANIES_URL}/{ticker}/securities"
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
securities_response =
|
|
52
|
+
with httpx.Client() as client:
|
|
53
|
+
securities_response = client.get(
|
|
64
54
|
securities_url,
|
|
65
55
|
params={"api_key": _get_api_key()},
|
|
66
56
|
)
|
|
@@ -70,4 +60,26 @@ async def test_intrinio_record_validates_against_schema() -> None:
|
|
|
70
60
|
if not security_records:
|
|
71
61
|
pytest.skip("No securities found for first company")
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
return security_records[0]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_intrinio_endpoint_returns_ok(
|
|
67
|
+
intrinio_companies_response: httpx.Response,
|
|
68
|
+
) -> None:
|
|
69
|
+
"""
|
|
70
|
+
ARRANGE: live Intrinio companies API endpoint
|
|
71
|
+
ACT: send GET request
|
|
72
|
+
ASSERT: returns HTTP 200
|
|
73
|
+
"""
|
|
74
|
+
assert intrinio_companies_response.status_code == _HTTP_OK
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_intrinio_record_validates_against_schema(
|
|
78
|
+
intrinio_security_record: dict,
|
|
79
|
+
) -> None:
|
|
80
|
+
"""
|
|
81
|
+
ARRANGE: security record from live Intrinio API response
|
|
82
|
+
ACT: validate with IntrinioFeedData schema
|
|
83
|
+
ASSERT: no ValidationError raised
|
|
84
|
+
"""
|
|
85
|
+
assert IntrinioFeedData.model_validate(intrinio_security_record)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# lseg/test_lseg.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.lseg._utils import (
|
|
8
|
+
parse_response,
|
|
9
|
+
)
|
|
10
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.lseg.lseg import (
|
|
11
|
+
_HEADERS,
|
|
12
|
+
_LSEG_BASE_PARAMS,
|
|
13
|
+
_LSEG_PATH,
|
|
14
|
+
_LSEG_SEARCH_URL,
|
|
15
|
+
)
|
|
16
|
+
from equity_aggregator.schemas.feeds import LsegFeedData
|
|
17
|
+
|
|
18
|
+
pytestmark = pytest.mark.live
|
|
19
|
+
|
|
20
|
+
_HTTP_OK = 200
|
|
21
|
+
_TIMEOUT = httpx.Timeout(connect=10.0, read=60.0, write=10.0, pool=None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture(scope="module")
|
|
25
|
+
def lseg_response() -> httpx.Response:
|
|
26
|
+
"""
|
|
27
|
+
Fetch LSEG API response once and share across all tests in this module.
|
|
28
|
+
"""
|
|
29
|
+
with httpx.Client(headers=_HEADERS, timeout=_TIMEOUT) as client:
|
|
30
|
+
return client.get(
|
|
31
|
+
_LSEG_SEARCH_URL,
|
|
32
|
+
params={
|
|
33
|
+
"path": _LSEG_PATH,
|
|
34
|
+
"parameters": f"{_LSEG_BASE_PARAMS}&page=0",
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_lseg_endpoint_returns_ok(lseg_response: httpx.Response) -> None:
|
|
40
|
+
"""
|
|
41
|
+
ARRANGE: live LSEG API endpoint
|
|
42
|
+
ACT: send GET request
|
|
43
|
+
ASSERT: returns HTTP 200
|
|
44
|
+
"""
|
|
45
|
+
assert lseg_response.status_code == _HTTP_OK
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_lseg_response_contains_components_key(lseg_response: httpx.Response) -> None:
|
|
49
|
+
"""
|
|
50
|
+
ARRANGE: live LSEG API response
|
|
51
|
+
ACT: parse JSON payload
|
|
52
|
+
ASSERT: contains 'components' key
|
|
53
|
+
"""
|
|
54
|
+
payload = lseg_response.json()
|
|
55
|
+
|
|
56
|
+
assert "components" in payload
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_lseg_response_parses_to_non_empty_records(
|
|
60
|
+
lseg_response: httpx.Response,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""
|
|
63
|
+
ARRANGE: live LSEG API response
|
|
64
|
+
ACT: parse response using parse_response
|
|
65
|
+
ASSERT: returns non-empty list of records
|
|
66
|
+
"""
|
|
67
|
+
payload = lseg_response.json()
|
|
68
|
+
records, _ = parse_response(payload)
|
|
69
|
+
|
|
70
|
+
assert isinstance(records, list) and len(records) > 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_lseg_record_validates_against_schema(lseg_response: httpx.Response) -> None:
|
|
74
|
+
"""
|
|
75
|
+
ARRANGE: first record from live LSEG API response
|
|
76
|
+
ACT: validate with LsegFeedData schema
|
|
77
|
+
ASSERT: no ValidationError raised
|
|
78
|
+
"""
|
|
79
|
+
payload = lseg_response.json()
|
|
80
|
+
records, _ = parse_response(payload)
|
|
81
|
+
first_record = records[0]
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
LsegFeedData.model_validate(first_record)
|
|
85
|
+
validated = True
|
|
86
|
+
except ValidationError:
|
|
87
|
+
validated = False
|
|
88
|
+
|
|
89
|
+
assert validated
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# sec/test_sec.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.sec.sec import (
|
|
8
|
+
_HEADERS,
|
|
9
|
+
_SEC_SEARCH_URL,
|
|
10
|
+
_parse_row,
|
|
11
|
+
)
|
|
12
|
+
from equity_aggregator.schemas.feeds import SecFeedData
|
|
13
|
+
|
|
14
|
+
pytestmark = pytest.mark.live
|
|
15
|
+
|
|
16
|
+
_HTTP_OK = 200
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture(scope="module")
|
|
20
|
+
def sec_response() -> httpx.Response:
|
|
21
|
+
"""
|
|
22
|
+
Fetch SEC API response once and share across all tests in this module.
|
|
23
|
+
"""
|
|
24
|
+
with httpx.Client(headers=_HEADERS) as client:
|
|
25
|
+
return client.get(_SEC_SEARCH_URL)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_sec_endpoint_returns_ok(sec_response: httpx.Response) -> None:
|
|
29
|
+
"""
|
|
30
|
+
ARRANGE: live SEC API endpoint
|
|
31
|
+
ACT: send GET request
|
|
32
|
+
ASSERT: returns HTTP 200
|
|
33
|
+
"""
|
|
34
|
+
assert sec_response.status_code == _HTTP_OK
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_sec_response_contains_data_key(sec_response: httpx.Response) -> None:
|
|
38
|
+
"""
|
|
39
|
+
ARRANGE: live SEC API response
|
|
40
|
+
ACT: parse JSON payload
|
|
41
|
+
ASSERT: contains 'data' key
|
|
42
|
+
"""
|
|
43
|
+
payload = sec_response.json()
|
|
44
|
+
|
|
45
|
+
assert "data" in payload
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_sec_response_data_is_non_empty_list(sec_response: httpx.Response) -> None:
|
|
49
|
+
"""
|
|
50
|
+
ARRANGE: live SEC API response
|
|
51
|
+
ACT: extract 'data' field
|
|
52
|
+
ASSERT: is a non-empty list
|
|
53
|
+
"""
|
|
54
|
+
payload = sec_response.json()
|
|
55
|
+
data = payload.get("data", [])
|
|
56
|
+
|
|
57
|
+
assert isinstance(data, list) and len(data) > 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_sec_record_validates_against_schema(sec_response: httpx.Response) -> None:
|
|
61
|
+
"""
|
|
62
|
+
ARRANGE: first row from live SEC API response, parsed into record
|
|
63
|
+
ACT: validate with SecFeedData schema
|
|
64
|
+
ASSERT: no ValidationError raised
|
|
65
|
+
"""
|
|
66
|
+
payload = sec_response.json()
|
|
67
|
+
first_row = payload["data"][0]
|
|
68
|
+
record = _parse_row(first_row)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
SecFeedData.model_validate(record)
|
|
72
|
+
validated = True
|
|
73
|
+
except ValidationError:
|
|
74
|
+
validated = False
|
|
75
|
+
|
|
76
|
+
assert validated
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# stock_analysis/test_stock_analysis.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.stock_analysis import (
|
|
8
|
+
stock_analysis,
|
|
9
|
+
)
|
|
10
|
+
from equity_aggregator.schemas.feeds import StockAnalysisFeedData
|
|
11
|
+
|
|
12
|
+
pytestmark = pytest.mark.live
|
|
13
|
+
|
|
14
|
+
_HTTP_OK = 200
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture(scope="module")
|
|
18
|
+
def stock_analysis_response() -> httpx.Response:
|
|
19
|
+
"""
|
|
20
|
+
Fetch Stock Analysis API response once and share across all tests in this module.
|
|
21
|
+
"""
|
|
22
|
+
with httpx.Client() as client:
|
|
23
|
+
return client.get(
|
|
24
|
+
stock_analysis._STOCK_ANALYSIS_SEARCH_URL,
|
|
25
|
+
params=stock_analysis._PARAMS,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_stock_analysis_endpoint_returns_ok(
|
|
30
|
+
stock_analysis_response: httpx.Response,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
ARRANGE: live Stock Analysis screener API endpoint
|
|
34
|
+
ACT: send GET request
|
|
35
|
+
ASSERT: returns HTTP 200
|
|
36
|
+
"""
|
|
37
|
+
assert stock_analysis_response.status_code == _HTTP_OK
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_stock_analysis_response_contains_data_key(
|
|
41
|
+
stock_analysis_response: httpx.Response,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""
|
|
44
|
+
ARRANGE: live Stock Analysis API response
|
|
45
|
+
ACT: parse JSON payload
|
|
46
|
+
ASSERT: contains 'data' key
|
|
47
|
+
"""
|
|
48
|
+
payload = stock_analysis_response.json()
|
|
49
|
+
|
|
50
|
+
assert "data" in payload
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_stock_analysis_response_data_is_non_empty_list(
|
|
54
|
+
stock_analysis_response: httpx.Response,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""
|
|
57
|
+
ARRANGE: live Stock Analysis API response
|
|
58
|
+
ACT: extract nested data array
|
|
59
|
+
ASSERT: is a non-empty list
|
|
60
|
+
"""
|
|
61
|
+
payload = stock_analysis_response.json()
|
|
62
|
+
data_wrapper = payload.get("data", {})
|
|
63
|
+
records = data_wrapper.get("data", [])
|
|
64
|
+
|
|
65
|
+
assert isinstance(records, list) and len(records) > 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_stock_analysis_record_validates_against_schema(
|
|
69
|
+
stock_analysis_response: httpx.Response,
|
|
70
|
+
) -> None:
|
|
71
|
+
"""
|
|
72
|
+
ARRANGE: first record from live Stock Analysis API response
|
|
73
|
+
ACT: validate with StockAnalysisFeedData schema
|
|
74
|
+
ASSERT: no ValidationError raised
|
|
75
|
+
"""
|
|
76
|
+
payload = stock_analysis_response.json()
|
|
77
|
+
data_wrapper = payload.get("data", {})
|
|
78
|
+
records = data_wrapper.get("data", [])
|
|
79
|
+
first_record = records[0]
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
StockAnalysisFeedData.model_validate(first_record)
|
|
83
|
+
validated = True
|
|
84
|
+
except ValidationError:
|
|
85
|
+
validated = False
|
|
86
|
+
|
|
87
|
+
assert validated
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# tradingview/test_tradingview.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.tradingview import (
|
|
8
|
+
tradingview,
|
|
9
|
+
)
|
|
10
|
+
from equity_aggregator.schemas.feeds import TradingViewFeedData
|
|
11
|
+
|
|
12
|
+
pytestmark = pytest.mark.live
|
|
13
|
+
|
|
14
|
+
_HTTP_OK = 200
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _build_request_body(start: int = 0, end: int = tradingview._PAGE_SIZE) -> dict:
|
|
18
|
+
"""
|
|
19
|
+
Build request body with pagination range.
|
|
20
|
+
"""
|
|
21
|
+
return {**tradingview._REQUEST_BODY_TEMPLATE, "range": [start, end]}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture(scope="module")
|
|
25
|
+
def tradingview_response() -> httpx.Response:
|
|
26
|
+
"""
|
|
27
|
+
Fetch TradingView API response once and share across all tests in this module.
|
|
28
|
+
"""
|
|
29
|
+
with httpx.Client() as client:
|
|
30
|
+
return client.post(
|
|
31
|
+
tradingview._TRADINGVIEW_SCAN_URL,
|
|
32
|
+
json=_build_request_body(),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_tradingview_endpoint_returns_ok(tradingview_response: httpx.Response) -> None:
|
|
37
|
+
"""
|
|
38
|
+
ARRANGE: live TradingView scanner API endpoint
|
|
39
|
+
ACT: send POST request
|
|
40
|
+
ASSERT: returns HTTP 200
|
|
41
|
+
"""
|
|
42
|
+
assert tradingview_response.status_code == _HTTP_OK
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_tradingview_response_contains_data_key(
|
|
46
|
+
tradingview_response: httpx.Response,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""
|
|
49
|
+
ARRANGE: live TradingView API response
|
|
50
|
+
ACT: parse JSON payload
|
|
51
|
+
ASSERT: contains 'data' key
|
|
52
|
+
"""
|
|
53
|
+
payload = tradingview_response.json()
|
|
54
|
+
|
|
55
|
+
assert "data" in payload
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_tradingview_response_data_is_non_empty_list(
|
|
59
|
+
tradingview_response: httpx.Response,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""
|
|
62
|
+
ARRANGE: live TradingView API response
|
|
63
|
+
ACT: extract 'data' field
|
|
64
|
+
ASSERT: is a non-empty list
|
|
65
|
+
"""
|
|
66
|
+
payload = tradingview_response.json()
|
|
67
|
+
data = payload.get("data", [])
|
|
68
|
+
|
|
69
|
+
assert isinstance(data, list) and len(data) > 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_tradingview_record_validates_against_schema(
|
|
73
|
+
tradingview_response: httpx.Response,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""
|
|
76
|
+
ARRANGE: first parsed record from live TradingView API response
|
|
77
|
+
ACT: validate with TradingViewFeedData schema
|
|
78
|
+
ASSERT: no ValidationError raised
|
|
79
|
+
"""
|
|
80
|
+
payload = tradingview_response.json()
|
|
81
|
+
first_row = payload["data"][0]
|
|
82
|
+
record = tradingview._parse_row(first_row)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
TradingViewFeedData.model_validate(record)
|
|
86
|
+
validated = True
|
|
87
|
+
except ValidationError:
|
|
88
|
+
validated = False
|
|
89
|
+
|
|
90
|
+
assert validated
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# xetra/test_xetra.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from equity_aggregator.adapters.data_sources.discovery_feeds.xetra.xetra import (
|
|
8
|
+
_HEADERS,
|
|
9
|
+
_XETRA_SEARCH_URL,
|
|
10
|
+
_build_payload,
|
|
11
|
+
_extract_records,
|
|
12
|
+
)
|
|
13
|
+
from equity_aggregator.schemas.feeds import XetraFeedData
|
|
14
|
+
|
|
15
|
+
pytestmark = pytest.mark.live
|
|
16
|
+
|
|
17
|
+
_HTTP_OK = 200
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture(scope="module")
|
|
21
|
+
def xetra_response() -> httpx.Response:
|
|
22
|
+
"""
|
|
23
|
+
Fetch XETRA API response once and share across all tests in this module.
|
|
24
|
+
"""
|
|
25
|
+
with httpx.Client(headers=_HEADERS) as client:
|
|
26
|
+
return client.post(_XETRA_SEARCH_URL, json=_build_payload(offset=0))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_xetra_endpoint_returns_ok(xetra_response: httpx.Response) -> None:
|
|
30
|
+
"""
|
|
31
|
+
ARRANGE: live XETRA API endpoint
|
|
32
|
+
ACT: send POST request
|
|
33
|
+
ASSERT: returns HTTP 200
|
|
34
|
+
"""
|
|
35
|
+
assert xetra_response.status_code == _HTTP_OK
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_xetra_response_contains_data_key(xetra_response: httpx.Response) -> None:
|
|
39
|
+
"""
|
|
40
|
+
ARRANGE: live XETRA API response
|
|
41
|
+
ACT: parse JSON payload
|
|
42
|
+
ASSERT: contains 'data' key
|
|
43
|
+
"""
|
|
44
|
+
payload = xetra_response.json()
|
|
45
|
+
|
|
46
|
+
assert "data" in payload
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_xetra_response_data_is_non_empty_list(xetra_response: httpx.Response) -> None:
|
|
50
|
+
"""
|
|
51
|
+
ARRANGE: live XETRA API response
|
|
52
|
+
ACT: extract 'data' field
|
|
53
|
+
ASSERT: is a non-empty list
|
|
54
|
+
"""
|
|
55
|
+
payload = xetra_response.json()
|
|
56
|
+
data = payload.get("data", [])
|
|
57
|
+
|
|
58
|
+
assert isinstance(data, list) and len(data) > 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_xetra_extracted_record_validates_against_schema(
|
|
62
|
+
xetra_response: httpx.Response,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""
|
|
65
|
+
ARRANGE: first extracted record from live XETRA API response
|
|
66
|
+
ACT: validate with XetraFeedData schema
|
|
67
|
+
ASSERT: no ValidationError raised
|
|
68
|
+
"""
|
|
69
|
+
payload = xetra_response.json()
|
|
70
|
+
records = _extract_records(payload)
|
|
71
|
+
first_record = records[0]
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
XetraFeedData.model_validate(first_record)
|
|
75
|
+
validated = True
|
|
76
|
+
except ValidationError:
|
|
77
|
+
validated = False
|
|
78
|
+
|
|
79
|
+
assert validated
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# gleif/test_gleif.py
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from equity_aggregator.adapters.data_sources.enrichment_feeds.gleif.api import (
|
|
7
|
+
GLEIF_ISIN_LEI_URL,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
pytestmark = pytest.mark.live
|
|
11
|
+
|
|
12
|
+
_HTTP_OK = 200
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture(scope="module")
|
|
16
|
+
def gleif_response() -> httpx.Response:
|
|
17
|
+
"""
|
|
18
|
+
Fetch GLEIF API response once and share across all tests in this module.
|
|
19
|
+
"""
|
|
20
|
+
with httpx.Client() as client:
|
|
21
|
+
return client.get(GLEIF_ISIN_LEI_URL)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_gleif_api_endpoint_returns_ok(gleif_response: httpx.Response) -> None:
|
|
25
|
+
"""
|
|
26
|
+
ARRANGE: live GLEIF ISIN-LEI mapping API endpoint
|
|
27
|
+
ACT: send GET request
|
|
28
|
+
ASSERT: returns HTTP 200
|
|
29
|
+
"""
|
|
30
|
+
assert gleif_response.status_code == _HTTP_OK
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_gleif_api_response_contains_data_key(gleif_response: httpx.Response) -> None:
|
|
34
|
+
"""
|
|
35
|
+
ARRANGE: live GLEIF API response
|
|
36
|
+
ACT: parse JSON payload
|
|
37
|
+
ASSERT: contains 'data' key
|
|
38
|
+
"""
|
|
39
|
+
payload = gleif_response.json()
|
|
40
|
+
|
|
41
|
+
assert "data" in payload
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_gleif_api_response_data_contains_attributes(
|
|
45
|
+
gleif_response: httpx.Response,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""
|
|
48
|
+
ARRANGE: live GLEIF API response
|
|
49
|
+
ACT: extract 'data' field
|
|
50
|
+
ASSERT: contains 'attributes' key
|
|
51
|
+
"""
|
|
52
|
+
payload = gleif_response.json()
|
|
53
|
+
data = payload.get("data", {})
|
|
54
|
+
|
|
55
|
+
assert "attributes" in data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_gleif_api_response_contains_download_link(
|
|
59
|
+
gleif_response: httpx.Response,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""
|
|
62
|
+
ARRANGE: live GLEIF API response
|
|
63
|
+
ACT: extract attributes from response
|
|
64
|
+
ASSERT: contains 'downloadLink' key
|
|
65
|
+
"""
|
|
66
|
+
payload = gleif_response.json()
|
|
67
|
+
attrs = payload.get("data", {}).get("attributes", {})
|
|
68
|
+
|
|
69
|
+
assert "downloadLink" in attrs
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_gleif_api_download_link_is_valid_url(gleif_response: httpx.Response) -> None:
|
|
73
|
+
"""
|
|
74
|
+
ARRANGE: live GLEIF API response
|
|
75
|
+
ACT: extract download link
|
|
76
|
+
ASSERT: is a non-empty string starting with https
|
|
77
|
+
"""
|
|
78
|
+
payload = gleif_response.json()
|
|
79
|
+
download_link = payload.get("data", {}).get("attributes", {}).get("downloadLink")
|
|
80
|
+
|
|
81
|
+
assert isinstance(download_link, str) and download_link.startswith("https://")
|