equity-aggregator 1.0.0__tar.gz → 1.0.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.
Files changed (250) hide show
  1. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/PKG-INFO +1 -1
  2. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/pyproject.toml +1 -1
  3. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/fuzzy.py +10 -2
  4. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_fuzzy.py +66 -1
  5. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/uv.lock +1 -1
  6. equity_aggregator-1.0.0/.claude/settings.local.json +0 -25
  7. equity_aggregator-1.0.0/debug_yfinance.py +0 -473
  8. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.dockerignore +0 -0
  9. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.env_example +0 -0
  10. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.github/workflows/publish-build-release.yml +0 -0
  11. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.github/workflows/publish-pypi.yml +0 -0
  12. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.github/workflows/validate-push.yml +0 -0
  13. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.gitignore +0 -0
  14. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/.python-version +0 -0
  15. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/Dockerfile +0 -0
  16. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/LICENCE.txt +0 -0
  17. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/README.md +0 -0
  18. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/docker-compose.yml +0 -0
  19. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/intrinio_equity.json +0 -0
  20. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/lseg_equities.json +0 -0
  21. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/sec_equity.json +0 -0
  22. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/stock_analysis_equity.json +0 -0
  23. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/tradingview_equities.json +0 -0
  24. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/xetra_equities.json +0 -0
  25. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/yfinance_quote_summary_equity.json +0 -0
  26. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/documentation/reference_data/yfinance_search_equity.json +0 -0
  27. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/__init__.py +0 -0
  28. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/__main__.py +0 -0
  29. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/analyzers.py +0 -0
  30. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/formatters.py +0 -0
  31. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/main.py +0 -0
  32. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/models.py +0 -0
  33. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/examples/data_integrity_analysis/rendering.py +0 -0
  34. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/README.md +0 -0
  35. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/__init__.py +0 -0
  36. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/__init__.py +0 -0
  37. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/__init__.py +0 -0
  38. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/_utils/__init__.py +0 -0
  39. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/_utils/_client.py +0 -0
  40. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/_utils/_record_types.py +0 -0
  41. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/__init__.py +0 -0
  42. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
  43. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
  44. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/backoff.py +0 -0
  45. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/_utils/parser.py +0 -0
  46. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/intrinio.py +0 -0
  47. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/intrinio/session.py +0 -0
  48. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
  49. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
  50. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/backoff.py +0 -0
  51. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/_utils/parser.py +0 -0
  52. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/lseg.py +0 -0
  53. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/lseg/session.py +0 -0
  54. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
  55. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/sec/sec.py +0 -0
  56. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
  57. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/stock_analysis/stock_analysis.py +0 -0
  58. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
  59. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/tradingview/tradingview.py +0 -0
  60. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
  61. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/discovery_feeds/xetra/xetra.py +0 -0
  62. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
  63. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
  64. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/api.py +0 -0
  65. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/download.py +0 -0
  66. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/gleif.py +0 -0
  67. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/gleif/parser.py +0 -0
  68. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
  69. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
  70. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/backoff.py +0 -0
  71. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/_utils/json.py +0 -0
  72. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
  73. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/quote_summary.py +0 -0
  74. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/api/search.py +0 -0
  75. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/auth.py +0 -0
  76. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/config.py +0 -0
  77. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/ranking.py +0 -0
  78. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/session.py +0 -0
  79. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/transport.py +0 -0
  80. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/enrichment_feeds/yfinance/yfinance.py +0 -0
  81. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/reference_lookup/__init__.py +0 -0
  82. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/reference_lookup/exchange_rate_api.py +0 -0
  83. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/adapters/data_sources/reference_lookup/openfigi.py +0 -0
  84. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/__init__.py +0 -0
  85. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/config.py +0 -0
  86. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/dispatcher.py +0 -0
  87. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/main.py +0 -0
  88. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/parser.py +0 -0
  89. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/cli/signals.py +0 -0
  90. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/__init__.py +0 -0
  91. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/_utils/__init__.py +0 -0
  92. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/_utils/_load_converter.py +0 -0
  93. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/_utils/_merge.py +0 -0
  94. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/_utils/_merge_config.py +0 -0
  95. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/_utils/_strategies.py +0 -0
  96. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/__init__.py +0 -0
  97. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/resolve.py +0 -0
  98. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/runner.py +0 -0
  99. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/seed.py +0 -0
  100. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/__init__.py +0 -0
  101. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/canonicalise.py +0 -0
  102. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/convert.py +0 -0
  103. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/enrich.py +0 -0
  104. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/group.py +0 -0
  105. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/identify.py +0 -0
  106. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/pipeline/transforms/parse.py +0 -0
  107. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/retrieval/__init__.py +0 -0
  108. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/domain/retrieval/retrieval.py +0 -0
  109. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/logging_config.py +0 -0
  110. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/__init__.py +0 -0
  111. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/canonical.py +0 -0
  112. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/__init__.py +0 -0
  113. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/feed_validators.py +0 -0
  114. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/gleif_feed_data.py +0 -0
  115. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/intrinio_feed_data.py +0 -0
  116. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/lseg_feed_data.py +0 -0
  117. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/sec_feed_data.py +0 -0
  118. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/stock_analysis_feed_data.py +0 -0
  119. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/tradingview_feed_data.py +0 -0
  120. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/xetra_feed_data.py +0 -0
  121. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/feeds/yfinance_feed_data.py +0 -0
  122. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/raw.py +0 -0
  123. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/types.py +0 -0
  124. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/schemas/validators.py +0 -0
  125. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/__init__.py +0 -0
  126. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/_utils.py +0 -0
  127. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/cache.py +0 -0
  128. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/data_store.py +0 -0
  129. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/export.py +0 -0
  130. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/src/equity_aggregator/storage/metadata.py +0 -0
  131. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/README.md +0 -0
  132. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/__init__.py +0 -0
  133. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/conftest.py +0 -0
  134. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/__init__.py +0 -0
  135. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/__init__.py +0 -0
  136. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/__init__.py +0 -0
  137. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/__init__.py +0 -0
  138. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
  139. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +0 -0
  140. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
  141. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -0
  142. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
  143. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -0
  144. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/__init__.py +0 -0
  145. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -0
  146. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
  147. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -0
  148. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
  149. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -0
  150. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
  151. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
  152. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/enrichment_feeds/gleif/test_gleif.py +0 -0
  153. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
  154. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -0
  155. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/reference_lookup/__init__.py +0 -0
  156. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/reference_lookup/exchange_rate_api/__init__.py +0 -0
  157. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/reference_lookup/exchange_rate_api/test_exchange_rate_api.py +0 -0
  158. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/reference_lookup/openfigi/__init__.py +0 -0
  159. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/live/adapters/data_sources/reference_lookup/openfigi/test_openfigi.py +0 -0
  160. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/__init__.py +0 -0
  161. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/__init__.py +0 -0
  162. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/_utils/__init__.py +0 -0
  163. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/_utils/test_client.py +0 -0
  164. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/__init__.py +0 -0
  165. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/__init__.py +0 -0
  166. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/__init__.py +0 -0
  167. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/__init__.py +0 -0
  168. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_backoff.py +0 -0
  169. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/_utils/test_parser.py +0 -0
  170. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_intrinio.py +0 -0
  171. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/intrinio/test_session.py +0 -0
  172. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/__init__.py +0 -0
  173. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/__init__.py +0 -0
  174. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_backoff.py +0 -0
  175. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/_utils/test_parser.py +0 -0
  176. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_lseg.py +0 -0
  177. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/lseg/test_session.py +0 -0
  178. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/sec/__init__.py +0 -0
  179. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/sec/test_sec.py +0 -0
  180. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/stock_analysis/test_stock_analysis.py +0 -0
  181. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/__init__.py +0 -0
  182. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/tradingview/test_tradingview.py +0 -0
  183. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/xetra/__init__.py +0 -0
  184. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/discovery_feeds/xetra/test_xetra.py +0 -0
  185. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/__init__.py +0 -0
  186. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/__init__.py +0 -0
  187. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/_helpers.py +0 -0
  188. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_api.py +0 -0
  189. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_download.py +0 -0
  190. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_gleif.py +0 -0
  191. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/gleif/test_parser.py +0 -0
  192. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/__init__.py +0 -0
  193. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_helpers.py +0 -0
  194. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/__init__.py +0 -0
  195. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_backoff.py +0 -0
  196. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/_utils/test_json.py +0 -0
  197. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/__init__.py +0 -0
  198. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_quote_summary.py +0 -0
  199. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/api/test_search.py +0 -0
  200. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_auth.py +0 -0
  201. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_config.py +0 -0
  202. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_ranking.py +0 -0
  203. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_session.py +0 -0
  204. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_transport.py +0 -0
  205. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/enrichment_feeds/yfinance/test_yfinance.py +0 -0
  206. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/reference_lookup/__init__.py +0 -0
  207. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/reference_lookup/test_exchange_rate_api.py +0 -0
  208. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/adapters/data_sources/reference_lookup/test_openfigi.py +0 -0
  209. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/cli/__init__.py +0 -0
  210. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/cli/test_config.py +0 -0
  211. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/cli/test_dispatcher.py +0 -0
  212. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/cli/test_main.py +0 -0
  213. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/cli/test_parser.py +0 -0
  214. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/__init__.py +0 -0
  215. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/_utils/__init__.py +0 -0
  216. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/_utils/test__merge.py +0 -0
  217. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/_utils/test__merge_config.py +0 -0
  218. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/_utils/test__strategies.py +0 -0
  219. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/_utils/test_load_converter.py +0 -0
  220. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/__init__.py +0 -0
  221. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/test_resolve.py +0 -0
  222. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/__init__.py +0 -0
  223. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_canonicalise.py +0 -0
  224. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_convert.py +0 -0
  225. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_enrich.py +0 -0
  226. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_group.py +0 -0
  227. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_identify.py +0 -0
  228. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/pipeline/transforms/test_parse.py +0 -0
  229. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/domain/retrieval/test_retrieval.py +0 -0
  230. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/__init__.py +0 -0
  231. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/__init__.py +0 -0
  232. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_feed_validators.py +0 -0
  233. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_gleif_feed_data.py +0 -0
  234. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_intrinio_feed_data.py +0 -0
  235. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_lseg_feed_data.py +0 -0
  236. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_sec_feed_data.py +0 -0
  237. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_stock_analysis_feed_data.py +0 -0
  238. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_tradingview_feed_data.py +0 -0
  239. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_xetra_feed_data.py +0 -0
  240. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/feeds/test_yfinance_feed_data.py +0 -0
  241. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/test_canonical.py +0 -0
  242. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/test_raw.py +0 -0
  243. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/test_types.py +0 -0
  244. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/schemas/test_validators.py +0 -0
  245. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/__init__.py +0 -0
  246. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/test__utils.py +0 -0
  247. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/test_cache.py +0 -0
  248. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/test_data_store.py +0 -0
  249. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/test_export.py +0 -0
  250. {equity_aggregator-1.0.0 → equity_aggregator-1.0.1}/tests/unit/storage/test_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: equity-aggregator
