wbfdm 1.55.9__tar.gz → 1.56.0__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.

Potentially problematic release.


This version of wbfdm might be problematic. Click here for more details.

Files changed (369) hide show
  1. {wbfdm-1.55.9 → wbfdm-1.56.0}/PKG-INFO +1 -1
  2. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/dsws/client.py +3 -3
  3. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/dsws/dataloaders/market_data.py +1 -1
  4. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/internal/dataloaders/market_data.py +1 -1
  5. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/backends/base.py +2 -2
  6. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/backends/statistics.py +2 -2
  7. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/exceptions.py +1 -1
  8. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/models.py +6 -0
  9. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/orchestrators.py +4 -4
  10. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/mixins.py +6 -6
  11. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/client.py +3 -0
  12. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/database_routers.py +1 -1
  13. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/adjustments.py +2 -1
  14. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/corporate_actions.py +2 -1
  15. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/market_data.py +2 -2
  16. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/officers.py +1 -1
  17. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/sync/utils.py +14 -9
  18. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dataloaders/protocols.py +1 -1
  19. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/figures/financials/financial_analysis_charts.py +2 -8
  20. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/instrument_prices.py +0 -1
  21. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/instruments.py +1 -1
  22. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/utils/client.py +8 -8
  23. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/utils/controller.py +1 -1
  24. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/instrument.py +17 -9
  25. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/cbinsights/equities.py +2 -3
  26. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/jinja2.py +2 -1
  27. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/esg/controversies.py +3 -0
  28. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/fields.py +2 -2
  29. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/fk_fields.py +3 -3
  30. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/instrument_relationships.py +3 -0
  31. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/instruments.py +9 -9
  32. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/mixin/instruments.py +2 -2
  33. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/options.py +6 -0
  34. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/private_equities.py +3 -0
  35. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +0 -1
  36. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/instruments_relationships.py +3 -1
  37. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/instrument_prices.py +2 -1
  38. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/esg.py +2 -2
  39. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +2 -2
  40. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +1 -1
  41. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/financial_analysis/financial_summary.py +6 -6
  42. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/financial_analysis/statement_with_estimates.py +1 -1
  43. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/financials_analysis.py +9 -12
  44. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/market_data.py +1 -1
  45. {wbfdm-1.55.9 → wbfdm-1.56.0}/.gitignore +0 -0
  46. {wbfdm-1.55.9 → wbfdm-1.56.0}/pyproject.toml +0 -0
  47. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/__init__.py +0 -0
  48. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/__init__.py +0 -0
  49. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/classifications.py +0 -0
  50. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/esg.py +0 -0
  51. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/exchanges.py +0 -0
  52. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/instrument_lists.py +0 -0
  53. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/instrument_prices.py +0 -0
  54. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/instrument_requests.py +0 -0
  55. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/instruments.py +0 -0
  56. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/instruments_relationships.py +0 -0
  57. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/admin/options.py +0 -0
  58. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/__init__.py +0 -0
  59. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/esg/__init__.py +0 -0
  60. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/esg/enums.py +0 -0
  61. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/esg/esg_analysis.py +0 -0
  62. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/esg/utils.py +0 -0
  63. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/__init__.py +0 -0
  64. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/change_point_detection.py +0 -0
  65. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/financial_metric_analysis.py +0 -0
  66. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +0 -0
  67. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +0 -0
  68. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/statement_with_estimates.py +0 -0
  69. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/financial_analysis/utils.py +0 -0
  70. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/technical_analysis/__init__.py +0 -0
  71. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/technical_analysis/technical_analysis.py +0 -0
  72. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/technical_analysis/traces.py +0 -0
  73. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/analysis/utils.py +0 -0
  74. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/apps.py +0 -0
  75. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/backends/dto.py +0 -0
  76. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/__init__.py +0 -0
  77. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/dsws/__init__.py +0 -0
  78. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/internal/__init__.py +0 -0
  79. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/internal/dataloaders/__init__.py +0 -0
  80. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/__init__.py +0 -0
  81. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/admin/__init__.py +0 -0
  82. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/admin/instruments.py +0 -0
  83. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/admin/metrics.py +0 -0
  84. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/apps.py +0 -0
  85. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/backends/__init__.py +0 -0
  86. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/backends/performances.py +0 -0
  87. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/backends/utils.py +0 -0
  88. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/decorators.py +0 -0
  89. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/dispatch.py +0 -0
  90. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/dto.py +0 -0
  91. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/factories.py +0 -0
  92. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/filters.py +0 -0
  93. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/migrations/0001_initial.py +0 -0
  94. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/migrations/0002_remove_instrumentmetric_unique_instrument_metric_and_more.py +0 -0
  95. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/migrations/__init__.py +0 -0
  96. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/registry.py +0 -0
  97. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/serializers.py +0 -0
  98. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/signals.py +0 -0
  99. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tasks.py +0 -0
  100. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/__init__.py +0 -0
  101. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/backends/__init__.py +0 -0
  102. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/backends/test_performances.py +0 -0
  103. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/backends/test_statistics.py +0 -0
  104. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/conftest.py +0 -0
  105. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/test_dto.py +0 -0
  106. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/test_models.py +0 -0
  107. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/test_tasks.py +0 -0
  108. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/tests/test_viewsets.py +0 -0
  109. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/urls.py +0 -0
  110. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/__init__.py +0 -0
  111. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/configs/__init__.py +0 -0
  112. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/configs/display.py +0 -0
  113. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/configs/menus.py +0 -0
  114. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/configs/utils.py +0 -0
  115. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/metric/viewsets/viewsets.py +0 -0
  116. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/__init__.py +0 -0
  117. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/dataloaders/__init__.py +0 -0
  118. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/dataloaders/esg.py +0 -0
  119. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/dataloaders/esg_controversies.py +0 -0
  120. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/sync.py +0 -0
  121. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/tests/__init__.py +0 -0
  122. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/tests/conftest.py +0 -0
  123. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/msci/tests/test_client.py +0 -0
  124. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/__init__.py +0 -0
  125. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/apps.py +0 -0
  126. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/__init__.py +0 -0
  127. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/financials.py +0 -0
  128. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/fx_rates.py +0 -0
  129. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/reporting_dates.py +0 -0
  130. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/statements.py +0 -0
  131. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/dataloaders/utils.py +0 -0
  132. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/companies.sql +0 -0
  133. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/ibes/base_estimates.sql +0 -0
  134. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/ibes/calendarized.sql +0 -0
  135. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/ibes/complete.sql +0 -0
  136. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/ibes/estimates.sql +0 -0
  137. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/ibes/financials.sql +0 -0
  138. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/instruments.sql +0 -0
  139. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/jinja2/qa/sql/quotes.sql +0 -0
  140. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/sync/exchanges.py +0 -0
  141. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/sync/instruments.py +0 -0
  142. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/contrib/qa/tasks.py +0 -0
  143. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dataloaders/__init__.py +0 -0
  144. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dataloaders/cache.py +0 -0
  145. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dataloaders/proxies.py +0 -0
  146. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dataloaders/types.py +0 -0
  147. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/dynamic_preferences_registry.py +0 -0
  148. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/enums.py +0 -0
  149. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/__init__.py +0 -0
  150. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/classifications.py +0 -0
  151. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/controversies.py +0 -0
  152. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/exchanges.py +0 -0
  153. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/instrument_list.py +0 -0
  154. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/instrument_prices.py +0 -0
  155. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/instruments.py +0 -0
  156. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/instruments_relationships.py +0 -0
  157. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/factories/options.py +0 -0
  158. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/figures/__init__.py +0 -0
  159. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/figures/financials/__init__.py +0 -0
  160. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/figures/financials/financials_charts.py +0 -0
  161. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/__init__.py +0 -0
  162. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/classifications.py +0 -0
  163. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/exchanges.py +0 -0
  164. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/financials.py +0 -0
  165. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/financials_analysis.py +0 -0
  166. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/filters/utils.py +0 -0
  167. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/__init__.py +0 -0
  168. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/__init__.py +0 -0
  169. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/__init__.py +0 -0
  170. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/deals.py +0 -0
  171. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/equities.py +0 -0
  172. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/mixin.py +0 -0
  173. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/utils/__init__.py +0 -0
  174. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/cbinsights/utils/classifications.py +0 -0
  175. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/__init__.py +0 -0
  176. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/daily_fundamental.py +0 -0
  177. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/fiscal_period.py +0 -0
  178. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/forecast.py +0 -0
  179. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/fundamental.py +0 -0
  180. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/geographic_segment.py +0 -0
  181. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/instrument.py +0 -0
  182. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/instrument_price.py +0 -0
  183. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/mixin.py +0 -0
  184. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/backends/refinitiv/utils/__init__.py +0 -0
  185. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/__init__.py +0 -0
  186. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/instrument_list.py +0 -0
  187. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/instrument_price.py +0 -0
  188. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/option.py +0 -0
  189. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/handlers/private_equities.py +0 -0
  190. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/__init__.py +0 -0
  191. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/cbinsights/__init__.py +0 -0
  192. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/cbinsights/deals.py +0 -0
  193. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/cbinsights/fundamentals.py +0 -0
  194. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/__init__.py +0 -0
  195. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/daily_fundamental.py +0 -0
  196. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/forecast.py +0 -0
  197. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/fundamental.py +0 -0
  198. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/geographic_segment.py +0 -0
  199. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/instrument.py +0 -0
  200. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/instrument_price.py +0 -0
  201. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/parsers/refinitiv/utils.py +0 -0
  202. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/resources/__init__.py +0 -0
  203. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/resources/classification.py +0 -0
  204. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/resources/instrument_prices.py +0 -0
  205. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/import_export/resources/instruments.py +0 -0
  206. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/de/LC_MESSAGES/django.mo +0 -0
  207. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/de/LC_MESSAGES/django.po +0 -0
  208. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/en/LC_MESSAGES/django.mo +0 -0
  209. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/en/LC_MESSAGES/django.po +0 -0
  210. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/fr/LC_MESSAGES/django.mo +0 -0
  211. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/locale/fr/LC_MESSAGES/django.po +0 -0
  212. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/management/__init__.py +0 -0
  213. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0001_initial.py +0 -0
  214. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0002_rename_statements_instrumentlookup_financials_and_more.py +0 -0
  215. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0003_instrument_estimate_backend_and_more.py +0 -0
  216. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0004_rename_financials_instrumentlookup_statements_and_more.py +0 -0
  217. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0005_instrument_corporate_action_backend.py +0 -0
  218. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0006_instrument_officer_backend.py +0 -0
  219. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0007_instrument_country_instrument_currency_and_more.py +0 -0
  220. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0008_controversy.py +0 -0
  221. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0009_alter_controversy_flag_alter_controversy_initiated_and_more.py +0 -0
  222. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0010_classification_classificationgroup_deal_exchange_and_more.py +0 -0
  223. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0011_delete_instrumentlookup_instrument_corporate_actions_and_more.py +0 -0
  224. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0012_instrumentprice_created_instrumentprice_modified.py +0 -0
  225. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0013_instrument_is_investable_universe_and_more.py +0 -0
  226. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0014_alter_controversy_instrument.py +0 -0
  227. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0015_instrument_instrument_investible_index.py +0 -0
  228. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0016_instrumenttype_name_repr.py +0 -0
  229. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0017_instrument_instrument_security_index.py +0 -0
  230. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0018_instrument_instrument_level_index.py +0 -0
  231. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0019_alter_controversy_source.py +0 -0
  232. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0020_optionaggregate_option_and_more.py +0 -0
  233. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0021_delete_instrumentdailystatistics.py +0 -0
  234. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0022_instrument_cusip_option_open_interest_20d_and_more.py +0 -0
  235. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0023_instrument_unique_ric_instrument_unique_rmc_and_more.py +0 -0
  236. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0024_option_open_interest_10d_option_volume_10d_and_more.py +0 -0
  237. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0025_instrument_is_primary_and_more.py +0 -0
  238. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0026_instrument_is_cash_equivalent.py +0 -0
  239. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0027_remove_instrument_unique_ric_and_more.py +0 -0
  240. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0028_instrumentprice_annualized_daily_volatility.py +0 -0
  241. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0029_alter_instrumentprice_volume.py +0 -0
  242. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0030_alter_relatedinstrumentthroughmodel_related_type.py +0 -0
  243. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0031_exchange_apply_round_lot_size_and_more.py +0 -0
  244. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0032_alter_instrumentprice_outstanding_shares.py +0 -0
  245. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/0033_alter_controversy_review.py +0 -0
  246. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/migrations/__init__.py +0 -0
  247. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/__init__.py +0 -0
  248. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/esg/__init__.py +0 -0
  249. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/exchanges/__init__.py +0 -0
  250. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/exchanges/exchanges.py +0 -0
  251. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/indicators.py +0 -0
  252. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/__init__.py +0 -0
  253. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/classifications.py +0 -0
  254. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/instrument_lists.py +0 -0
  255. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/instrument_prices.py +0 -0
  256. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/instrument_requests.py +0 -0
  257. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/llm/__init__.py +0 -0
  258. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/llm/create_instrument_news_relationships.py +0 -0
  259. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/mixin/__init__.py +0 -0
  260. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/mixin/financials_computed.py +0 -0
  261. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/mixin/financials_serializer_fields.py +0 -0
  262. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/querysets.py +0 -0
  263. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/models/instruments/utils.py +0 -0
  264. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/preferences.py +0 -0
  265. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/__init__.py +0 -0
  266. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/esg.py +0 -0
  267. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/exchanges.py +0 -0
  268. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/__init__.py +0 -0
  269. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/classifications.py +0 -0
  270. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/instrument_lists.py +0 -0
  271. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/instrument_prices.py +0 -0
  272. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/instrument_relationships.py +0 -0
  273. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/instrument_requests.py +0 -0
  274. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/instruments.py +0 -0
  275. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/instruments/mixins.py +0 -0
  276. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/serializers/officers.py +0 -0
  277. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/signals.py +0 -0
  278. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/sync/__init__.py +0 -0
  279. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/sync/abstract.py +0 -0
  280. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/sync/runner.py +0 -0
  281. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tasks.py +0 -0
  282. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/__init__.py +0 -0
  283. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/analysis/__init__.py +0 -0
  284. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/analysis/financial_analysis/__init__.py +0 -0
  285. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/analysis/financial_analysis/test_utils.py +0 -0
  286. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/analysis/test_esg.py +0 -0
  287. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/conftest.py +0 -0
  288. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/dataloaders/__init__.py +0 -0
  289. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/dataloaders/test_cache.py +0 -0
  290. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/__init__.py +0 -0
  291. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_classifications.py +0 -0
  292. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_exchanges.py +0 -0
  293. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_instrument_list.py +0 -0
  294. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_instrument_prices.py +0 -0
  295. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_instruments.py +0 -0
  296. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_merge.py +0 -0
  297. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_options.py +0 -0
  298. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/models/test_queryset.py +0 -0
  299. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/test_tasks.py +0 -0
  300. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/tests/tests.py +0 -0
  301. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/urls.py +0 -0
  302. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/utils.py +0 -0
  303. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/__init__.py +0 -0
  304. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/__init__.py +0 -0
  305. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/buttons/__init__.py +0 -0
  306. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/buttons/classifications.py +0 -0
  307. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/buttons/exchanges.py +0 -0
  308. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/buttons/instrument_prices.py +0 -0
  309. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/buttons/instruments.py +0 -0
  310. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/__init__.py +0 -0
  311. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/classifications.py +0 -0
  312. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/esg.py +0 -0
  313. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/exchanges.py +0 -0
  314. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/financial_summary.py +0 -0
  315. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/instrument_lists.py +0 -0
  316. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/instrument_prices.py +0 -0
  317. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/instrument_requests.py +0 -0
  318. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/instruments.py +0 -0
  319. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/monthly_performances.py +0 -0
  320. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/officers.py +0 -0
  321. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/prices.py +0 -0
  322. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/statement_with_estimates.py +0 -0
  323. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/display/statements.py +0 -0
  324. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/__init__.py +0 -0
  325. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/classifications.py +0 -0
  326. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/esg.py +0 -0
  327. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/exchanges.py +0 -0
  328. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/financials_analysis.py +0 -0
  329. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/instrument_lists.py +0 -0
  330. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/instrument_prices.py +0 -0
  331. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/instrument_requests.py +0 -0
  332. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/instruments.py +0 -0
  333. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/instruments_relationships.py +0 -0
  334. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/endpoints/statements.py +0 -0
  335. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/__init__.py +0 -0
  336. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/classifications.py +0 -0
  337. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/exchanges.py +0 -0
  338. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/instrument_lists.py +0 -0
  339. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/instruments.py +0 -0
  340. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/menus/instruments_relationships.py +0 -0
  341. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/__init__.py +0 -0
  342. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/classifications.py +0 -0
  343. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/esg.py +0 -0
  344. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/exchanges.py +0 -0
  345. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/financial_ratio_analysis.py +0 -0
  346. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/financials_analysis.py +0 -0
  347. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/instrument_requests.py +0 -0
  348. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/instruments.py +0 -0
  349. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/instruments_relationships.py +0 -0
  350. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/market_data.py +0 -0
  351. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/prices.py +0 -0
  352. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/configs/titles/statement_with_estimates.py +0 -0
  353. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/exchanges.py +0 -0
  354. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/financial_analysis/__init__.py +0 -0
  355. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/__init__.py +0 -0
  356. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/classifications.py +0 -0
  357. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/instrument_lists.py +0 -0
  358. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/instrument_prices.py +0 -0
  359. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/instrument_requests.py +0 -0
  360. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/instruments.py +0 -0
  361. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/instruments_relationships.py +0 -0
  362. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/instruments/utils.py +0 -0
  363. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/mixins.py +0 -0
  364. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/officers.py +0 -0
  365. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/prices.py +0 -0
  366. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/statements/__init__.py +0 -0
  367. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/statements/statements.py +0 -0
  368. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/technical_analysis/__init__.py +0 -0
  369. {wbfdm-1.55.9 → wbfdm-1.56.0}/wbfdm/viewsets/technical_analysis/monthly_performances.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbfdm
