fmp-data 2.2.1__tar.gz → 2.3.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.
- {fmp_data-2.2.1 → fmp_data-2.3.1}/PKG-INFO +25 -19
- {fmp_data-2.2.1 → fmp_data-2.3.1}/README.md +6 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/models.py +2 -2
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/base.py +4 -4
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/redis_backend.py +6 -3
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/models.py +1 -1
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/embedding.py +1 -1
- {fmp_data-2.2.1 → fmp_data-2.3.1}/pyproject.toml +36 -36
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_company.py +1 -1
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_embedding.py +1 -1
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_alternative.py +33 -1
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_base.py +42 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_company.py +37 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_intelligence.py +49 -50
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_mcp.py +3 -3
- {fmp_data-2.2.1 → fmp_data-2.3.1}/.gitignore +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/LICENSE +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/NOTICE +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/alternative/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/_csv_utils.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/batch/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/base.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/config.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/file.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/cache/memory.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/hints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/company/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/config.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/economics/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/exceptions.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/fundamental/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/helpers.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/index/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/institutional/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/intelligence/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/investment/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/config.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/hints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/registry.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/utils.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/validation.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/lc/vector_store.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/logger.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/hints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/market/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/__main__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/cli.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/discovery.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/server.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/setup.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/tool_loader.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/tools_manifest.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/mcp/utils.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/py.typed +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/rate_limit.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/sec/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/technical/schema.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/async_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/endpoints.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/fmp_data/transcripts/models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/examples/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/examples/test_examples_smoke.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/base.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/conftest.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_alternative.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_batch.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_economics.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_fundamental.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_index.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_institutional.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_intelligence.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_investment.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_lc.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_market.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_sec.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_technical.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/integration/test_transcripts.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/conftest.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/__init__.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/conftest.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_config.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_endpoint_based_rules.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_imports.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_mapping.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_utils.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_validation_registry.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/lc/test_vector_store.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_alternative_coverage.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_async_clients.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_base_async.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_base_coverage.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_batch.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_cache_backends.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_cache_integration.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_cassette_contracts.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_client.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_company_coverage.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_config.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_economics.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_exceptions.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_fundamental.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_helpers.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_index.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_institutional.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_investment.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_logger.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_market.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_mcp_cli.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_models.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_rate_limit.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_rate_limit_async.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_rate_limit_coverage.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_sec.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_technical.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_technical_coverage.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_transcripts.py +0 -0
- {fmp_data-2.2.1 → fmp_data-2.3.1}/tests/unit/test_vcr_sanitization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmp-data
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Python client for the Financial Modeling Prep API
|
|
5
5
|
Project-URL: Homepage, https://github.com/MehdiZare/fmp-data
|
|
6
6
|
Project-URL: Repository, https://github.com/MehdiZare/fmp-data
|
|
@@ -29,42 +29,42 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
29
29
|
Requires-Python: <3.15,>=3.10
|
|
30
30
|
Requires-Dist: httpx>=0.28.1
|
|
31
31
|
Requires-Dist: pydantic>=2.12.5
|
|
32
|
-
Requires-Dist: tenacity>=9.1.
|
|
32
|
+
Requires-Dist: tenacity>=9.1.4
|
|
33
33
|
Provides-Extra: cache-redis
|
|
34
|
-
Requires-Dist: redis>=
|
|
34
|
+
Requires-Dist: redis>=7.4.0; extra == 'cache-redis'
|
|
35
35
|
Provides-Extra: dev
|
|
36
|
-
Requires-Dist: bandit[toml]>=1.9.
|
|
37
|
-
Requires-Dist: coverage>=7.13.
|
|
36
|
+
Requires-Dist: bandit[toml]>=1.9.4; extra == 'dev'
|
|
37
|
+
Requires-Dist: coverage>=7.13.5; extra == 'dev'
|
|
38
38
|
Requires-Dist: freezegun>=1.5.5; extra == 'dev'
|
|
39
|
-
Requires-Dist: mypy>=1.
|
|
40
|
-
Requires-Dist: nox>=
|
|
39
|
+
Requires-Dist: mypy>=1.20.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: nox>=2026.2.9; extra == 'dev'
|
|
41
41
|
Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
|
|
42
42
|
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
43
43
|
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
|
|
44
|
-
Requires-Dist: pytest-cov>=7.
|
|
44
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == 'dev'
|
|
45
45
|
Requires-Dist: pytest-mock>=3.15.1; extra == 'dev'
|
|
46
46
|
Requires-Dist: pytest-xdist>=3.6.1; extra == 'dev'
|
|
47
47
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
48
|
-
Requires-Dist: python-dotenv>=1.
|
|
49
|
-
Requires-Dist: responses>=0.
|
|
50
|
-
Requires-Dist: rich>=14.3.
|
|
51
|
-
Requires-Dist: ruff>=0.
|
|
48
|
+
Requires-Dist: python-dotenv>=1.2.2; extra == 'dev'
|
|
49
|
+
Requires-Dist: responses>=0.26.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: rich>=14.3.3; extra == 'dev'
|
|
51
|
+
Requires-Dist: ruff>=0.15.9; extra == 'dev'
|
|
52
52
|
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
53
53
|
Requires-Dist: vcrpy>=8.1.1; extra == 'dev'
|
|
54
54
|
Provides-Extra: docs
|
|
55
|
-
Requires-Dist: mkdocs-material>=9.7.
|
|
55
|
+
Requires-Dist: mkdocs-material>=9.7.6; extra == 'docs'
|
|
56
56
|
Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
|
|
57
|
-
Requires-Dist: mkdocstrings-python>=2.0.
|
|
57
|
+
Requires-Dist: mkdocstrings-python>=2.0.3; extra == 'docs'
|
|
58
58
|
Provides-Extra: langchain
|
|
59
59
|
Requires-Dist: faiss-cpu>=1.13.2; extra == 'langchain'
|
|
60
60
|
Requires-Dist: langchain-community>=0.4.1; extra == 'langchain'
|
|
61
|
-
Requires-Dist: langchain-core>=1.2.
|
|
62
|
-
Requires-Dist: langchain-openai>=1.1.
|
|
63
|
-
Requires-Dist: langgraph>=1.
|
|
64
|
-
Requires-Dist: openai>=2.
|
|
61
|
+
Requires-Dist: langchain-core>=1.2.26; extra == 'langchain'
|
|
62
|
+
Requires-Dist: langchain-openai>=1.1.12; extra == 'langchain'
|
|
63
|
+
Requires-Dist: langgraph>=1.1.6; extra == 'langchain'
|
|
64
|
+
Requires-Dist: openai>=2.30.0; extra == 'langchain'
|
|
65
65
|
Requires-Dist: tiktoken>=0.12.0; extra == 'langchain'
|
|
66
66
|
Provides-Extra: mcp
|
|
67
|
-
Requires-Dist: mcp[cli]>=1.
|
|
67
|
+
Requires-Dist: mcp[cli]>=1.27.0; extra == 'mcp'
|
|
68
68
|
Requires-Dist: pyyaml>=6.0.3; extra == 'mcp'
|
|
69
69
|
Description-Content-Type: text/markdown
|
|
70
70
|
|
|
@@ -118,6 +118,12 @@ To use this library, you'll need an API key from Financial Modeling Prep (FMP).
|
|
|
118
118
|
|
|
119
119
|
## Installation
|
|
120
120
|
|
|
121
|
+
## Launch Notes
|
|
122
|
+
|
|
123
|
+
- Price-history volume fields now normalize to `float` so integer and fractional FMP payloads share one stable return type.
|
|
124
|
+
- This affects `alternative.HistoricalPrice.volume`, `alternative.HistoricalPrice.unadjusted_volume`, and `company.IntradayPrice.volume`.
|
|
125
|
+
- Whole-number payloads for those models now deserialize as values like `123.0`, which is why this ships as a minor release instead of a patch.
|
|
126
|
+
|
|
121
127
|
### Using UV (Recommended)
|
|
122
128
|
|
|
123
129
|
```bash
|
|
@@ -48,6 +48,12 @@ To use this library, you'll need an API key from Financial Modeling Prep (FMP).
|
|
|
48
48
|
|
|
49
49
|
## Installation
|
|
50
50
|
|
|
51
|
+
## Launch Notes
|
|
52
|
+
|
|
53
|
+
- Price-history volume fields now normalize to `float` so integer and fractional FMP payloads share one stable return type.
|
|
54
|
+
- This affects `alternative.HistoricalPrice.volume`, `alternative.HistoricalPrice.unadjusted_volume`, and `company.IntradayPrice.volume`.
|
|
55
|
+
- Whole-number payloads for those models now deserialize as values like `123.0`, which is why this ships as a minor release instead of a patch.
|
|
56
|
+
|
|
51
57
|
### Using UV (Recommended)
|
|
52
58
|
|
|
53
59
|
```bash
|
|
@@ -119,8 +119,8 @@ class HistoricalPrice(BaseModel):
|
|
|
119
119
|
adj_close: float | None = Field(
|
|
120
120
|
None, alias="adjClose", description="Adjusted closing price"
|
|
121
121
|
)
|
|
122
|
-
volume:
|
|
123
|
-
unadjusted_volume:
|
|
122
|
+
volume: float = Field(description="Volume traded")
|
|
123
|
+
unadjusted_volume: float | None = Field(
|
|
124
124
|
None, alias="unadjustedVolume", description="Unadjusted trading volume"
|
|
125
125
|
)
|
|
126
126
|
change: float = Field(description="Price change")
|
|
@@ -564,27 +564,27 @@ class BaseClient:
|
|
|
564
564
|
status_code=429,
|
|
565
565
|
response=error_details,
|
|
566
566
|
retry_after=wait_time,
|
|
567
|
-
) from
|
|
567
|
+
) from None
|
|
568
568
|
|
|
569
569
|
if status_code == 401:
|
|
570
570
|
raise AuthenticationError(
|
|
571
571
|
"Invalid API key or authentication failed",
|
|
572
572
|
status_code=401,
|
|
573
573
|
response=error_details,
|
|
574
|
-
) from
|
|
574
|
+
) from None
|
|
575
575
|
|
|
576
576
|
if status_code == 400:
|
|
577
577
|
raise ValidationError(
|
|
578
578
|
f"Invalid request parameters: {error_details}",
|
|
579
579
|
status_code=400,
|
|
580
580
|
response=error_details,
|
|
581
|
-
) from
|
|
581
|
+
) from None
|
|
582
582
|
|
|
583
583
|
raise FMPError(
|
|
584
584
|
f"HTTP {status_code} error occurred: {error_details}",
|
|
585
585
|
status_code=status_code,
|
|
586
586
|
response=error_details,
|
|
587
|
-
) from
|
|
587
|
+
) from None
|
|
588
588
|
|
|
589
589
|
@staticmethod
|
|
590
590
|
def _check_error_response(data: dict[str, Any]) -> None:
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import logging
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any, cast
|
|
8
8
|
|
|
9
9
|
from fmp_data.cache.base import CacheBackend
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ class RedisCache(CacheBackend):
|
|
|
44
44
|
|
|
45
45
|
def get(self, key: str) -> Any | None:
|
|
46
46
|
try:
|
|
47
|
-
raw = self._client.get(self._prefixed(key))
|
|
47
|
+
raw = cast(str | None, self._client.get(self._prefixed(key)))
|
|
48
48
|
except Exception:
|
|
49
49
|
logger.warning("Redis get error for key %s", key, exc_info=True)
|
|
50
50
|
return None
|
|
@@ -74,7 +74,10 @@ class RedisCache(CacheBackend):
|
|
|
74
74
|
cursor: int = 0
|
|
75
75
|
try:
|
|
76
76
|
while True:
|
|
77
|
-
cursor, keys =
|
|
77
|
+
cursor, keys = cast(
|
|
78
|
+
tuple[int, list[str]],
|
|
79
|
+
self._client.scan(cursor, match=pattern, count=100),
|
|
80
|
+
)
|
|
78
81
|
if keys:
|
|
79
82
|
self._client.delete(*keys)
|
|
80
83
|
if cursor == 0:
|
|
@@ -517,7 +517,7 @@ class IntradayPrice(BaseModel):
|
|
|
517
517
|
low: float = Field(description="Low price")
|
|
518
518
|
high: float = Field(description="High price")
|
|
519
519
|
close: float = Field(description="Closing price")
|
|
520
|
-
volume:
|
|
520
|
+
volume: float = Field(description="Trading volume")
|
|
521
521
|
|
|
522
522
|
|
|
523
523
|
class ExecutiveCompensation(BaseModel):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
|
-
"hatchling>=1.
|
|
4
|
-
"hatch-vcs>=0.
|
|
3
|
+
"hatchling>=1.29.0",
|
|
4
|
+
"hatch-vcs>=0.5.0",
|
|
5
5
|
]
|
|
6
6
|
build-backend = "hatchling.build"
|
|
7
7
|
|
|
@@ -48,7 +48,7 @@ classifiers = [
|
|
|
48
48
|
dependencies = [
|
|
49
49
|
"httpx>=0.28.1",
|
|
50
50
|
"pydantic>=2.12.5",
|
|
51
|
-
"tenacity>=9.1.
|
|
51
|
+
"tenacity>=9.1.4",
|
|
52
52
|
]
|
|
53
53
|
|
|
54
54
|
[project.license]
|
|
@@ -64,84 +64,84 @@ fmp-mcp = "fmp_data.mcp.cli:main"
|
|
|
64
64
|
|
|
65
65
|
[project.optional-dependencies]
|
|
66
66
|
mcp = [
|
|
67
|
-
"mcp[cli]>=1.
|
|
67
|
+
"mcp[cli]>=1.27.0",
|
|
68
68
|
"pyyaml>=6.0.3",
|
|
69
69
|
]
|
|
70
70
|
cache-redis = [
|
|
71
|
-
"redis>=
|
|
71
|
+
"redis>=7.4.0",
|
|
72
72
|
]
|
|
73
73
|
langchain = [
|
|
74
74
|
"faiss-cpu>=1.13.2",
|
|
75
|
-
"langchain-core>=1.2.
|
|
75
|
+
"langchain-core>=1.2.26",
|
|
76
76
|
"langchain-community>=0.4.1",
|
|
77
|
-
"langchain-openai>=1.1.
|
|
78
|
-
"langgraph>=1.
|
|
79
|
-
"openai>=2.
|
|
77
|
+
"langchain-openai>=1.1.12",
|
|
78
|
+
"langgraph>=1.1.6",
|
|
79
|
+
"openai>=2.30.0",
|
|
80
80
|
"tiktoken>=0.12.0",
|
|
81
81
|
]
|
|
82
82
|
dev = [
|
|
83
|
-
"ruff>=0.
|
|
84
|
-
"mypy>=1.
|
|
85
|
-
"bandit[toml]>=1.9.
|
|
83
|
+
"ruff>=0.15.9",
|
|
84
|
+
"mypy>=1.20.0",
|
|
85
|
+
"bandit[toml]>=1.9.4",
|
|
86
86
|
"pip-audit>=2.10.0",
|
|
87
|
-
"python-dotenv>=1.
|
|
87
|
+
"python-dotenv>=1.2.2",
|
|
88
88
|
"pytest>=9.0.2",
|
|
89
89
|
"pytest-asyncio>=1.3.0",
|
|
90
|
-
"pytest-cov>=7.
|
|
90
|
+
"pytest-cov>=7.1.0",
|
|
91
91
|
"pytest-mock>=3.15.1",
|
|
92
92
|
"pytest-xdist>=3.6.1",
|
|
93
93
|
"freezegun>=1.5.5",
|
|
94
|
-
"responses>=0.
|
|
94
|
+
"responses>=0.26.0",
|
|
95
95
|
"vcrpy>=8.1.1",
|
|
96
|
-
"coverage>=7.13.
|
|
96
|
+
"coverage>=7.13.5",
|
|
97
97
|
"pre-commit>=4.5.1",
|
|
98
|
-
"rich>=14.3.
|
|
98
|
+
"rich>=14.3.3",
|
|
99
99
|
"twine>=6.2.0",
|
|
100
|
-
"nox>=
|
|
100
|
+
"nox>=2026.2.9",
|
|
101
101
|
]
|
|
102
102
|
docs = [
|
|
103
103
|
"mkdocs>=1.6.1",
|
|
104
|
-
"mkdocs-material>=9.7.
|
|
105
|
-
"mkdocstrings-python>=2.0.
|
|
104
|
+
"mkdocs-material>=9.7.6",
|
|
105
|
+
"mkdocstrings-python>=2.0.3",
|
|
106
106
|
]
|
|
107
107
|
|
|
108
108
|
[dependency-groups]
|
|
109
109
|
dev = [
|
|
110
|
-
"ruff>=0.
|
|
111
|
-
"mypy>=1.
|
|
112
|
-
"bandit[toml]>=1.9.
|
|
110
|
+
"ruff>=0.15.9",
|
|
111
|
+
"mypy>=1.20.0",
|
|
112
|
+
"bandit[toml]>=1.9.4",
|
|
113
113
|
"pip-audit>=2.10.0",
|
|
114
|
-
"python-dotenv>=1.
|
|
114
|
+
"python-dotenv>=1.2.2",
|
|
115
115
|
"pytest>=9.0.2",
|
|
116
116
|
"pytest-asyncio>=1.3.0",
|
|
117
|
-
"pytest-cov>=7.
|
|
117
|
+
"pytest-cov>=7.1.0",
|
|
118
118
|
"pytest-mock>=3.15.1",
|
|
119
119
|
"pytest-xdist>=3.6.1",
|
|
120
120
|
"freezegun>=1.5.5",
|
|
121
|
-
"responses>=0.
|
|
121
|
+
"responses>=0.26.0",
|
|
122
122
|
"vcrpy>=8.1.1",
|
|
123
|
-
"coverage>=7.13.
|
|
123
|
+
"coverage>=7.13.5",
|
|
124
124
|
"pre-commit>=4.5.1",
|
|
125
|
-
"rich>=14.3.
|
|
125
|
+
"rich>=14.3.3",
|
|
126
126
|
"twine>=6.2.0",
|
|
127
|
-
"nox>=
|
|
127
|
+
"nox>=2026.2.9",
|
|
128
128
|
]
|
|
129
129
|
docs = [
|
|
130
130
|
"mkdocs>=1.6.1",
|
|
131
|
-
"mkdocs-material>=9.7.
|
|
132
|
-
"mkdocstrings-python>=2.0.
|
|
131
|
+
"mkdocs-material>=9.7.6",
|
|
132
|
+
"mkdocstrings-python>=2.0.3",
|
|
133
133
|
]
|
|
134
134
|
langchain = [
|
|
135
135
|
"faiss-cpu>=1.13.2",
|
|
136
|
-
"langchain-core>=1.2.
|
|
136
|
+
"langchain-core>=1.2.26",
|
|
137
137
|
"langchain-community>=0.4.1",
|
|
138
|
-
"langchain-openai>=1.1.
|
|
139
|
-
"langgraph>=1.
|
|
140
|
-
"openai>=2.
|
|
138
|
+
"langchain-openai>=1.1.12",
|
|
139
|
+
"langgraph>=1.1.6",
|
|
140
|
+
"openai>=2.30.0",
|
|
141
141
|
"tiktoken>=0.12.0",
|
|
142
142
|
]
|
|
143
143
|
mcp = [
|
|
144
|
-
"mcp[cli]>=1.
|
|
144
|
+
"mcp[cli]>=1.27.0",
|
|
145
145
|
"pyyaml>=6.0.3",
|
|
146
146
|
]
|
|
147
147
|
|
|
@@ -130,7 +130,7 @@ class TestCompanyEndpoints(BaseTestCase):
|
|
|
130
130
|
assert isinstance(price.high, float)
|
|
131
131
|
assert isinstance(price.low, float)
|
|
132
132
|
assert isinstance(price.close, float)
|
|
133
|
-
assert isinstance(price.volume,
|
|
133
|
+
assert isinstance(price.volume, float)
|
|
134
134
|
|
|
135
135
|
def test_get_market_cap(self, fmp_client: FMPDataClient, vcr_instance):
|
|
136
136
|
"""Test getting market capitalization data"""
|
|
@@ -9,6 +9,9 @@ from fmp_data.alternative.models import (
|
|
|
9
9
|
CryptoQuote,
|
|
10
10
|
ForexQuote,
|
|
11
11
|
)
|
|
12
|
+
from fmp_data.alternative.models import (
|
|
13
|
+
HistoricalPrice as AlternativeHistoricalPrice,
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
@pytest.fixture
|
|
@@ -155,7 +158,36 @@ def test_crypto_historical_price_model(mock_crypto_historical):
|
|
|
155
158
|
assert price.low == 43500.00
|
|
156
159
|
assert price.close == 45000.00
|
|
157
160
|
assert price.adj_close == 45000.00
|
|
158
|
-
assert price.volume == 25000000000
|
|
161
|
+
assert price.volume == pytest.approx(25000000000.0)
|
|
162
|
+
assert isinstance(price.volume, float)
|
|
159
163
|
assert price.change == 1250.00
|
|
160
164
|
assert price.change_percent == 2.85
|
|
161
165
|
assert price.vwap == 44500.00
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_alternative_historical_price_accepts_float_volume(mock_crypto_historical):
|
|
169
|
+
"""Test alternative historical price models accept float volume values."""
|
|
170
|
+
historical_data = dict(mock_crypto_historical["historical"][0])
|
|
171
|
+
historical_data["volume"] = 25000000000.75
|
|
172
|
+
historical_data["unadjustedVolume"] = 24999999999.25
|
|
173
|
+
|
|
174
|
+
price = AlternativeHistoricalPrice.model_validate(historical_data)
|
|
175
|
+
|
|
176
|
+
assert price.volume == pytest.approx(25000000000.75)
|
|
177
|
+
assert price.unadjusted_volume == pytest.approx(24999999999.25)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_alternative_historical_price_normalizes_integer_volume_to_float(
|
|
181
|
+
mock_crypto_historical,
|
|
182
|
+
):
|
|
183
|
+
"""Test integer volume payloads normalize to float for a single API shape."""
|
|
184
|
+
historical_data = dict(mock_crypto_historical["historical"][0])
|
|
185
|
+
historical_data["volume"] = 123456789
|
|
186
|
+
historical_data["unadjustedVolume"] = 123456700
|
|
187
|
+
|
|
188
|
+
price = AlternativeHistoricalPrice.model_validate(historical_data)
|
|
189
|
+
|
|
190
|
+
assert price.volume == pytest.approx(123456789.0)
|
|
191
|
+
assert isinstance(price.volume, float)
|
|
192
|
+
assert price.unadjusted_volume == pytest.approx(123456700.0)
|
|
193
|
+
assert isinstance(price.unadjusted_volume, float)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import traceback
|
|
2
3
|
from unittest.mock import MagicMock, Mock, patch
|
|
3
4
|
|
|
4
5
|
import httpx
|
|
@@ -273,6 +274,47 @@ def test_handle_http_status_error_uses_retry_after_header(base_client):
|
|
|
273
274
|
assert exc_info.value.retry_after == 12.0
|
|
274
275
|
|
|
275
276
|
|
|
277
|
+
@pytest.mark.parametrize(
|
|
278
|
+
("status_code", "expected_exception"),
|
|
279
|
+
[
|
|
280
|
+
(429, RateLimitError),
|
|
281
|
+
(401, AuthenticationError),
|
|
282
|
+
(400, ValidationError),
|
|
283
|
+
(500, FMPError),
|
|
284
|
+
],
|
|
285
|
+
)
|
|
286
|
+
def test_handle_http_status_error_redacts_chained_apikey_traceback(
|
|
287
|
+
base_client: BaseClient,
|
|
288
|
+
status_code: int,
|
|
289
|
+
expected_exception: type[FMPError],
|
|
290
|
+
) -> None:
|
|
291
|
+
"""Test HTTP error tracebacks do not expose the API key query parameter."""
|
|
292
|
+
marker = "TRACEBACK_REDACTION_SENTINEL"
|
|
293
|
+
request = httpx.Request(
|
|
294
|
+
"GET",
|
|
295
|
+
f"https://financialmodelingprep.com/stable/quote?symbol=AAPL&apikey={marker}",
|
|
296
|
+
)
|
|
297
|
+
response = httpx.Response(
|
|
298
|
+
status_code,
|
|
299
|
+
request=request,
|
|
300
|
+
json={"message": "Rate limit exceeded"},
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
base_client.handle_response(response)
|
|
305
|
+
except FMPError as exc:
|
|
306
|
+
assert isinstance(exc, expected_exception)
|
|
307
|
+
formatted_traceback = traceback.format_exc()
|
|
308
|
+
assert "apikey=" not in formatted_traceback
|
|
309
|
+
assert marker not in formatted_traceback
|
|
310
|
+
assert "apikey=" not in str(exc)
|
|
311
|
+
assert marker not in str(exc)
|
|
312
|
+
assert exc.__cause__ is None
|
|
313
|
+
assert exc.__suppress_context__ is True
|
|
314
|
+
else:
|
|
315
|
+
pytest.fail(f"Expected {expected_exception.__name__}")
|
|
316
|
+
|
|
317
|
+
|
|
276
318
|
@pytest.mark.parametrize(
|
|
277
319
|
"payload",
|
|
278
320
|
[
|
|
@@ -11,6 +11,7 @@ from fmp_data.company.models import (
|
|
|
11
11
|
ExecutiveCompensationBenchmark,
|
|
12
12
|
HistoricalData,
|
|
13
13
|
HistoricalPrice,
|
|
14
|
+
IntradayPrice,
|
|
14
15
|
MergerAcquisition,
|
|
15
16
|
PriceTarget,
|
|
16
17
|
PriceTargetSummary,
|
|
@@ -444,6 +445,42 @@ class TestSimpleQuoteModel:
|
|
|
444
445
|
"""Test SimpleQuote volume validator handles None."""
|
|
445
446
|
assert SimpleQuote.coerce_volume_to_int(None) is None
|
|
446
447
|
|
|
448
|
+
|
|
449
|
+
class TestIntradayPriceModel:
|
|
450
|
+
"""Tests for intraday price model validation"""
|
|
451
|
+
|
|
452
|
+
def test_intraday_price_accepts_float_volume(self):
|
|
453
|
+
"""Test intraday prices accept float volume values from the API."""
|
|
454
|
+
data = {
|
|
455
|
+
"date": "2024-01-05T16:00:00",
|
|
456
|
+
"open": 149.00,
|
|
457
|
+
"low": 148.50,
|
|
458
|
+
"high": 151.00,
|
|
459
|
+
"close": 150.25,
|
|
460
|
+
"volume": 28541.004200000316,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
price = IntradayPrice.model_validate(data)
|
|
464
|
+
|
|
465
|
+
assert price.volume == pytest.approx(28541.004200000316)
|
|
466
|
+
assert isinstance(price.volume, float)
|
|
467
|
+
|
|
468
|
+
def test_intraday_price_normalizes_integer_volume_to_float(self):
|
|
469
|
+
"""Test integer intraday volume payloads normalize to float."""
|
|
470
|
+
data = {
|
|
471
|
+
"date": "2024-01-05T16:00:00",
|
|
472
|
+
"open": 149.00,
|
|
473
|
+
"low": 148.50,
|
|
474
|
+
"high": 151.00,
|
|
475
|
+
"close": 150.25,
|
|
476
|
+
"volume": 28541,
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
price = IntradayPrice.model_validate(data)
|
|
480
|
+
|
|
481
|
+
assert price.volume == pytest.approx(28541.0)
|
|
482
|
+
assert isinstance(price.volume, float)
|
|
483
|
+
|
|
447
484
|
def test_simple_quote_volume_validator_string(self):
|
|
448
485
|
"""Test SimpleQuote volume validator passes non-numeric values."""
|
|
449
486
|
assert SimpleQuote.coerce_volume_to_int("abc") == "abc"
|