3
- Version: 1.0.0
3
+ Version: 1.0.1
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "equity-aggregator"
3
- version = "1.0.0"
3
+ version = "1.0.1"
4
4
  description = "Financial equity data aggregation toolkit"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -70,6 +70,14 @@ def _score_quote(
70
70
  `name_key`) and the expected name. Applies minimum score thresholds to prevent
71
71
  matching completely unrelated equities.
72
72
 
73
+ Note:
74
+ Symbol comparison uses `partial_ratio` rather than `ratio` because
75
+ exchange symbols follow a `[prefix]ROOT[.suffix]` pattern (e.g.
76
+ `MELE.BR` vs `2MELE`). `partial_ratio` aligns the shorter string
77
+ against the best-matching substring of the longer one, effectively
78
+ matching on the shared root symbol regardless of exchange-specific
79
+ decoration.
80
+
73
81
  Args:
74
82
  quote (dict): The quote dictionary containing at least a "symbol" key and
75
83
  a name field specified by `name_key`.
@@ -80,12 +88,12 @@ def _score_quote(
80
88
  Returns:
81
89
  tuple[int, str, str]: A tuple of (total_score, actual_symbol, actual_name),
82
90
  where total_score is the sum of the symbol and name fuzzy scores.
83
- Returns (0, symbol, name) if either score is below the minimum threshold.
91
+ Returns (0, symbol, name) if either score is below its minimum threshold.
84
92
  """
85
93
  actual_symbol = quote["symbol"]
86
94
  actual_name = quote.get(name_key, "<no-name>")
87
95
 
88
- symbol_score = fuzz.ratio(
96
+ symbol_score = fuzz.partial_ratio(
89
97
  actual_symbol,
90
98
  expected_symbol,
91
99
  processor=utils.default_process,
@@ -86,7 +86,7 @@ def test_score_quote_total_matches_component_sum() -> None:
86
86
  expected_name="Microsoft Corporation",
87
87
  )
88
88
 
89
- symbol_score = fuzz.ratio(symbol, "MSFT", processor=rf_utils.default_process)
89
+ symbol_score = fuzz.partial_ratio(symbol, "MSFT", processor=rf_utils.default_process)
90
90
  name_score = fuzz.WRatio(
91
91
  name,
92
92
  "Microsoft Corporation",
@@ -94,3 +94,68 @@ def test_score_quote_total_matches_component_sum() -> None:
94
94
  )
95
95
 
96
96
  assert total == symbol_score + name_score
97
+
98
+
99
+ def test_score_quote_accepts_cross_exchange_symbol_variant() -> None:
100
+ """
101
+ ARRANGE: quote with exchange-decorated symbol (MELE.BR) vs prefixed symbol (2MELE)
102
+ ACT: call _score_quote
103
+ ASSERT: total_score is non-zero (shared root symbol matched via partial_ratio)
104
+ """
105
+
106
+ quote = {"symbol": "MELE.BR", "longname": "Melexis NV"}
107
+ total, _, _ = _score_quote(
108
+ quote=quote,
109
+ name_key="longname",
110
+ expected_symbol="2MELE",
111
+ expected_name="MELEXIS NV",
112
+ )
113
+
114
+ assert total > 0
115
+
116
+
117
+ def test_score_quote_rejects_when_name_score_below_threshold() -> None:
118
+ """
119
+ ARRANGE: quote where the name has no similarity to expected name
120
+ ACT: call _score_quote
121
+ ASSERT: total_score is zero (candidate rejected)
122
+ """
123
+
124
+ quote = {"symbol": "MELE.BR", "longname": "Unrelated Corp"}
125
+ total, _, _ = _score_quote(
126
+ quote=quote,
127
+ name_key="longname",
128
+ expected_symbol="2MELE",
129
+ expected_name="MELEXIS NV",
130
+ )
131
+
132
+ assert total == 0
133
+
134
+
135
+ def test_score_quote_substring_symbol_with_similar_name_is_not_rejected() -> None:
136
+ """
137
+ ARRANGE: quote whose symbol is a superstring of the expected symbol
138
+ AND whose name is similar (META/MET, MetLife/Meta Platforms)
139
+ ACT: call _score_quote
140
+ ASSERT: total_score is non-zero — partial_ratio cannot distinguish these
141
+ by symbol alone; the caller's min_score ranking ensures the exact
142
+ match (MET) outscores the substring match (META)
143
+
144
+ Note:
145
+ This is a known limitation of partial_ratio for very short symbols.
146
+ It is not a regression: fuzz.ratio("META", "MET") = 85.7, which also
147
+ clears the score_cutoff=70 gate. The name gate (WRatio) is the primary
148
+ discriminator, but when two companies share a lexical root (Met-) it
149
+ cannot distinguish them either. In practice, ISIN lookups return the
150
+ correct ticker, so the exact match ranks first.
151
+ """
152
+
153
+ quote = {"symbol": "META", "longname": "Meta Platforms Inc"}
154
+ total, _, _ = _score_quote(
155
+ quote=quote,
156
+ name_key="longname",
157
+ expected_symbol="MET",
158
+ expected_name="MetLife Inc",
159
+ )
160
+
161
+ assert total > 0
@@ -160,7 +160,7 @@ wheels = [
160
160
 
161
161
  [[package]]
162
162
  name = "equity-aggregator"
163
- version = "1.0.0"
163
+ version = "1.0.1"
164
164
  source = { editable = "." }
165
165
  dependencies = [
166
166
  { name = "httpx", extra = ["http2"] },
@@ -1,25 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(uv run pytest:*)",
5
- "Bash(uv run:*)",
6
- "Bash(tree:*)",
7
- "Bash(find:*)",
8
- "Bash(sqlite3:*)",
9
- "Bash(xargs basename:*)",
10
- "Bash(xargs cat:*)",
11
- "Bash(cat:*)",
12
- "WebFetch(domain:docs.intrinio.com)",
13
- "WebFetch(domain:mapping.gleif.org)",
14
- "WebSearch",
15
- "WebFetch(domain:www.gleif.org)",
16
- "Bash(python3 -m py_compile src/equity_aggregator/**/*.py)",
17
- "Bash(python3 -m pytest:*)",
18
- "Bash(wc:*)",
19
- "Bash(grep:*)",
20
- "Bash(git -C /Users/gregory.kelleher/Documents/equity-aggregator log --oneline -10)"
21
- ],
22
- "deny": [],
23
- "ask": []
24
- }
25
- }
@@ -1,473 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Debug script to investigate Yahoo Finance API responses for specific tickers.
4
-
5
- This script tests the YFinance endpoints:
6
- 1. search_url - Search for ticker/company name
7
- 2. quote_summary_url - Detailed quote data endpoint
8
- 3. Validation - Test Pydantic validation and show failures
9
-
10
- Usage:
11
- uv run python debug_yfinance.py BKSC
12
- uv run python debug_yfinance.py AAPL
13
- uv run python debug_yfinance.py CMC
14
- """
15
-
16
- import asyncio
17
- import json
18
- import sys
19
-
20
- from pydantic import ValidationError
21
-
22
- from equity_aggregator.adapters.data_sources.enrichment_feeds.yfinance.api.quote_summary import (
23
- _get_quote_summary_fallback,
24
- get_quote_summary,
25
- )
26
- from equity_aggregator.adapters.data_sources.enrichment_feeds.yfinance.config import (
27
- FeedConfig,
28
- )
29
- from equity_aggregator.adapters.data_sources.enrichment_feeds.yfinance.session import (
30
- YFSession,
31
- )
32
- from equity_aggregator.schemas.feeds.yfinance_feed_data import YFinanceFeedData
33
- from equity_aggregator.schemas.raw import RawEquity
34
-
35
-
36
- async def debug_ticker(
37
- ticker: str, query: str | None = None, isin: str | None = None, export: bool = False
38
- ) -> None:
39
- """
40
- Debug Yahoo Finance API endpoints for a specific ticker.
41
-
42
- Args:
43
- ticker: The ticker symbol to query (e.g., "BKSC", "AAPL")
44
- query: Optional search query (defaults to ticker if not provided)
45
- isin: Optional ISIN to test identifier-based lookup flow
46
- export: Whether to export raw API responses to JSON files
47
- """
48
- query = query or ticker
49
-
50
- config = FeedConfig()
51
-
52
- print("=" * 80)
53
- print(f"DEBUGGING TICKER: {ticker}")
54
- print(f"SEARCH QUERY: {query}")
55
- if isin:
56
- print(f"ISIN: {isin}")
57
- print("=" * 80)
58
- print()
59
-
60
- session = YFSession(config)
61
-
62
- try:
63
- # ===================================================================
64
- # TEST 1: Search URL
65
- # ===================================================================
66
- print("📍 TEST 1: SEARCH URL")
67
- print(f" URL: {config.search_url}")
68
- print(f" Query: {query}")
69
- print("-" * 80)
70
-
71
- try:
72
- search_response = await session.get(
73
- config.search_url,
74
- params={"q": query},
75
- )
76
- print(f" ✓ Status Code: {search_response.status_code}")
77
-
78
- search_data = search_response.json()
79
-
80
- # Export search data if requested
81
- if export:
82
- import os
83
-
84
- export_path = "documentation/reference_data/yfinance_search_equity.json"
85
- os.makedirs(os.path.dirname(export_path), exist_ok=True)
86
- with open(export_path, "w") as f:
87
- json.dump(search_data, f, indent=2)
88
- print(f" ✓ Exported to {export_path}")
89
- quotes = search_data.get("quotes", [])
90
- print(f" ✓ Found {len(quotes)} results")
91
-
92
- if quotes:
93
- print("\n Results:")
94
- for i, quote in enumerate(quotes[:5], 1): # Show first 5
95
- symbol = quote.get("symbol", "N/A")
96
- name = quote.get("longName") or quote.get("shortName", "N/A")
97
- quote_type = quote.get("quoteType", "N/A")
98
- exchange = quote.get("exchange", "N/A")
99
- print(f" {i}. {symbol} - {name}")
100
- print(f" Type: {quote_type}, Exchange: {exchange}")
101
- else:
102
- print(" ⚠ No results found")
103
-
104
- except Exception as e:
105
- print(f" ✗ ERROR: {e}")
106
-
107
- print()
108
-
109
- # ===================================================================
110
- # TEST 2: Quote Summary URL (Primary)
111
- # ===================================================================
112
- print("📍 TEST 2: QUOTE SUMMARY URL (PRIMARY)")
113
- print(f" URL: {config.quote_summary_primary_url}{ticker}")
114
- print(f" Modules: {len(config.modules)} modules requested")
115
- print("-" * 80)
116
-
117
- try:
118
- summary_url = config.quote_summary_primary_url + ticker
119
- # Add 30-second timeout to prevent hanging
120
- summary_response = await asyncio.wait_for(
121
- session.get(
122
- summary_url,
123
- params={
124
- "modules": ",".join(config.modules),
125
- "corsDomain": "finance.yahoo.com",
126
- "formatted": "false",
127
- "symbol": ticker,
128
- "lang": "en-US",
129
- "region": "US",
130
- },
131
- ),
132
- timeout=30.0,
133
- )
134
-
135
- print(f" ✓ Status Code: {summary_response.status_code}")
136
- print(
137
- f" ✓ Content-Type: {summary_response.headers.get('content-type', 'N/A')}"
138
- )
139
- print(f" ✓ Content-Length: {len(summary_response.content)} bytes")
140
-
141
- if summary_response.status_code == 200:
142
- summary_data = summary_response.json()
143
-
144
- # Export quote summary data if requested
145
- if export:
146
- import os
147
-
148
- export_path = "documentation/reference_data/yfinance_quote_summary_equity.json"
149
- os.makedirs(os.path.dirname(export_path), exist_ok=True)
150
- with open(export_path, "w") as f:
151
- json.dump(summary_data, f, indent=2)
152
- print(f" ✓ Exported to {export_path}")
153
-
154
- result = summary_data.get("quoteSummary", {}).get("result", [])
155
- error = summary_data.get("quoteSummary", {}).get("error")
156
-
157
- if error:
158
- print(f" ✗ API Error: {error}")
159
- elif result:
160
- print(f" ✓ Data received for {len(result)} ticker(s)")
161
-
162
- # Show available modules
163
- modules_present = list(result[0].keys())
164
- print(
165
- f" ✓ Available modules ({len(modules_present)}): {', '.join(modules_present)}"
166
- )
167
-
168
- # Flatten all module data to get total field count
169
- all_fields = {}
170
- for module_name in modules_present:
171
- module_data = result[0].get(module_name)
172
- if isinstance(module_data, dict):
173
- all_fields.update(module_data)
174
-
175
- print(f" ✓ Total flattened fields: {len(all_fields)}")
176
- print(f"\n All fields from quote_summary_url:")
177
- for field_name in sorted(all_fields.keys()):
178
- value = all_fields[field_name]
179
- # Truncate long values
180
- if isinstance(value, (dict, list)):
181
- value_str = str(type(value).__name__)
182
- else:
183
- value_str = str(value)[:50]
184
- print(f" {field_name}: {value_str}")
185
- else:
186
- print(" ⚠ Empty result array")
187
- print(
188
- f" Raw response: {json.dumps(summary_data, indent=2)[:500]}..."
189
- )
190
- else:
191
- print(f" ✗ Non-200 status code")
192
- print(f" Response preview: {summary_response.text[:500]}...")
193
-
194
- except Exception as e:
195
- print(f" ✗ ERROR: {e}")
196
-
197
- print()
198
-
199
- # ===================================================================
200
- # TEST 3: Quote Summary Fallback URL
201
- # ===================================================================
202
- print("📍 TEST 3: QUOTE SUMMARY FALLBACK URL")
203
- print(f" URL: {config.quote_summary_fallback_url}")
204
- print(f" Ticker: {ticker}")
205
- print("-" * 80)
206
-
207
- try:
208
- fallback_data = await _get_quote_summary_fallback(session, ticker)
209
-
210
- if fallback_data:
211
- print(f" ✓ Data received for ticker")
212
- print(f" ✓ Total fields: {len(fallback_data)}")
213
-
214
- print(f"\n All fields from quote_summary_fallback_url:")
215
- for field_name in sorted(fallback_data.keys()):
216
- value = fallback_data[field_name]
217
- # Truncate long values
218
- if isinstance(value, (dict, list)):
219
- value_str = str(type(value).__name__)
220
- else:
221
- value_str = str(value)[:50]
222
- print(f" {field_name}: {value_str}")
223
-
224
- # Compare with primary endpoint
225
- print(f"\n 📊 FIELD COMPARISON:")
226
- print(f" Primary endpoint would have returned flattened module data")
227
- print(
228
- f" Fallback endpoint returns {len(fallback_data)} direct fields"
229
- )
230
-
231
- except Exception as e:
232
- print(f" ✗ ERROR: {e}")
233
-
234
- print()
235
-
236
- # ===================================================================
237
- # TEST 3.5: ISIN-based Lookup (Pipeline Simulation)
238
- # ===================================================================
239
- if isin:
240
- print("📍 TEST 3.5: ISIN-BASED LOOKUP (PIPELINE SIMULATION)")
241
- print(
242
- " Simulating actual pipeline flow: search by ISIN → get symbol → fetch data"
243
- )
244
- print("-" * 80)
245
-
246
- try:
247
- # Search for the ISIN
248
- isin_search_response = await session.get(
249
- config.search_url,
250
- params={"q": isin},
251
- )
252
- isin_search_data = isin_search_response.json()
253
- isin_quotes = isin_search_data.get("quotes", [])
254
-
255
- # Filter for EQUITY type only (like search_quotes does)
256
- isin_equities = [
257
- q for q in isin_quotes if q.get("quoteType") == "EQUITY"
258
- ]
259
-
260
- print(f" ✓ Searched for ISIN: {isin}")
261
- print(f" ✓ Found {len(isin_equities)} EQUITY results")
262
-
263
- if isin_equities:
264
- print("\n ISIN Search Results:")
265
- for i, quote in enumerate(isin_equities, 1):
266
- symbol = quote.get("symbol", "N/A")
267
- name = quote.get("longname") or quote.get("shortname", "N/A")
268
- exchange = quote.get("exchange", "N/A")
269
- print(f" {i}. {symbol} ({exchange}) - {name}")
270
-
271
- # Pick the first equity (simplified - pipeline does fuzzy matching)
272
- chosen_symbol = isin_equities[0].get("symbol")
273
- print(f"\n → Pipeline would fetch data for: {chosen_symbol}")
274
-
275
- # Now fetch quote summary for this symbol
276
- print(f"\n Fetching quote summary for {chosen_symbol}...")
277
- isin_raw_data = await get_quote_summary(session, chosen_symbol)
278
-
279
- if isin_raw_data:
280
- print(f" ✓ Data fetched: {len(isin_raw_data)} fields")
281
-
282
- # Validate this data
283
- print(f"\n Validating data from ISIN-based lookup...")
284
- try:
285
- yf_data = YFinanceFeedData.model_validate(isin_raw_data)
286
- coerced = yf_data.model_dump()
287
- _ = RawEquity.model_validate(coerced)
288
- print(f" ✓ Validation PASSED for {chosen_symbol}")
289
- except ValidationError as ve:
290
- print(f" ✗ Validation FAILED for {chosen_symbol}")
291
- print(
292
- f"\n ⚠️ THIS IS THE ACTUAL VALIDATION FAILURE IN THE PIPELINE!"
293
- )
294
- print(f"\n Failed fields ({len(ve.errors())}):")
295
- for err in ve.errors():
296
- field_name = err["loc"][0] if err["loc"] else "unknown"
297
- error_msg = err["msg"]
298
-
299
- # Try to get value from either stage
300
- if field_name in coerced:
301
- raw_value = coerced.get(field_name)
302
- else:
303
- raw_value = isin_raw_data.get(field_name)
304
-
305
- print(f"\n ❌ Field: {field_name}")
306
- print(f" Error: {error_msg}")
307
- print(f" Raw value: {raw_value!r}")
308
- print(
309
- f" Value type: {type(raw_value).__name__}"
310
- )
311
-
312
- # Show related fields
313
- if field_name in ["revenue", "revenue_per_share"]:
314
- print(f" Related fields:")
315
- for related in ["totalRevenue", "revenuePerShare"]:
316
- if related in isin_raw_data:
317
- print(
318
- f" {related}: {isin_raw_data[related]!r}"
319
- )
320
- elif field_name == "trailing_pe":
321
- print(f" Related fields:")
322
- for related in ["trailingPE", "forwardPE"]:
323
- if related in isin_raw_data:
324
- print(
325
- f" {related}: {isin_raw_data[related]!r}"
326
- )
327
- else:
328
- print(" ⚠ No EQUITY results found for ISIN")
329
-
330
- except Exception as e:
331
- print(f" ✗ ERROR: {e}")
332
- import traceback
333
-
334
- traceback.print_exc()
335
-
336
- print()
337
-
338
- # ===================================================================
339
- # TEST 4: Validation Testing
340
- # ===================================================================
341
- print("📍 TEST 4: VALIDATION TESTING (Direct Symbol Lookup)")
342
- print(" Testing Pydantic validation against YFinanceFeedData schema")
343
- print("-" * 80)
344
-
345
- try:
346
- # Fetch raw data using the quote_summary function
347
- raw_data = await get_quote_summary(session, ticker)
348
-
349
- if not raw_data:
350
- print(" ⚠ No data returned from get_quote_summary()")
351
- else:
352
- print(f" ✓ Raw data fetched: {len(raw_data)} fields")
353
-
354
- # Step 1: Try validating with YFinanceFeedData
355
- print("\n Step 1: Validating with YFinanceFeedData model...")
356
- try:
357
- yf_data = YFinanceFeedData.model_validate(raw_data)
358
- print(" ✓ YFinanceFeedData validation PASSED")
359
-
360
- # Step 2: Try converting to RawEquity
361
- print("\n Step 2: Converting to RawEquity model...")
362
- try:
363
- coerced = yf_data.model_dump()
364
- _ = RawEquity.model_validate(coerced)
365
- print(" ✓ RawEquity validation PASSED")
366
- print(f" ✓ All validation successful for ticker {ticker}")
367
-
368
- except ValidationError as ve:
369
- print(" ✗ RawEquity validation FAILED")
370
- print(f"\n Failed fields ({len(ve.errors())}):")
371
- for err in ve.errors():
372
- field_name = err["loc"][0] if err["loc"] else "unknown"
373
- error_type = err["type"]
374
- error_msg = err["msg"]
375
-
376
- # Get the actual raw value that failed
377
- raw_value = coerced.get(field_name, "<not found>")
378
-
379
- print(f"\n ❌ Field: {field_name}")
380
- print(f" Error: {error_msg}")
381
- print(f" Type: {error_type}")
382
- print(f" Raw value: {raw_value!r}")
383
- print(f" Value type: {type(raw_value).__name__}")
384
-
385
- except ValidationError as ve:
386
- print(" ✗ YFinanceFeedData validation FAILED")
387
- print(f"\n Failed fields ({len(ve.errors())}):")
388
- for err in ve.errors():
389
- field_name = err["loc"][0] if err["loc"] else "unknown"
390
- error_type = err["type"]
391
- error_msg = err["msg"]
392
-
393
- # Get the actual raw value that failed
394
- raw_value = raw_data.get(field_name, "<not found>")
395
-
396
- print(f"\n ❌ Field: {field_name}")
397
- print(f" Error: {error_msg}")
398
- print(f" Type: {error_type}")
399
- print(f" Raw value: {raw_value!r}")
400
- print(f" Value type: {type(raw_value).__name__}")
401
-
402
- # Show context: what other related fields look like
403
- if field_name in ["revenue", "revenue_per_share"]:
404
- print(f" Related fields:")
405
- for related in [
406
- "totalRevenue",
407
- "revenuePerShare",
408
- "revenue",
409
- "revenue_per_share",
410
- ]:
411
- if related in raw_data:
412
- print(
413
- f" {related}: {raw_data[related]!r}"
414
- )
415
- elif field_name == "trailing_pe":
416
- print(f" Related fields:")
417
- for related in ["trailingPE", "trailing_pe", "forwardPE"]:
418
- if related in raw_data:
419
- print(
420
- f" {related}: {raw_data[related]!r}"
421
- )
422
-
423
- except Exception as e:
424
- print(f" ✗ ERROR: {e}")
425
- import traceback
426
-
427
- traceback.print_exc()
428
-
429
- print()
430
- print("=" * 80)
431
- print("DEBUG COMPLETE")
432
- print("=" * 80)
433
-
434
- finally:
435
- await session.aclose()
436
-
437
-
438
- async def main() -> None:
439
- """Main entry point for the debug script."""
440
- if len(sys.argv) < 2:
441
- print(
442
- "Usage: uv run python debug_yfinance.py TICKER [QUERY] [--isin ISIN] [--export]"
443
- )
444
- print("\nExamples:")
445
- print(" uv run python debug_yfinance.py BKSC")
446
- print(" uv run python debug_yfinance.py AIT --isin GG00BB0RDB98")
447
- print(" uv run python debug_yfinance.py CMC --isin GB00BL6NGV24")
448
- print(" uv run python debug_yfinance.py AAPL --export")
449
- sys.exit(1)
450
-
451
- ticker = sys.argv[1].upper()
452
- query = None
453
- isin = None
454
- export = False
455
-
456
- # Parse optional arguments
457
- i = 2
458
- while i < len(sys.argv):
459
- if sys.argv[i] == "--isin" and i + 1 < len(sys.argv):
460
- isin = sys.argv[i + 1]
461
- i += 2
462
- elif sys.argv[i] == "--export":
463
- export = True
464
- i += 1
465
- else:
466
- query = sys.argv[i]
467
- i += 1
468
-
469
- await debug_ticker(ticker, query, isin, export)
470
-
471
-
472
- if __name__ == "__main__":
473
- asyncio.run(main())