3
- Version: 1.55.9
3
+ Version: 1.56.0
4
4
  Summary: The workbench module ensures rapid access to diverse financial data (market, fundamental, forecasts, ESG), with features for storing instruments, classifying them, and conducting financial analysis.
5
5
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
6
6
  Requires-Dist: roman==4.*
@@ -3,7 +3,7 @@ import re
3
3
  from datetime import date, datetime
4
4
  from typing import Generator, List, Optional
5
5
 
6
- import DatastreamPy as dsweb
6
+ import DatastreamPy as dsweb # noqa
7
7
  import numpy as np
8
8
  import pandas as pd
9
9
  import pytz
@@ -13,7 +13,7 @@ from wbcore.contrib.currency.models import Currency, CurrencyFXRates
13
13
 
14
14
 
15
15
  class CachedTokenDataClient(dsweb.DataClient):
16
- def _get_token(self, isProxy=False):
16
+ def _get_token(self, isProxy=False): # noqa
17
17
  if (token := cache.get("dsws_token")) and (token_expiry := cache.get("dsws_token_expiry")):
18
18
  self.token = token
19
19
  self.tokenExpiry = timezone.make_aware(datetime.fromtimestamp(token_expiry), timezone=pytz.UTC)
@@ -102,7 +102,7 @@ class Client:
102
102
  if len(requests_data) > self.MAXIMUM_REQUESTS_PER_BUNDLE:
103
103
  raise ValueError(f"number of request exceed {self.MAXIMUM_REQUESTS_PER_BUNDLE}")
104
104
  # Construct the requests bundle
105
- for request_tickers, request_symbols in requests_data:
105
+ for request_tickers, _ in requests_data:
106
106
  # Convert a list of string into a valid string
107
107
  converted_ticker = ",".join(request_tickers)
108
108
  if "start" in extra_client_kwargs or "end" in extra_client_kwargs:
@@ -30,7 +30,7 @@ FIELD_MAP = {
30
30
  class DSWSMarketDataDataloader(MarketDataProtocol, Dataloader):
31
31
  def market_data(
32
32
  self,
33
- values: list[MarketData] = [MarketData.CLOSE],
33
+ values: list[MarketData] | None = None,
34
34
  from_date: date | None = None,
35
35
  to_date: date | None = None,
36
36
  exact_date: date | None = None,
@@ -36,7 +36,7 @@ def _cast_decimal_to_float(value: float | Decimal) -> float:
36
36
  class MarketDataDataloader(MarketDataProtocol, Dataloader):
37
37
  def market_data(
38
38
  self,
39
- values: list[MarketData] = [MarketData.CLOSE],
39
+ values: list[MarketData] | None = None,
40
40
  from_date: date | None = None,
41
41
  to_date: date | None = None,
42
42
  exact_date: date | None = None,
@@ -13,7 +13,7 @@ from wbcore.contrib.currency.models import CurrencyFXRates
13
13
  from wbfdm.models import Instrument, InstrumentPrice
14
14
 
15
15
  from ..dto import Metric, MetricField, MetricKey
16
- from ..exceptions import MetricInvalidParameterException
16
+ from ..exceptions import MetricInvalidParameterError
17
17
  from .utils import get_today
18
18
 
19
19
  T = TypeVar("T", bound=Model)
@@ -94,7 +94,7 @@ class InstrumentMetricBaseBackend(AbstractBackend[Instrument]):
94
94
  [val_date, (get_today() - pd.tseries.offsets.BDay(1)).date()]
95
95
  ) # ensure that value date is at least lower than today (otherwise, we might compute performance for intraday, which we do not want yet
96
96
  else:
97
- raise MetricInvalidParameterException()
97
+ raise MetricInvalidParameterError()
98
98
 
99
99
 
100
100
  class BaseDataloader:
@@ -11,7 +11,7 @@ from wbfdm.models import Instrument, InstrumentPrice
11
11
 
12
12
  from ..decorators import register
13
13
  from ..dto import Metric, MetricField, MetricKey
14
- from ..exceptions import MetricInvalidParameterException
14
+ from ..exceptions import MetricInvalidParameterError
15
15
  from .base import BaseDataloader, InstrumentMetricBaseBackend
16
16
 
17
17
  STATISTICS_METRIC = MetricKey(
@@ -207,7 +207,7 @@ class InstrumentFinancialStatisticsMetricBackend(InstrumentMetricBaseBackend):
207
207
  elif self.val_date:
208
208
  with suppress(InstrumentPrice.DoesNotExist):
209
209
  return instrument.valuations.filter(date__lte=self.val_date).latest("date").date
210
- raise MetricInvalidParameterException()
210
+ raise MetricInvalidParameterError()
211
211
 
212
212
 
213
213
  @register(move_first=True)
@@ -1,4 +1,4 @@
1
- class MetricInvalidParameterException(Exception):
1
+ class MetricInvalidParameterError(Exception):
2
2
  """
3
3
  If instantiated backend parameters are invalid, the orchestrator or backend itself are expected to raise that exception
4
4
  """
@@ -48,6 +48,12 @@ class InstrumentMetric(models.Model):
48
48
  self.basket_repr = str(self.basket)
49
49
  super().save(*args, **kwargs)
50
50
 
51
+ def __str__(self) -> str:
52
+ repr = f"{self.basket} - {self.key}"
53
+ if self.date:
54
+ repr += f"({self.date})"
55
+ return repr
56
+
51
57
  @classmethod
52
58
  def update_or_create_from_metric(cls, metric: Metric, parent_instrument_metric: Self | None = None):
53
59
  """
@@ -8,7 +8,7 @@ from tqdm import tqdm
8
8
 
9
9
  from .backends.base import AbstractBackend
10
10
  from .dto import Metric
11
- from .exceptions import MetricInvalidParameterException
11
+ from .exceptions import MetricInvalidParameterError
12
12
  from .models import InstrumentMetric
13
13
  from .registry import backend_registry
14
14
 
@@ -73,9 +73,9 @@ class MetricOrchestrator:
73
73
  if debug:
74
74
  # if debug mode is enabled, we wrap the parameters list into a tqdm generator
75
75
  parameters = tqdm(parameters)
76
- for parameters in parameters:
77
- with suppress(MetricInvalidParameterException):
78
- yield from parameters[0].compute_metrics(parameters[1])
76
+ for param in parameters:
77
+ with suppress(MetricInvalidParameterError):
78
+ yield from param[0].compute_metrics(param[1])
79
79
 
80
80
  def process(self, debug: bool = False):
81
81
  """
@@ -185,19 +185,19 @@ class InstrumentMetricMixin(_Base):
185
185
  serializer_class = self.get_serializer_class()
186
186
  kwargs.setdefault("context", self.get_serializer_context())
187
187
 
188
- BaseMeta = serializer_class.Meta
189
- fields = list(getattr(BaseMeta, "fields", ()))
190
- read_only_fields = list(getattr(BaseMeta, "read_only_fields", ()))
188
+ base_meta = serializer_class.Meta
189
+ fields = list(getattr(base_meta, "fields", ()))
190
+ read_only_fields = list(getattr(base_meta, "read_only_fields", ()))
191
191
  for extra_field in self._metric_serializer_fields.keys():
192
192
  fields.append(extra_field)
193
193
  read_only_fields.append(extra_field)
194
194
 
195
- Meta = type(str("Meta"), (BaseMeta,), {"fields": fields, "read_only_fields": read_only_fields})
195
+ meta = type(str("Meta"), (base_meta,), {"fields": fields, "read_only_fields": read_only_fields})
196
196
  new_class = type(
197
197
  serializer_class.__name__,
198
198
  (serializer_class,),
199
199
  {
200
- "Meta": Meta,
200
+ "Meta": meta,
201
201
  **self._metric_serializer_fields,
202
202
  "SERIALIZER_CLASS_FOR_REMOTE_ADDITIONAL_RESOURCES": serializer_class,
203
203
  },
@@ -233,7 +233,7 @@ class InstrumentMetricMixin(_Base):
233
233
  # for all the missings keys (not present in the aggregates already), we compute the aggregatation based on the aggregate function given by the MetricField class
234
234
  missing_aggregate_map = {}
235
235
  for metric_key in self.metric_keys:
236
- for field_key, field_filter in metric_key.subfields_filter_map.items():
236
+ for field_key in metric_key.subfields_filter_map.keys():
237
237
  if field_key in self._metric_serializer_fields.keys() and field_key not in aggregates:
238
238
  missing_aggregate_map[field_key] = metric_key.subfields_map[field_key]
239
239
  missing_aggregate = queryset.aggregate(
@@ -38,6 +38,7 @@ class MSCIClient:
38
38
  "grant_type": "client_credentials",
39
39
  "audience": "https://esg/data",
40
40
  },
41
+ timeout=10,
41
42
  )
42
43
  if resp.status_code == requests.codes.ok:
43
44
  with suppress(KeyError, requests.exceptions.JSONDecodeError):
@@ -58,6 +59,7 @@ class MSCIClient:
58
59
  "factor_name_list": factors,
59
60
  },
60
61
  headers={"AUTHORIZATION": f"Bearer {self.oauth_token}"},
62
+ timeout=10,
61
63
  )
62
64
  if response.ok:
63
65
  for row in response.json().get("result", {}).get("issuers", []):
@@ -78,6 +80,7 @@ class MSCIClient:
78
80
  "offset": offset,
79
81
  },
