ml4t-data 0.2.0__tar.gz → 0.2.1__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.
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/PKG-INFO +2 -2
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/README.md +1 -1
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/pyproject.toml +2 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/_version.py +2 -2
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/config/models.py +2 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/macro/downloader.py +10 -1
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/AGENTS.md +1 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/__init__.py +12 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/base.py +122 -7
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/fama_french.py +6 -5
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/finnhub.py +18 -6
- ml4t_data-0.2.1/src/ml4t/data/providers/forecastex.py +438 -0
- ml4t_data-0.2.1/src/ml4t/data/providers/kalshi.py +1634 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/polygon.py +22 -1
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/polymarket.py +740 -6
- ml4t_data-0.2.1/src/ml4t/data/providers/polymarket_us.py +431 -0
- ml4t_data-0.2.1/src/ml4t/data/providers/prediction_markets.py +136 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/registry.py +16 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/update_manager.py +3 -2
- ml4t_data-0.2.1/tests/integration/test_forecastex.py +34 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_kalshi.py +66 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_polymarket.py +57 -0
- ml4t_data-0.2.1/tests/integration/test_polymarket_us.py +35 -0
- ml4t_data-0.2.1/tests/providers/conftest.py +43 -0
- ml4t_data-0.2.1/tests/providers/fixtures/polymarket/clob_prices_history.json +106 -0
- ml4t_data-0.2.1/tests/providers/fixtures/polymarket/data_api_trades.json +413 -0
- ml4t_data-0.2.1/tests/providers/fixtures/polymarket/gamma_keyset_active.json +198 -0
- ml4t_data-0.2.1/tests/providers/fixtures/polymarket/gamma_keyset_closed.json +1224 -0
- ml4t_data-0.2.1/tests/providers/test_forecastex_provider.py +295 -0
- ml4t_data-0.2.1/tests/providers/test_kalshi_history.py +819 -0
- ml4t_data-0.2.1/tests/providers/test_polymarket_history.py +408 -0
- ml4t_data-0.2.1/tests/providers/test_polymarket_us_provider.py +323 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_fama_french_provider.py +23 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_finnhub_provider.py +15 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_macro_downloader.py +2 -1
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_polygon_provider.py +24 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_release_pipeline.py +107 -1
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_release_workflows.py +4 -0
- ml4t_data-0.2.1/tests/test_source_reference_contract.py +99 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_update_manager.py +18 -0
- ml4t_data-0.2.0/src/ml4t/data/providers/kalshi.py +0 -832
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/.gitignore +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/LICENSE +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/scripts/verify_distribution.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/scripts/verify_provider_contract_runs.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/AGENTS.md +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/adjustments/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/adjustments/core.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/anomaly/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/anomaly/base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/anomaly/config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/anomaly/detectors.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/anomaly/manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/asset_class.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/contracts.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/data/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/data/crypto_top100.json +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/data/forex_majors.json +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/data/nasdaq100.json +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/data/sp500.json +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/schemas.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/assets/validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/calendar/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/calendar/crypto.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/batch.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/core.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/cot.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/futures.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli/utils.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cli_interface.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/config/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/config/_serialization.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/config/loader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/config/validator.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/core/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/core/config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/core/exceptions.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/core/models.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/core/schemas.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cot/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cot/fetcher.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/cot/workflow.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/crypto/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/crypto/downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/data_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/etfs/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/etfs/downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/formats/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/formats/base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/formats/csv.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/formats/excel.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/formats/json.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/export/manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/AGENTS.md +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/adjustment.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/book_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/continuous.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/continuous_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/databento_parser.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/definitions.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/individual_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/parser.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/roll.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/futures/schema.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/macro/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/async_batch.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/batch_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/bulk_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/config_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/fetch_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/metadata_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/provider_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/managers/storage_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/provider_updater.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/alpaca.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/aqr.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/async_base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/binance.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/binance_public.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/coingecko.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/cryptocompare.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/databento.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/eodhd.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/fred.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/fundamentals.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/fxmacrodata.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/learned_synthetic.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/async_session.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/circuit_breaker.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/retry.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/session.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mixins/validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/mock.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/nasdaq_itch.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/oanda.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/okx.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/protocols.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/synthetic.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/tiingo.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/twelve_data.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/wiki_prices.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/providers/yahoo.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/py.typed +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/security/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/security/path_validator.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/sessions/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/sessions/assigner.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/sessions/completer.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/AGENTS.md +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/async_base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/backend.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/chunked.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/data_profile.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/flat.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/hive.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/keys.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/legacy_migration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/metadata_tracker.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/migration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/storage/protocols.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/synthetic/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/synthetic/ohlcv_utils.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/synthetic/registry.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/universe.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/async_rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/conversion.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/format.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/gap_optimizer.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/gaps.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/global_rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/locking.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/utils/retry.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/cross_validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/ohlcv.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/report.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/src/ml4t/data/validation/rules.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/anomaly/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/anomaly/test_base.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/anomaly/test_config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/anomaly/test_detectors.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/anomaly/test_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/assets/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/assets/test_asset_class.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/assets/test_contracts.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/assets/test_schemas.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/assets/test_validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/conftest.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/core/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/core/test_schemas.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/cot/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/cot/test_fetcher.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/cot/test_workflow.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_adjustment.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_book_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_continuous.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_databento_parser.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_parser.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/futures/test_roll.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/conftest.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_alpaca.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_binance_public.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_coingecko.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_cryptocompare.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_eodhd.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_factor_providers.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_finnhub.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_fred.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_fxmacrodata.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_oanda.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_polygon.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_real_api_integration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_release_provider_contracts.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_tiingo.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_twelve_data.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_wiki_prices.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/integration/test_yahoo.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/providers/test_nasdaq_itch.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/storage/test_data_profile.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/storage/test_keys.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/storage/test_legacy_migration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/storage/test_metadata_tracker.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/storage/test_migration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/synthetic/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/synthetic/test_learned_synthetic.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/synthetic/test_registry.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_adjustments_core.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_alpaca_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_anomaly/__init__.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_anomaly/test_detectors.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_anomaly/test_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_aqr_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_aqr_provider_edge_cases.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_async_providers.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_async_providers_extended.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_async_rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_base_provider_enhanced.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_batch_load.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_batch_load_integration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_batch_load_performance.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_binance.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_binance_public_edge_cases.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_binance_public_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_chunked_storage.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_cli_batch.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_cli_interface.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_coingecko_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config_basics.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config_integration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config_loader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config_models.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_config_validator.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_configuration_authority.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_conversion.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_core_abstractions.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_core_models.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_cot.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_crypto_assets.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_crypto_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_cryptocompare.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_cryptocompare_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_data_manager.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_data_manager_storage.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_data_manager_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_data_manager_universe.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_databento_acceptance.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_databento_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_distribution_verification.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_eodhd_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_etf_downloader.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_exceptions.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_export.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_fred_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_fundamentals_providers.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_fxmacrodata_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_gap_optimizer.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_gaps.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_global_rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_global_rate_limiting.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_hive_storage.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_import.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_incremental_update_system.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_kalshi_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_locking.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_metadata_tracker.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_mock_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_mock_provider_enhanced.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_multi_asset.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_oanda_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_offline_network_policy.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_okx_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_package_metadata.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_polymarket_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_provider_consistency.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_provider_contract_verification.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_provider_registration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_provider_registry_contract.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_provider_updater.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_rate_limit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_regenerate_combined.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_retry.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_security.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_sessions.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_simple_utils.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_storage.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_storage_backends.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_storage_config_authority.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_storage_migration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_storage_paths.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_synthetic_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_tiingo_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_twelve_data_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_universe.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_utils_gaps.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_validation_report.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_validation_rules.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_validation_simple.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_validation_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_wiki_prices_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_yahoo_empty_download_reporting.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_yahoo_provider.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_yahoo_provider_edge_cases.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/test_yahoo_provider_unit.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/utils/test_format.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/utils/test_format_integration.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/validation/test_cross_validation.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/validation/test_ohlcv_validator.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/validation/test_report.py +0 -0
- {ml4t_data-0.2.0 → ml4t_data-0.2.1}/tests/validation/test_rules.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ml4t-data
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Market data acquisition, storage, and update workflows for machine learning for trading.
|
|
5
5
|
Project-URL: Homepage, https://www.ml4trading.io/
|
|
6
6
|
Project-URL: Documentation, https://www.ml4trading.io/docs/data/
|
|
@@ -179,7 +179,7 @@ uv run pre-commit install
|
|
|
179
179
|
uv run ruff check src/ tests/
|
|
180
180
|
uv run ruff format --check src/ tests/
|
|
181
181
|
uv run ty check
|
|
182
|
-
uv run pytest tests/ -q
|
|
182
|
+
uv run pytest tests/ -q -ra --cov=ml4t.data --cov-report=term-missing
|
|
183
183
|
uv run mkdocs build --strict
|
|
184
184
|
uv build
|
|
185
185
|
```
|
|
@@ -70,7 +70,7 @@ uv run pre-commit install
|
|
|
70
70
|
uv run ruff check src/ tests/
|
|
71
71
|
uv run ruff format --check src/ tests/
|
|
72
72
|
uv run ty check
|
|
73
|
-
uv run pytest tests/ -q
|
|
73
|
+
uv run pytest tests/ -q -ra --cov=ml4t.data --cov-report=term-missing
|
|
74
74
|
uv run mkdocs build --strict
|
|
75
75
|
uv build
|
|
76
76
|
```
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -55,6 +55,8 @@ class ProviderType(StrEnum):
|
|
|
55
55
|
FAMA_FRENCH = "fama_french"
|
|
56
56
|
KALSHI = "kalshi"
|
|
57
57
|
POLYMARKET = "polymarket"
|
|
58
|
+
POLYMARKET_US = "polymarket_us"
|
|
59
|
+
FORECASTEX = "forecastex"
|
|
58
60
|
COINGECKO = "coingecko"
|
|
59
61
|
BINANCE = "binance"
|
|
60
62
|
BINANCE_PUBLIC = "binance_public"
|
|
@@ -30,6 +30,7 @@ from __future__ import annotations
|
|
|
30
30
|
|
|
31
31
|
import os
|
|
32
32
|
from dataclasses import dataclass, field
|
|
33
|
+
from datetime import datetime, timedelta
|
|
33
34
|
from pathlib import Path
|
|
34
35
|
from typing import Any
|
|
35
36
|
|
|
@@ -269,10 +270,18 @@ class MacroDataManager:
|
|
|
269
270
|
|
|
270
271
|
ticker = yf_mapping[series_id]
|
|
271
272
|
try:
|
|
273
|
+
# yfinance treats end as exclusive. Config dates are inclusive.
|
|
274
|
+
end = self.config.end
|
|
275
|
+
try:
|
|
276
|
+
end = (datetime.strptime(end, "%Y-%m-%d") + timedelta(days=1)).strftime(
|
|
277
|
+
"%Y-%m-%d"
|
|
278
|
+
)
|
|
279
|
+
except ValueError:
|
|
280
|
+
pass
|
|
272
281
|
df = yf.download(
|
|
273
282
|
ticker,
|
|
274
283
|
start=self.config.start,
|
|
275
|
-
end=
|
|
284
|
+
end=end,
|
|
276
285
|
progress=False,
|
|
277
286
|
auto_adjust=True,
|
|
278
287
|
threads=False,
|
|
@@ -12,6 +12,7 @@ service limitations are documented in `docs/providers/`.
|
|
|
12
12
|
| `protocols.py` | Structural contracts used by callers and type checking |
|
|
13
13
|
| `registry.py` | Provider discovery, capabilities, aliases, and factory metadata |
|
|
14
14
|
| `mixins/` | Shared HTTP-session and rate-limit behavior |
|
|
15
|
+
| `prediction_markets.py` | Shared resolution-outcome vocabulary and trade schema for event-contract adapters |
|
|
15
16
|
| `synthetic.py`, `mock.py` | Deterministic offline and testing providers |
|
|
16
17
|
| Other top-level modules | One external or packaged data source per adapter |
|
|
17
18
|
|
|
@@ -74,6 +74,16 @@ try:
|
|
|
74
74
|
except ImportError:
|
|
75
75
|
PolymarketProvider = None # type: ignore
|
|
76
76
|
|
|
77
|
+
try:
|
|
78
|
+
from ml4t.data.providers.polymarket_us import PolymarketUSProvider
|
|
79
|
+
except ImportError:
|
|
80
|
+
PolymarketUSProvider = None # type: ignore
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
from ml4t.data.providers.forecastex import ForecastExProvider
|
|
84
|
+
except ImportError:
|
|
85
|
+
ForecastExProvider = None # type: ignore
|
|
86
|
+
|
|
77
87
|
# Crypto providers
|
|
78
88
|
from ml4t.data.providers.coingecko import CoinGeckoProvider
|
|
79
89
|
|
|
@@ -151,6 +161,8 @@ __all__ = [
|
|
|
151
161
|
# Prediction market providers
|
|
152
162
|
"KalshiProvider",
|
|
153
163
|
"PolymarketProvider",
|
|
164
|
+
"PolymarketUSProvider",
|
|
165
|
+
"ForecastExProvider",
|
|
154
166
|
# Crypto providers
|
|
155
167
|
"CoinGeckoProvider",
|
|
156
168
|
"BinanceProvider",
|
|
@@ -31,10 +31,12 @@ Example (composing mixins):
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
+
import math
|
|
34
35
|
import os
|
|
35
36
|
from abc import ABC, abstractmethod
|
|
36
37
|
from typing import Any, ClassVar
|
|
37
38
|
|
|
39
|
+
import httpx
|
|
38
40
|
import polars as pl
|
|
39
41
|
import structlog
|
|
40
42
|
from tenacity import (
|
|
@@ -45,7 +47,12 @@ from tenacity import (
|
|
|
45
47
|
wait_exponential,
|
|
46
48
|
)
|
|
47
49
|
|
|
48
|
-
from ml4t.data.core.exceptions import
|
|
50
|
+
from ml4t.data.core.exceptions import (
|
|
51
|
+
DataValidationError,
|
|
52
|
+
NetworkError,
|
|
53
|
+
RateLimitError,
|
|
54
|
+
SymbolNotFoundError,
|
|
55
|
+
)
|
|
49
56
|
from ml4t.data.providers.mixins.circuit_breaker import CircuitBreaker, CircuitBreakerMixin
|
|
50
57
|
from ml4t.data.providers.mixins.rate_limit import RateLimitMixin
|
|
51
58
|
from ml4t.data.providers.mixins.session import SessionMixin
|
|
@@ -73,6 +80,29 @@ def _provider_error_is_retryable(error: BaseException) -> bool:
|
|
|
73
80
|
return isinstance(error, NetworkError) and error.retryable
|
|
74
81
|
|
|
75
82
|
|
|
83
|
+
# Shared retry policy: three attempts, exponential backoff, provider ``retry_after`` honored.
|
|
84
|
+
provider_retry = retry(
|
|
85
|
+
stop=stop_after_attempt(3),
|
|
86
|
+
wait=_provider_retry_wait,
|
|
87
|
+
retry=retry_if_exception(_provider_error_is_retryable),
|
|
88
|
+
reraise=True,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def retry_after_seconds(response: httpx.Response) -> float | None:
|
|
93
|
+
"""Return a non-negative ``Retry-After`` delay in seconds, or None when absent or invalid."""
|
|
94
|
+
value = response.headers.get("Retry-After")
|
|
95
|
+
if value is None:
|
|
96
|
+
return None
|
|
97
|
+
try:
|
|
98
|
+
delay = float(value)
|
|
99
|
+
except ValueError:
|
|
100
|
+
return None
|
|
101
|
+
if not math.isfinite(delay):
|
|
102
|
+
return None
|
|
103
|
+
return max(delay, 0.0)
|
|
104
|
+
|
|
105
|
+
|
|
76
106
|
# Re-export for backward compatibility
|
|
77
107
|
__all__ = [
|
|
78
108
|
"BaseProvider",
|
|
@@ -81,6 +111,8 @@ __all__ = [
|
|
|
81
111
|
"circuit_breaker",
|
|
82
112
|
"OHLCVProvider",
|
|
83
113
|
"ProviderCapabilities",
|
|
114
|
+
"provider_retry",
|
|
115
|
+
"retry_after_seconds",
|
|
84
116
|
]
|
|
85
117
|
|
|
86
118
|
|
|
@@ -196,12 +228,7 @@ class BaseProvider(
|
|
|
196
228
|
rate_limit=self.DEFAULT_RATE_LIMIT,
|
|
197
229
|
)
|
|
198
230
|
|
|
199
|
-
@
|
|
200
|
-
stop=stop_after_attempt(3),
|
|
201
|
-
wait=_provider_retry_wait,
|
|
202
|
-
retry=retry_if_exception(_provider_error_is_retryable),
|
|
203
|
-
reraise=True,
|
|
204
|
-
)
|
|
231
|
+
@provider_retry
|
|
205
232
|
def fetch_ohlcv(
|
|
206
233
|
self,
|
|
207
234
|
symbol: str,
|
|
@@ -271,6 +298,94 @@ class BaseProvider(
|
|
|
271
298
|
|
|
272
299
|
return validated_data
|
|
273
300
|
|
|
301
|
+
def _classify_error_response(self, response: httpx.Response) -> Exception | None:
|
|
302
|
+
"""Return a provider-specific exception for an error response, or None for the default.
|
|
303
|
+
|
|
304
|
+
Override to mark a service-specific failure as transient (for example an edge-layer
|
|
305
|
+
rejection). The default classification in ``_request`` applies when this returns None.
|
|
306
|
+
"""
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
@provider_retry
|
|
310
|
+
def _request(
|
|
311
|
+
self,
|
|
312
|
+
url: str,
|
|
313
|
+
*,
|
|
314
|
+
resource: str,
|
|
315
|
+
params: dict[str, Any] | None = None,
|
|
316
|
+
headers: dict[str, str] | None = None,
|
|
317
|
+
) -> httpx.Response:
|
|
318
|
+
"""Rate-limited GET that maps transport and HTTP failures to shared exceptions.
|
|
319
|
+
|
|
320
|
+
Transient failures (transport errors, HTTP 429 and 5xx) raise retryable errors and are
|
|
321
|
+
retried by the shared ``provider_retry`` policy, honoring ``Retry-After``.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
url: Absolute request URL.
|
|
325
|
+
resource: Human-readable name of the requested resource, used in errors.
|
|
326
|
+
params: Query parameters.
|
|
327
|
+
headers: Request headers.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
The successful (2xx) response.
|
|
331
|
+
|
|
332
|
+
Raises:
|
|
333
|
+
RateLimitError: HTTP 429.
|
|
334
|
+
SymbolNotFoundError: HTTP 404; ``symbol`` is ``resource``.
|
|
335
|
+
NetworkError: Transport failure, HTTP 5xx (retryable) or other HTTP errors.
|
|
336
|
+
"""
|
|
337
|
+
self._acquire_rate_limit()
|
|
338
|
+
try:
|
|
339
|
+
response = self.session.get(url, params=params, headers=headers)
|
|
340
|
+
except httpx.HTTPError as err:
|
|
341
|
+
raise NetworkError(
|
|
342
|
+
provider=self.name, message=f"Request failed for {resource}: {err}"
|
|
343
|
+
) from err
|
|
344
|
+
|
|
345
|
+
if 200 <= response.status_code < 300:
|
|
346
|
+
return response
|
|
347
|
+
custom = self._classify_error_response(response)
|
|
348
|
+
if custom is not None:
|
|
349
|
+
raise custom
|
|
350
|
+
if response.status_code == 429:
|
|
351
|
+
raise RateLimitError(provider=self.name, retry_after=retry_after_seconds(response))
|
|
352
|
+
if response.status_code == 404:
|
|
353
|
+
raise SymbolNotFoundError(provider=self.name, symbol=resource)
|
|
354
|
+
message = f"HTTP {response.status_code} for {resource}: {response.text[:200]}"
|
|
355
|
+
raise NetworkError(
|
|
356
|
+
provider=self.name,
|
|
357
|
+
message=message,
|
|
358
|
+
retryable=response.status_code >= 500,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
def _request_json(
|
|
362
|
+
self,
|
|
363
|
+
url: str,
|
|
364
|
+
*,
|
|
365
|
+
resource: str,
|
|
366
|
+
params: dict[str, Any] | None = None,
|
|
367
|
+
headers: dict[str, str] | None = None,
|
|
368
|
+
) -> dict[str, Any]:
|
|
369
|
+
"""Run ``_request`` and parse a JSON object body.
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
DataValidationError: The body is not valid JSON or not a JSON object.
|
|
373
|
+
"""
|
|
374
|
+
response = self._request(url, resource=resource, params=params, headers=headers)
|
|
375
|
+
try:
|
|
376
|
+
payload = response.json()
|
|
377
|
+
except ValueError as err:
|
|
378
|
+
raise DataValidationError(
|
|
379
|
+
provider=self.name,
|
|
380
|
+
message=f"Malformed JSON response for {resource}",
|
|
381
|
+
) from err
|
|
382
|
+
if not isinstance(payload, dict):
|
|
383
|
+
raise DataValidationError(
|
|
384
|
+
provider=self.name,
|
|
385
|
+
message=f"Expected a JSON object for {resource}, got {type(payload).__name__}",
|
|
386
|
+
)
|
|
387
|
+
return payload
|
|
388
|
+
|
|
274
389
|
def _create_empty_dataframe(self) -> pl.DataFrame:
|
|
275
390
|
"""Create an empty DataFrame with canonical OHLCV schema.
|
|
276
391
|
|
|
@@ -41,6 +41,7 @@ from __future__ import annotations
|
|
|
41
41
|
|
|
42
42
|
import io
|
|
43
43
|
import zipfile
|
|
44
|
+
from calendar import monthrange
|
|
44
45
|
from datetime import datetime
|
|
45
46
|
from pathlib import Path
|
|
46
47
|
from typing import Any, ClassVar, Literal
|
|
@@ -730,11 +731,11 @@ class FamaFrenchProvider(BaseProvider):
|
|
|
730
731
|
)
|
|
731
732
|
df = df.filter(pl.col("timestamp") >= start_date)
|
|
732
733
|
if end:
|
|
733
|
-
|
|
734
|
-
datetime.strptime(end[:10], "%Y-%m-%d").date()
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
734
|
+
if len(end) >= 10:
|
|
735
|
+
end_date = datetime.strptime(end[:10], "%Y-%m-%d").date()
|
|
736
|
+
else:
|
|
737
|
+
year, month = (int(part) for part in end.split("-", 1))
|
|
738
|
+
end_date = datetime(year, month, monthrange(year, month)[1]).date()
|
|
738
739
|
df = df.filter(pl.col("timestamp") <= end_date)
|
|
739
740
|
|
|
740
741
|
self.logger.info(
|
|
@@ -4,7 +4,7 @@ Endpoint availability, request quotas, and historical depth depend on the accoun
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
|
-
from datetime import datetime
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
8
|
from typing import Any, ClassVar
|
|
9
9
|
|
|
10
10
|
import polars as pl
|
|
@@ -34,6 +34,19 @@ from ml4t.data.providers.fundamentals import (
|
|
|
34
34
|
logger = structlog.get_logger()
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def _utc_unix_timestamp(value: str, *, inclusive_date: bool = False) -> int:
|
|
38
|
+
"""Parse a date or datetime as a UTC unix timestamp.
|
|
39
|
+
|
|
40
|
+
A date-only end bound covers that UTC calendar day. An explicit datetime
|
|
41
|
+
is used as given, with naive values treated as UTC.
|
|
42
|
+
"""
|
|
43
|
+
parsed = datetime.fromisoformat(value)
|
|
44
|
+
parsed = parsed.replace(tzinfo=UTC) if parsed.tzinfo is None else parsed.astimezone(UTC)
|
|
45
|
+
if inclusive_date and "T" not in value and len(value) == 10:
|
|
46
|
+
parsed = parsed.replace(hour=23, minute=59, second=59)
|
|
47
|
+
return int(parsed.timestamp())
|
|
48
|
+
|
|
49
|
+
|
|
37
50
|
class FinnhubProvider(BaseProvider):
|
|
38
51
|
"""Fetch Finnhub market data through synchronous provider methods.
|
|
39
52
|
|
|
@@ -196,12 +209,11 @@ class FinnhubProvider(BaseProvider):
|
|
|
196
209
|
value=frequency,
|
|
197
210
|
)
|
|
198
211
|
|
|
199
|
-
# Convert dates to unix timestamps
|
|
212
|
+
# Convert dates to unix timestamps. Date-only bounds are UTC calendar
|
|
213
|
+
# days, with the end date inclusive through 23:59:59.
|
|
200
214
|
try:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
start_ts = int(start_dt.timestamp())
|
|
204
|
-
end_ts = int(end_dt.timestamp())
|
|
215
|
+
start_ts = _utc_unix_timestamp(start)
|
|
216
|
+
end_ts = _utc_unix_timestamp(end, inclusive_date=True)
|
|
205
217
|
except ValueError as err:
|
|
206
218
|
raise DataValidationError(
|
|
207
219
|
provider="finnhub",
|