80
82
  headers={"AUTHORIZATION": f"Bearer {self.oauth_token}"},
83
+ timeout=10,
81
84
  )
82
85
 
83
86
  if not response.ok:
@@ -20,6 +20,6 @@ class QARouter:
20
20
  return None
21
21
 
22
22
  def allow_migrate(self, db, app_label, model_name=None, **hints):
23
- if app_label == "qa":
23
+ if db == "qa":
24
24
  return False
25
25
  return None
@@ -57,7 +57,8 @@ class DatastreamAdjustmentsDataloader(AdjustmentsProtocol, Dataloader):
57
57
  pk.MSSQLQuery.create_table(infocode).columns(pk.Column("infocode", SqlTypes.INTEGER)).get_sql()
58
58
  )
59
59
  for batch in batched(lookup.keys(), 1000):
60
- cursor.execute(f"insert into #ds2infocode values {",".join(map(lambda x: f"({x})", batch))};")
60
+ placeholders = ",".join(map(lambda x: f"({x})", batch))
61
+ cursor.execute("insert into #ds2infocode values %s;", (placeholders,))
61
62
 
62
63
  cursor.execute(query.get_sql())
63
64
 
@@ -58,7 +58,8 @@ class DatastreamCorporateActionsDataloader(CorporateActionsProtocol, Dataloader)
58
58
  pk.MSSQLQuery.create_table(infocode).columns(pk.Column("infocode", SqlTypes.INTEGER)).get_sql()
59
59
  )
60
60
  for batch in batched(lookup.keys(), 1000):
61
- cursor.execute(f"insert into #ds2infocode values {','.join(map(lambda x: f'({x})', batch))};")
61
+ placeholders = ",".join(map(lambda x: f"({x})", batch))
62
+ cursor.execute("insert into #ds2infocode values %s;", (placeholders,))
62
63
 
63
64
  cursor.execute(query.get_sql())
64
65
  for row in dictfetchall(cursor, CorporateActionDataDict):
@@ -39,9 +39,9 @@ class DS2MarketData(Enum):
39
39
 
40
40
 
41
41
  class DatastreamMarketDataDataloader(MarketDataProtocol, Dataloader):
42
- def market_data(
42
+ def market_data( # noqa: C901
43
43
  self,
44
- values: list[MarketData] = [MarketData.CLOSE],
44
+ values: list[MarketData] | None = None,
45
45
  from_date: date | None = None,
46
46
  to_date: date | None = None,
47
47
  exact_date: date | None = None,
@@ -60,7 +60,7 @@ class RKDOfficersDataloader(OfficersProtocol, Dataloader):
60
60
  )
61
61
  for batch in batched(lookup.keys(), 1000):
62
62
  placeholders = ",".join(map(lambda x: f"('{x}')", batch))
63
- cursor.execute(f"insert into #rkd_codes values {placeholders};")
63
+ cursor.execute("insert into #rkd_codes values %s;", (placeholders,))
64
64
 
65
65
  cursor.execute(query.get_sql())
66
66
 
@@ -148,12 +148,15 @@ def _delist_existing_duplicates(instrument: Instrument) -> None:
148
148
 
149
149
  for identifier_field in unique_identifiers:
150
150
  if identifier := getattr(instrument, identifier_field):
151
- with suppress(Instrument.DoesNotExist):
152
- duplicate = Instrument.objects.get(
153
- is_security=True, delisted_date__isnull=True, **{identifier_field: identifier}
154
- )
155
- duplicate.delisted_date = date.today() - timedelta(days=1)
156
- duplicate.save()
151
+ if instrument.delisted_date: # if delisted, we unset the identifier that can lead to constraint error
152
+ setattr(instrument, identifier_field, None)
153
+ else:
154
+ with suppress(Instrument.DoesNotExist):
155
+ duplicate = Instrument.objects.get(
156
+ is_security=True, delisted_date__isnull=True, **{identifier_field: identifier}
157
+ )
158
+ duplicate.delisted_date = date.today() - timedelta(days=1)
159
+ duplicate.save()
157
160
 
158
161
 
159
162
  def _save_single_instrument(instrument: Instrument) -> None:
@@ -211,7 +214,7 @@ def _bulk_create_instruments_chunk(instruments: list[Instrument], update_unique_
211
214
  )
212
215
  except IntegrityError:
213
216
  # we caught an integrity error on the bulk save, so we try to save one by one
214
- logger.info(
217
+ logger.error(
215
218
  "we detected an integrity error while bulk saving instruments. We save them one by one and delist the already existing instrument from the db if we can. "
216
219
  )
217
220
  for instrument in instruments:
@@ -290,14 +293,16 @@ def trigger_partial_update(
290
293
  else:
291
294
  with connections["qa"].cursor() as cursor:
292
295
  cursor.execute(
293
- f"SELECT MAX(last_user_update) FROM sys.dm_db_index_usage_stats WHERE OBJECT_NAME(object_id) = '{table_change_name}'"
296
+ "SELECT MAX(last_user_update) FROM sys.dm_db_index_usage_stats WHERE OBJECT_NAME(object_id) = %S'",
297
+ (table_change_name,),
294
298
  )
295
299
  max_last_updated_qa = (
296
300
  pytz.timezone(settings.TIME_ZONE).localize(result[0]) if (result := cursor.fetchone()) else None
297
301
  )
298
302
  if max_last_updated_qa and max_last_updated_qa > max_last_updated:
299
303
  for _, security_id in cursor.execute(
300
- f"SELECT UpdateFlag_, {id_field} FROM {table_change_name}"
304
+ "SELECT UpdateFlag_, %s FROM %s",
305
+ (id_field, table_change_name),
301
306
  ).fetchall():
302
307
  try:
303
308
  update_or_create_item(security_id)
@@ -49,7 +49,7 @@ class FXRateProtocol(Protocol):
49
49
  class MarketDataProtocol(Protocol):
50
50
  def market_data(
51
51
  self,
52
- values: list[MarketData] = [MarketData.CLOSE],
52
+ values: list[MarketData] | None = None,
53
53
  from_date: date | None = None,
54
54
  to_date: date | None = None,
55
55
  exact_date: date | None = None,
@@ -115,13 +115,7 @@ class FinancialAnalysisGenerator:
115
115
  self.instruments_repr_map = {i.id: i.name_repr for i in self.instruments}
116
116
  self.currency_map = {i.id: i.currency.id for i in self.instruments}
117
117
 
118
- def build_df(
119
- self,
120
- instrument_prices_field_names: list[str] = [],
121
- fundamental_field_names: list[str] = [],
122
- forecast_field_names: list[str] = [],
123
- daily_fundamental_field_names: list[str] = [],
124
- ):
118
+ def build_df(self, **kwargs):
125
119
  """
126
120
  Used to returns a df with all the variables passed in four separate lists
127
121
 
@@ -239,7 +233,7 @@ class FinancialAnalysisGenerator:
239
233
  @staticmethod
240
234
  def clean_data(
241
235
  df: pd.DataFrame,
242
- var_list: list[str] = [],
236
+ var_list: list[str],
243
237
  drop_negative=True,
244
238
  q_low: float = 0.05,
245
239
  q_high: float = 0.95,
@@ -26,7 +26,6 @@ class FakeDateRange(wb_filters.FilterSet):
26
26
 
27
27
  class InstrumentPriceFilterSet(wb_filters.FilterSet):
28
28
  date = wb_filters.FinancialPerformanceDateRangeFilter(
29
- method=wb_filters.DateRangeFilter.base_date_range_filter_method,
30
29
  label="Date Range",
31
30
  required=True,
32
31
  clearable=False,
@@ -138,7 +138,7 @@ class InstrumentFilterSet(TagFilterMixin, InstrumentFavoriteGroupFilterSet):
138
138
  if "parent" in data:
139
139
  data.pop("classifications", None) # remove classifications in case we are navigating the tree
140
140
  data.pop("level", None)
141
- super().__init__(data=data, *args, **kwargs)
141
+ super().__init__(*args, data=data, **kwargs)
142
142
 
143
143
  class Meta:
144
144
  model = Instrument
@@ -7,11 +7,11 @@ from celery import shared_task
7
7
  from tqdm import tqdm
8
8
 
9
9
 
10
- class RateLimitException(Exception):
10
+ class RateLimitError(Exception):
11
11
  pass
12
12
 
13
13
 
14
- class CreditLimitException(Exception):
14
+ class CreditLimitError(Exception):
15
15
  pass
16
16
 
17
17
 
@@ -38,17 +38,18 @@ class Client:
38
38
  url,
39
39
  params={**params, **{"nextPageToken": next_page_token}} if next_page_token else params,
40
40
  headers={"authorization": self.jwt_header_value},
41
+ timeout=10,
41
42
  )
42
43
  if resp.status_code != 200:
43
44
  if resp.status_code == 429:
44
- raise RateLimitException()
45
+ raise RateLimitError()
45
46
  raise requests.ConnectionError(
46
47
  f"unexpected error from api\nstatus_code: {resp.status_code}\nerror: {resp.text}"
47
48
  )
48
49
 
49
50
  if credits_remaining_str := resp.headers.get("x-cbinsights-credits-remaining", None):
50
51
  if int(credits_remaining_str) <= 0:
51
- raise CreditLimitException()
52
+ raise CreditLimitError()
52
53
 
53
54
  return resp.json()
54
55
 
@@ -72,11 +73,11 @@ class Client:
72
73
  resp = self._request(data_url, params=params, next_page_token=next_page_token)
73
74
  yield resp
74
75
  next_page_token = resp["nextPageToken"]
75
- except RateLimitException:
76
+ except RateLimitError:
76
77
  time.sleep(self.rate_limit_sleep)
77
78
  retry += 1
78
79
  if retry >= 5:
79
- raise RateLimitException()
80
+ raise RateLimitError()
80
81
 
81
82
  def _chunk_paginated_request(self, data_url, org_ids, extra_params, endpoint, debug: bool = False):
82
83
  data = []
@@ -96,8 +97,7 @@ class Client:
96
97
  auth_url = "https://api.cbinsights.com/v1/authorize"
97
98
 
98
99
  auth_resp = requests.get(
99
- auth_url,
100
- params={"clientId": self.client_id, "clientSecret": self.client_secret},
100
+ auth_url, params={"clientId": self.client_id, "clientSecret": self.client_secret}, timeout=10
101
101
  )
102
102
 
103
103
  if auth_resp.status_code != 200:
@@ -61,7 +61,7 @@ class Controller:
61
61
  instrument_ric: str = None,
62
62
  instrument_isin: str = None,
63
63
  instrument_mnemonic: str = None,
64
- perm_id_symbols: list[str] = ["QPID", "IPID"],
64
+ perm_id_symbols: tuple[str, ...] = ("QPID", "IPID"),
65
65
  ) -> str | None:
66
66
  def _process_ticker(ticker):
67
67
  if not (df := self.client.get_static_df(tickers=[ticker], fields=perm_id_symbols)).empty:
@@ -46,7 +46,7 @@ class InstrumentLookup:
46
46
  cache_key = self._get_cache_key(**kwargs)
47
47
  self.cache[cache_key] = instrument
48
48
 
49
- def _lookup_security(
49
+ def _lookup_security( # noqa: C901
50
50
  self,
51
51
  instrument_type=None,
52
52
  currency=None,
@@ -57,7 +57,6 @@ class InstrumentLookup:
57
57
  **identifiers,
58
58
  ):
59
59
  identifiers = {k: v for k, v in identifiers.items() if v is not None}
60
-
61
60
  instrument = None
62
61
 
63
62
  # We need to lookup ticker because some provider gives us ticker with or without space in it
@@ -113,33 +112,41 @@ class InstrumentLookup:
113
112
  conditions.append(Q(**{f"{field}": field_value}))
114
113
  if field == "isin":
115
114
  conditions.append(Q(old_isins__contains=[field_value]))
115
+ if field == "ticker":
116
+ conditions.append(Q(ticker__regex=rf"^{field_value}([A-Za-z]?|\.?[A-Za-z])$"))
117
+ conditions.append(Q(refinitiv_mnemonic_code=f"@{field_value}"))
118
+
116
119
  if conditions:
117
120
  instruments = instruments.filter(reduce(operator.or_, conditions))
118
-
119
- if currency: # if currency is provides, we use it as validator
121
+ if (
122
+ currency and instruments.filter(currency=currency).exists()
123
+ ): # if currency is provides, we use it as validator
120
124
  instruments = instruments.filter(currency=currency)
121
-
122
125
  if exchange:
123
126
  instruments_tmp = instruments.filter(exchange=exchange)
124
127
  if instruments_tmp.exists():
125
128
  instruments = instruments_tmp
129
+
126
130
  # last chance
127
131
  if name and instruments.count() > 1:
128
132
  instruments = instruments.annotate(similarity_score=TrigramSimilarity("name", name))
129
133
  if instruments.filter(similarity_score__gt=self.trigram_similarity_min_score).count() == 1:
130
134
  instruments = instruments.filter(similarity_score__gt=self.trigram_similarity_min_score)
135
+
131
136
  if instruments.count() == 1:
132
137
  instrument = instruments.first()
133
- elif instrument_type and identifiers:
138
+ elif instrument_type and (name or identifiers):
134
139
  # if instrument type was provided but we still didn't find the security, we try without the instrument type in case it was mislabeled
135
140
  instrument = self._lookup_security(
136
141
  only_investable_universe=only_investable_universe,
137
142
  exact_lookup=exact_lookup,
138
143
  currency=currency,
139
144
  exchange=exchange,
145
+ name=name,
140
146
  **identifiers,
141
147
  )
142
- if not instrument and name and identifiers:
148
+ if not instrument and identifiers:
149
+ identifiers.pop(list(identifiers.keys())[0])
143
150
  # Sometime, identifier provided emptied the queryset of possible instruments. In a last chance approach, we try to only look for security with the given name
144
151
  instrument = self._lookup_security(
145
152
  only_investable_universe=only_investable_universe,
@@ -148,6 +155,7 @@ class InstrumentLookup:
148
155
  currency=currency,
149
156
  exchange=exchange,
150
157
  name=name,
158
+ **identifiers,
151
159
  )
152
160
  if instrument and only_investable_universe and not instrument.is_security and instrument.parent:
153
161
  instrument = instrument.parent
@@ -237,8 +245,8 @@ class InstrumentImportHandler(ImportExportHandler):
237
245
  if instrument_id := data.pop("id", None):
238
246
  try:
239
247
  return self.model.objects.get(id=instrument_id)
240
- except self.model.DoesNotExist:
241
- raise DeserializationError("Instrument id does not match an existing instrument")
248
+ except self.model.DoesNotExist as e:
249
+ raise DeserializationError("Instrument id does not match an existing instrument") from e
242
250
  else:
243
251
  return self.instrument_lookup.lookup(only_security=only_security, **data)
244
252
 
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from contextlib import suppress
2
3
 
3
4
  from wbcore.contrib.geography.models import Geography
4
5
 
@@ -44,14 +45,12 @@ def parse(import_source):
44
45
  d["headquarter_address"] = f"{street}, {postal_code} {city_name}"
45
46
 
46
47
  if sector_id := summary.get("sectorId", None):
47
- try:
48
+ with suppress(Exception):
48
49
  code_aggregated = f"{int(sector_id):03}"
49
50
  if industry_id := summary.get("industryId", None):
50
51
  code_aggregated += f"{int(industry_id):03}"
51
52
  if subindustry_id := summary.get("subindustryId", None):
52
53
  code_aggregated += f"{int(subindustry_id):03}"
53
- except Exception:
54
- pass
55
54
  d["classifications"] = [{"code_aggregated": code_aggregated, "group": cbinsight_group.id}]
56
55
  data.append(d)
57
56
  return {"data": data}
@@ -3,5 +3,6 @@ from jinjasql import JinjaSql # type: ignore
3
3
 
4
4
 
5
5
  def get_environment(**options):
6
- env = Environment(**options)
6
+ # we except only SQL template, so we need to not escape special characters (possible XSS attack for HTML template)
7
+ env = Environment(**options) # noqa: S701
7
8
  return JinjaSql(env=env, param_style="format").env
@@ -56,6 +56,9 @@ class Controversy(models.Model):
56
56
  review = models.DateField(verbose_name="Reviewed", null=True, blank=True)
57
57
  initiated = models.DateField(verbose_name="initiated", null=True, blank=True)
58
58
 
59
+ def __str__(self) -> str:
60
+ return f"{self.headline} ({self.instrument})"
61
+
59
62
  @classmethod
60
63
  def dict_to_model(cls, controversy: dict[str, Any], instrument) -> Self:
61
64
  return Controversy(
@@ -23,7 +23,7 @@ class CompositeKey(models.AutoField):
23
23
  def __init__(self, columns: list[str], db_column_ref: str | None = None, *args, **kwargs):
24
24
  self.columns = columns
25
25
  self.db_column_ref = db_column_ref
26
- super().__init__(primary_key=True, *args, **kwargs)
26
+ super().__init__(*args, primary_key=True, **kwargs)
27
27
 
28
28
  def contribute_to_class(self, cls, name, private_only=False):
29
29
  self.set_attributes_from_name(name)
@@ -108,7 +108,7 @@ class Exact(models.Lookup):
108
108
 
109
109
  lookups = [
110
110
  lookup_class(field.get_col(self.lhs.alias), self.rhs[column])
111
- for lookup_class, field, column in zip(lookup_classes, fields, self.lhs.field.columns)
111
+ for lookup_class, field, column in zip(lookup_classes, fields, self.lhs.field.columns, strict=False)
112
112
  ]
113
113
 
114
114
  value_constraint = WhereNode()
@@ -93,7 +93,7 @@ class CompositeForeignKey(ForeignObject):
93
93
  kwargs["on_delete"] = self.override_on_delete(kwargs["on_delete"])
94
94
 
95
95
  kwargs["to_fields"], kwargs["from_fields"] = zip(
96
- *((k, v.value) for k, v in self._raw_fields.items() if v.is_local_field)
96
+ *((k, v.value) for k, v in self._raw_fields.items() if v.is_local_field), strict=False
97
97
  )
98
98
  super().__init__(to, **kwargs)
99
99
 
@@ -220,7 +220,7 @@ class CompositeForeignKey(ForeignObject):
220
220
 
221
221
  def _check_nullifequal_fields_exists(self):
222
222
  res = []
223
- for field_name, value in self.null_if_equal:
223
+ for field_name, _ in self.null_if_equal:
224
224
  try:
225
225
  self.model._meta.get_field(field_name)
226
226
  except FieldDoesNotExist:
@@ -269,7 +269,7 @@ class CompositeForeignKey(ForeignObject):
269
269
 
270
270
  return OrderedDict(
271
271
  (k, (v if isinstance(v, CompositePart) else LocalFieldValue(v)))
272
- for k, v in (to_fields.items() if isinstance(to_fields, dict) else zip(to_fields, to_fields))
272
+ for k, v in (to_fields.items() if isinstance(to_fields, dict) else zip(to_fields, to_fields, strict=False))
273
273
  )
274
274
 
275
275
  def db_type(self, connection):
@@ -162,6 +162,9 @@ class RelatedInstrumentThroughModel(models.Model):
162
162
  class Meta:
163
163
  unique_together = ("instrument", "related_instrument", "is_primary", "related_type")
164
164
 
165
+ def __str__(self) -> str:
166
+ return f"{self.instrument} - {self.related_instrument} ({self.related_type})"
167
+
165
168
  def save(self, *args, **kwargs):
166
169
  qs = RelatedInstrumentThroughModel.objects.filter(
167
170
  instrument=self.instrument, related_type=self.related_type, is_primary=True