wbfdm 2.2.1__py2.py3-none-any.whl

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 (337) hide show
  1. wbfdm/__init__.py +2 -0
  2. wbfdm/admin/__init__.py +42 -0
  3. wbfdm/admin/classifications.py +39 -0
  4. wbfdm/admin/esg.py +23 -0
  5. wbfdm/admin/exchanges.py +53 -0
  6. wbfdm/admin/instrument_lists.py +23 -0
  7. wbfdm/admin/instrument_prices.py +62 -0
  8. wbfdm/admin/instrument_requests.py +33 -0
  9. wbfdm/admin/instruments.py +117 -0
  10. wbfdm/admin/instruments_relationships.py +25 -0
  11. wbfdm/admin/options.py +101 -0
  12. wbfdm/analysis/__init__.py +2 -0
  13. wbfdm/analysis/esg/__init__.py +0 -0
  14. wbfdm/analysis/esg/enums.py +82 -0
  15. wbfdm/analysis/esg/esg_analysis.py +217 -0
  16. wbfdm/analysis/esg/utils.py +13 -0
  17. wbfdm/analysis/financial_analysis/__init__.py +1 -0
  18. wbfdm/analysis/financial_analysis/financial_metric_analysis.py +88 -0
  19. wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +125 -0
  20. wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +271 -0
  21. wbfdm/analysis/financial_analysis/statement_with_estimates.py +558 -0
  22. wbfdm/analysis/financial_analysis/utils.py +316 -0
  23. wbfdm/analysis/technical_analysis/__init__.py +1 -0
  24. wbfdm/analysis/technical_analysis/technical_analysis.py +138 -0
  25. wbfdm/analysis/technical_analysis/traces.py +165 -0
  26. wbfdm/analysis/utils.py +32 -0
  27. wbfdm/apps.py +14 -0
  28. wbfdm/contrib/__init__.py +0 -0
  29. wbfdm/contrib/dsws/__init__.py +0 -0
  30. wbfdm/contrib/dsws/client.py +285 -0
  31. wbfdm/contrib/internal/__init__.py +0 -0
  32. wbfdm/contrib/internal/dataloaders/__init__.py +0 -0
  33. wbfdm/contrib/internal/dataloaders/market_data.py +87 -0
  34. wbfdm/contrib/metric/__init__.py +0 -0
  35. wbfdm/contrib/metric/admin/__init__.py +2 -0
  36. wbfdm/contrib/metric/admin/instruments.py +12 -0
  37. wbfdm/contrib/metric/admin/metrics.py +43 -0
  38. wbfdm/contrib/metric/apps.py +10 -0
  39. wbfdm/contrib/metric/backends/__init__.py +2 -0
  40. wbfdm/contrib/metric/backends/base.py +159 -0
  41. wbfdm/contrib/metric/backends/performances.py +265 -0
  42. wbfdm/contrib/metric/backends/statistics.py +182 -0
  43. wbfdm/contrib/metric/decorators.py +14 -0
  44. wbfdm/contrib/metric/dispatch.py +23 -0
  45. wbfdm/contrib/metric/dto.py +88 -0
  46. wbfdm/contrib/metric/exceptions.py +6 -0
  47. wbfdm/contrib/metric/factories.py +33 -0
  48. wbfdm/contrib/metric/filters.py +28 -0
  49. wbfdm/contrib/metric/migrations/0001_initial.py +88 -0
  50. wbfdm/contrib/metric/migrations/0002_remove_instrumentmetric_unique_instrument_metric_and_more.py +26 -0
  51. wbfdm/contrib/metric/migrations/__init__.py +0 -0
  52. wbfdm/contrib/metric/models.py +180 -0
  53. wbfdm/contrib/metric/orchestrators.py +94 -0
  54. wbfdm/contrib/metric/registry.py +80 -0
  55. wbfdm/contrib/metric/serializers.py +44 -0
  56. wbfdm/contrib/metric/tasks.py +27 -0
  57. wbfdm/contrib/metric/tests/__init__.py +0 -0
  58. wbfdm/contrib/metric/tests/backends/__init__.py +0 -0
  59. wbfdm/contrib/metric/tests/backends/test_performances.py +152 -0
  60. wbfdm/contrib/metric/tests/backends/test_statistics.py +48 -0
  61. wbfdm/contrib/metric/tests/conftest.py +92 -0
  62. wbfdm/contrib/metric/tests/test_dto.py +73 -0
  63. wbfdm/contrib/metric/tests/test_models.py +72 -0
  64. wbfdm/contrib/metric/tests/test_tasks.py +24 -0
  65. wbfdm/contrib/metric/tests/test_viewsets.py +79 -0
  66. wbfdm/contrib/metric/urls.py +19 -0
  67. wbfdm/contrib/metric/viewsets/__init__.py +1 -0
  68. wbfdm/contrib/metric/viewsets/configs/__init__.py +1 -0
  69. wbfdm/contrib/metric/viewsets/configs/display.py +92 -0
  70. wbfdm/contrib/metric/viewsets/configs/menus.py +11 -0
  71. wbfdm/contrib/metric/viewsets/configs/utils.py +137 -0
  72. wbfdm/contrib/metric/viewsets/mixins.py +245 -0
  73. wbfdm/contrib/metric/viewsets/viewsets.py +40 -0
  74. wbfdm/contrib/msci/__init__.py +0 -0
  75. wbfdm/contrib/msci/client.py +92 -0
  76. wbfdm/contrib/msci/dataloaders/__init__.py +0 -0
  77. wbfdm/contrib/msci/dataloaders/esg.py +87 -0
  78. wbfdm/contrib/msci/dataloaders/esg_controversies.py +81 -0
  79. wbfdm/contrib/msci/sync.py +58 -0
  80. wbfdm/contrib/msci/tests/__init__.py +0 -0
  81. wbfdm/contrib/msci/tests/conftest.py +1 -0
  82. wbfdm/contrib/msci/tests/test_client.py +70 -0
  83. wbfdm/contrib/qa/__init__.py +0 -0
  84. wbfdm/contrib/qa/apps.py +22 -0
  85. wbfdm/contrib/qa/database_routers.py +25 -0
  86. wbfdm/contrib/qa/dataloaders/__init__.py +0 -0
  87. wbfdm/contrib/qa/dataloaders/adjustments.py +56 -0
  88. wbfdm/contrib/qa/dataloaders/corporate_actions.py +59 -0
  89. wbfdm/contrib/qa/dataloaders/financials.py +83 -0
  90. wbfdm/contrib/qa/dataloaders/market_data.py +117 -0
  91. wbfdm/contrib/qa/dataloaders/officers.py +59 -0
  92. wbfdm/contrib/qa/dataloaders/reporting_dates.py +67 -0
  93. wbfdm/contrib/qa/dataloaders/statements.py +267 -0
  94. wbfdm/contrib/qa/tasks.py +0 -0
  95. wbfdm/dataloaders/__init__.py +0 -0
  96. wbfdm/dataloaders/cache.py +129 -0
  97. wbfdm/dataloaders/protocols.py +112 -0
  98. wbfdm/dataloaders/proxies.py +201 -0
  99. wbfdm/dataloaders/types.py +209 -0
  100. wbfdm/dynamic_preferences_registry.py +45 -0
  101. wbfdm/enums.py +657 -0
  102. wbfdm/factories/__init__.py +13 -0
  103. wbfdm/factories/classifications.py +56 -0
  104. wbfdm/factories/controversies.py +27 -0
  105. wbfdm/factories/exchanges.py +21 -0
  106. wbfdm/factories/instrument_list.py +22 -0
  107. wbfdm/factories/instrument_prices.py +79 -0
  108. wbfdm/factories/instruments.py +63 -0
  109. wbfdm/factories/instruments_relationships.py +31 -0
  110. wbfdm/factories/options.py +66 -0
  111. wbfdm/figures/__init__.py +1 -0
  112. wbfdm/figures/financials/__init__.py +1 -0
  113. wbfdm/figures/financials/financial_analysis_charts.py +469 -0
  114. wbfdm/figures/financials/financials_charts.py +711 -0
  115. wbfdm/filters/__init__.py +31 -0
  116. wbfdm/filters/classifications.py +100 -0
  117. wbfdm/filters/exchanges.py +22 -0
  118. wbfdm/filters/financials.py +95 -0
  119. wbfdm/filters/financials_analysis.py +119 -0
  120. wbfdm/filters/instrument_prices.py +112 -0
  121. wbfdm/filters/instruments.py +198 -0
  122. wbfdm/filters/utils.py +44 -0
  123. wbfdm/import_export/__init__.py +0 -0
  124. wbfdm/import_export/backends/__init__.py +0 -0
  125. wbfdm/import_export/backends/cbinsights/__init__.py +2 -0
  126. wbfdm/import_export/backends/cbinsights/deals.py +44 -0
  127. wbfdm/import_export/backends/cbinsights/equities.py +41 -0
  128. wbfdm/import_export/backends/cbinsights/mixin.py +15 -0
  129. wbfdm/import_export/backends/cbinsights/utils/__init__.py +0 -0
  130. wbfdm/import_export/backends/cbinsights/utils/classifications.py +4150 -0
  131. wbfdm/import_export/backends/cbinsights/utils/client.py +217 -0
  132. wbfdm/import_export/backends/refinitiv/__init__.py +5 -0
  133. wbfdm/import_export/backends/refinitiv/daily_fundamental.py +36 -0
  134. wbfdm/import_export/backends/refinitiv/fiscal_period.py +63 -0
  135. wbfdm/import_export/backends/refinitiv/forecast.py +178 -0
  136. wbfdm/import_export/backends/refinitiv/fundamental.py +103 -0
  137. wbfdm/import_export/backends/refinitiv/geographic_segment.py +32 -0
  138. wbfdm/import_export/backends/refinitiv/instrument.py +55 -0
  139. wbfdm/import_export/backends/refinitiv/instrument_price.py +77 -0
  140. wbfdm/import_export/backends/refinitiv/mixin.py +29 -0
  141. wbfdm/import_export/backends/refinitiv/utils/__init__.py +1 -0
  142. wbfdm/import_export/backends/refinitiv/utils/controller.py +182 -0
  143. wbfdm/import_export/handlers/__init__.py +0 -0
  144. wbfdm/import_export/handlers/instrument.py +253 -0
  145. wbfdm/import_export/handlers/instrument_list.py +101 -0
  146. wbfdm/import_export/handlers/instrument_price.py +71 -0
  147. wbfdm/import_export/handlers/option.py +54 -0
  148. wbfdm/import_export/handlers/private_equities.py +49 -0
  149. wbfdm/import_export/parsers/__init__.py +0 -0
  150. wbfdm/import_export/parsers/cbinsights/__init__.py +0 -0
  151. wbfdm/import_export/parsers/cbinsights/deals.py +39 -0
  152. wbfdm/import_export/parsers/cbinsights/equities.py +56 -0
  153. wbfdm/import_export/parsers/cbinsights/fundamentals.py +45 -0
  154. wbfdm/import_export/parsers/refinitiv/__init__.py +0 -0
  155. wbfdm/import_export/parsers/refinitiv/daily_fundamental.py +7 -0
  156. wbfdm/import_export/parsers/refinitiv/forecast.py +7 -0
  157. wbfdm/import_export/parsers/refinitiv/fundamental.py +9 -0
  158. wbfdm/import_export/parsers/refinitiv/geographic_segment.py +7 -0
  159. wbfdm/import_export/parsers/refinitiv/instrument.py +75 -0
  160. wbfdm/import_export/parsers/refinitiv/instrument_price.py +26 -0
  161. wbfdm/import_export/parsers/refinitiv/utils.py +96 -0
  162. wbfdm/import_export/resources/__init__.py +0 -0
  163. wbfdm/import_export/resources/classification.py +23 -0
  164. wbfdm/import_export/resources/instrument_prices.py +33 -0
  165. wbfdm/import_export/resources/instruments.py +176 -0
  166. wbfdm/jinja2.py +7 -0
  167. wbfdm/management/__init__.py +30 -0
  168. wbfdm/menu.py +11 -0
  169. wbfdm/migrations/0001_initial.py +71 -0
  170. wbfdm/migrations/0002_rename_statements_instrumentlookup_financials_and_more.py +144 -0
  171. wbfdm/migrations/0003_instrument_estimate_backend_and_more.py +34 -0
  172. wbfdm/migrations/0004_rename_financials_instrumentlookup_statements_and_more.py +86 -0
  173. wbfdm/migrations/0005_instrument_corporate_action_backend.py +29 -0
  174. wbfdm/migrations/0006_instrument_officer_backend.py +29 -0
  175. wbfdm/migrations/0007_instrument_country_instrument_currency_and_more.py +117 -0
  176. wbfdm/migrations/0008_controversy.py +75 -0
  177. wbfdm/migrations/0009_alter_controversy_flag_alter_controversy_initiated_and_more.py +85 -0
  178. wbfdm/migrations/0010_classification_classificationgroup_deal_exchange_and_more.py +1299 -0
  179. wbfdm/migrations/0011_delete_instrumentlookup_instrument_corporate_actions_and_more.py +169 -0
  180. wbfdm/migrations/0012_instrumentprice_created_instrumentprice_modified.py +564 -0
  181. wbfdm/migrations/0013_instrument_is_investable_universe_and_more.py +199 -0
  182. wbfdm/migrations/0014_alter_controversy_instrument.py +22 -0
  183. wbfdm/migrations/0015_instrument_instrument_investible_index.py +16 -0
  184. wbfdm/migrations/0016_instrumenttype_name_repr.py +18 -0
  185. wbfdm/migrations/0017_instrument_instrument_security_index.py +16 -0
  186. wbfdm/migrations/0018_instrument_instrument_level_index.py +20 -0
  187. wbfdm/migrations/0019_alter_controversy_source.py +17 -0
  188. wbfdm/migrations/0020_optionaggregate_option_and_more.py +249 -0
  189. wbfdm/migrations/0021_delete_instrumentdailystatistics.py +15 -0
  190. wbfdm/migrations/0022_instrument_cusip_option_open_interest_20d_and_more.py +91 -0
  191. wbfdm/migrations/0023_instrument_unique_ric_instrument_unique_rmc_and_more.py +53 -0
  192. wbfdm/migrations/0024_option_open_interest_10d_option_volume_10d_and_more.py +36 -0
  193. wbfdm/migrations/0025_instrument_is_primary_and_more.py +29 -0
  194. wbfdm/migrations/0026_instrument_is_cash_equivalent.py +30 -0
  195. wbfdm/migrations/0027_remove_instrument_unique_ric_and_more.py +100 -0
  196. wbfdm/migrations/__init__.py +0 -0
  197. wbfdm/models/__init__.py +4 -0
  198. wbfdm/models/esg/__init__.py +1 -0
  199. wbfdm/models/esg/controversies.py +81 -0
  200. wbfdm/models/exchanges/__init__.py +1 -0
  201. wbfdm/models/exchanges/exchanges.py +223 -0
  202. wbfdm/models/fields.py +117 -0
  203. wbfdm/models/fk_fields.py +403 -0
  204. wbfdm/models/indicators.py +0 -0
  205. wbfdm/models/instruments/__init__.py +19 -0
  206. wbfdm/models/instruments/classifications.py +265 -0
  207. wbfdm/models/instruments/instrument_lists.py +120 -0
  208. wbfdm/models/instruments/instrument_prices.py +540 -0
  209. wbfdm/models/instruments/instrument_relationships.py +251 -0
  210. wbfdm/models/instruments/instrument_requests.py +196 -0
  211. wbfdm/models/instruments/instruments.py +991 -0
  212. wbfdm/models/instruments/llm/__init__.py +1 -0
  213. wbfdm/models/instruments/llm/create_instrument_news_relationships.py +78 -0
  214. wbfdm/models/instruments/mixin/__init__.py +0 -0
  215. wbfdm/models/instruments/mixin/financials_computed.py +804 -0
  216. wbfdm/models/instruments/mixin/financials_serializer_fields.py +1407 -0
  217. wbfdm/models/instruments/mixin/instruments.py +294 -0
  218. wbfdm/models/instruments/options.py +225 -0
  219. wbfdm/models/instruments/private_equities.py +59 -0
  220. wbfdm/models/instruments/querysets.py +73 -0
  221. wbfdm/models/instruments/utils.py +41 -0
  222. wbfdm/preferences.py +21 -0
  223. wbfdm/serializers/__init__.py +4 -0
  224. wbfdm/serializers/esg.py +36 -0
  225. wbfdm/serializers/exchanges.py +39 -0
  226. wbfdm/serializers/instruments/__init__.py +37 -0
  227. wbfdm/serializers/instruments/classifications.py +139 -0
  228. wbfdm/serializers/instruments/instrument_lists.py +61 -0
  229. wbfdm/serializers/instruments/instrument_prices.py +73 -0
  230. wbfdm/serializers/instruments/instrument_relationships.py +170 -0
  231. wbfdm/serializers/instruments/instrument_requests.py +61 -0
  232. wbfdm/serializers/instruments/instruments.py +274 -0
  233. wbfdm/serializers/instruments/mixins.py +104 -0
  234. wbfdm/serializers/officers.py +20 -0
  235. wbfdm/signals.py +7 -0
  236. wbfdm/sync/__init__.py +0 -0
  237. wbfdm/sync/abstract.py +31 -0
  238. wbfdm/sync/runner.py +22 -0
  239. wbfdm/tasks.py +69 -0
  240. wbfdm/tests/__init__.py +0 -0
  241. wbfdm/tests/analysis/__init__.py +0 -0
  242. wbfdm/tests/analysis/financial_analysis/__init__.py +0 -0
  243. wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +392 -0
  244. wbfdm/tests/analysis/financial_analysis/test_utils.py +322 -0
  245. wbfdm/tests/analysis/test_esg.py +159 -0
  246. wbfdm/tests/conftest.py +92 -0
  247. wbfdm/tests/dataloaders/__init__.py +0 -0
  248. wbfdm/tests/dataloaders/test_cache.py +73 -0
  249. wbfdm/tests/models/__init__.py +0 -0
  250. wbfdm/tests/models/test_classifications.py +99 -0
  251. wbfdm/tests/models/test_exchanges.py +7 -0
  252. wbfdm/tests/models/test_instrument_list.py +117 -0
  253. wbfdm/tests/models/test_instrument_prices.py +306 -0
  254. wbfdm/tests/models/test_instruments.py +202 -0
  255. wbfdm/tests/models/test_merge.py +99 -0
  256. wbfdm/tests/models/test_options.py +69 -0
  257. wbfdm/tests/test_tasks.py +6 -0
  258. wbfdm/tests/tests.py +10 -0
  259. wbfdm/urls.py +222 -0
  260. wbfdm/utils.py +54 -0
  261. wbfdm/viewsets/__init__.py +10 -0
  262. wbfdm/viewsets/configs/__init__.py +5 -0
  263. wbfdm/viewsets/configs/buttons/__init__.py +8 -0
  264. wbfdm/viewsets/configs/buttons/classifications.py +23 -0
  265. wbfdm/viewsets/configs/buttons/exchanges.py +9 -0
  266. wbfdm/viewsets/configs/buttons/instrument_prices.py +49 -0
  267. wbfdm/viewsets/configs/buttons/instruments.py +283 -0
  268. wbfdm/viewsets/configs/display/__init__.py +22 -0
  269. wbfdm/viewsets/configs/display/classifications.py +138 -0
  270. wbfdm/viewsets/configs/display/esg.py +75 -0
  271. wbfdm/viewsets/configs/display/exchanges.py +42 -0
  272. wbfdm/viewsets/configs/display/instrument_lists.py +137 -0
  273. wbfdm/viewsets/configs/display/instrument_prices.py +199 -0
  274. wbfdm/viewsets/configs/display/instrument_requests.py +116 -0
  275. wbfdm/viewsets/configs/display/instruments.py +618 -0
  276. wbfdm/viewsets/configs/display/instruments_relationships.py +65 -0
  277. wbfdm/viewsets/configs/display/monthly_performances.py +72 -0
  278. wbfdm/viewsets/configs/display/officers.py +16 -0
  279. wbfdm/viewsets/configs/display/prices.py +21 -0
  280. wbfdm/viewsets/configs/display/statement_with_estimates.py +101 -0
  281. wbfdm/viewsets/configs/display/statements.py +48 -0
  282. wbfdm/viewsets/configs/endpoints/__init__.py +41 -0
  283. wbfdm/viewsets/configs/endpoints/classifications.py +87 -0
  284. wbfdm/viewsets/configs/endpoints/esg.py +20 -0
  285. wbfdm/viewsets/configs/endpoints/exchanges.py +6 -0
  286. wbfdm/viewsets/configs/endpoints/financials_analysis.py +65 -0
  287. wbfdm/viewsets/configs/endpoints/instrument_lists.py +38 -0
  288. wbfdm/viewsets/configs/endpoints/instrument_prices.py +51 -0
  289. wbfdm/viewsets/configs/endpoints/instrument_requests.py +20 -0
  290. wbfdm/viewsets/configs/endpoints/instruments.py +13 -0
  291. wbfdm/viewsets/configs/endpoints/instruments_relationships.py +31 -0
  292. wbfdm/viewsets/configs/endpoints/statements.py +6 -0
  293. wbfdm/viewsets/configs/menus/__init__.py +9 -0
  294. wbfdm/viewsets/configs/menus/classifications.py +19 -0
  295. wbfdm/viewsets/configs/menus/exchanges.py +10 -0
  296. wbfdm/viewsets/configs/menus/instrument_lists.py +10 -0
  297. wbfdm/viewsets/configs/menus/instruments.py +20 -0
  298. wbfdm/viewsets/configs/menus/instruments_relationships.py +33 -0
  299. wbfdm/viewsets/configs/titles/__init__.py +42 -0
  300. wbfdm/viewsets/configs/titles/classifications.py +79 -0
  301. wbfdm/viewsets/configs/titles/esg.py +11 -0
  302. wbfdm/viewsets/configs/titles/exchanges.py +12 -0
  303. wbfdm/viewsets/configs/titles/financial_ratio_analysis.py +6 -0
  304. wbfdm/viewsets/configs/titles/financials_analysis.py +50 -0
  305. wbfdm/viewsets/configs/titles/instrument_prices.py +50 -0
  306. wbfdm/viewsets/configs/titles/instrument_requests.py +16 -0
  307. wbfdm/viewsets/configs/titles/instruments.py +31 -0
  308. wbfdm/viewsets/configs/titles/instruments_relationships.py +21 -0
  309. wbfdm/viewsets/configs/titles/market_data.py +13 -0
  310. wbfdm/viewsets/configs/titles/prices.py +15 -0
  311. wbfdm/viewsets/configs/titles/statement_with_estimates.py +10 -0
  312. wbfdm/viewsets/esg.py +72 -0
  313. wbfdm/viewsets/exchanges.py +63 -0
  314. wbfdm/viewsets/financial_analysis/__init__.py +3 -0
  315. wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +85 -0
  316. wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +85 -0
  317. wbfdm/viewsets/financial_analysis/statement_with_estimates.py +145 -0
  318. wbfdm/viewsets/instruments/__init__.py +80 -0
  319. wbfdm/viewsets/instruments/classifications.py +279 -0
  320. wbfdm/viewsets/instruments/financials_analysis.py +614 -0
  321. wbfdm/viewsets/instruments/instrument_lists.py +77 -0
  322. wbfdm/viewsets/instruments/instrument_prices.py +542 -0
  323. wbfdm/viewsets/instruments/instrument_requests.py +51 -0
  324. wbfdm/viewsets/instruments/instruments.py +106 -0
  325. wbfdm/viewsets/instruments/instruments_relationships.py +235 -0
  326. wbfdm/viewsets/instruments/utils.py +27 -0
  327. wbfdm/viewsets/market_data.py +172 -0
  328. wbfdm/viewsets/mixins.py +9 -0
  329. wbfdm/viewsets/officers.py +27 -0
  330. wbfdm/viewsets/prices.py +62 -0
  331. wbfdm/viewsets/statements/__init__.py +1 -0
  332. wbfdm/viewsets/statements/statements.py +100 -0
  333. wbfdm/viewsets/technical_analysis/__init__.py +1 -0
  334. wbfdm/viewsets/technical_analysis/monthly_performances.py +93 -0
  335. wbfdm-2.2.1.dist-info/METADATA +15 -0
  336. wbfdm-2.2.1.dist-info/RECORD +337 -0
  337. wbfdm-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,137 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from wbcore.contrib.color.enums import WBColor
3
+ from wbcore.metadata.configs import display as dp
4
+ from wbcore.metadata.configs.display.formatting import Condition, Operator
5
+
6
+ PERFORMANCE_FORMATTING = [
7
+ dp.FormattingRule(
8
+ style={"color": WBColor.GREY.value, "fontWeight": "bold"},
9
+ condition=Condition(Operator("=="), 0),
10
+ ),
11
+ dp.FormattingRule(
12
+ style={"color": WBColor.RED_DARK.value, "fontWeight": "bold"},
13
+ condition=Condition(Operator("<"), 0),
14
+ ),
15
+ dp.FormattingRule(
16
+ style={"color": WBColor.GREEN_DARK.value, "fontWeight": "bold"},
17
+ condition=Condition(Operator(">"), 0),
18
+ ),
19
+ ]
20
+
21
+ BORDER_LEFT = dp.FormattingRule(
22
+ style={
23
+ "borderLeft": "1px solid #bdc3c7",
24
+ }
25
+ )
26
+
27
+
28
+ def get_performance_fields(with_comparison_performances: bool = False) -> list[dp.Field]:
29
+ def _get_performance_field(key: str, label: str, show=None):
30
+ if not with_comparison_performances:
31
+ return dp.Field(
32
+ key=f"performance_{key}",
33
+ label=label,
34
+ width=100,
35
+ formatting_rules=PERFORMANCE_FORMATTING,
36
+ )
37
+ return dp.Field(
38
+ key=None,
39
+ label=label,
40
+ width=100,
41
+ show=show,
42
+ children=[
43
+ dp.Field(
44
+ key=f"performance_{key}",
45
+ label="Absolute",
46
+ width=100,
47
+ formatting_rules=PERFORMANCE_FORMATTING + [BORDER_LEFT],
48
+ ),
49
+ dp.Field(
50
+ key=f"performance_peer_{key}",
51
+ label="vs Peer",
52
+ width=100,
53
+ formatting_rules=PERFORMANCE_FORMATTING,
54
+ show="open",
55
+ tooltip=dp.Tooltip(key="peers"),
56
+ ),
57
+ dp.Field(
58
+ key=f"performance_benchmark_{key}",
59
+ label="vs Benchmark",
60
+ width=100,
61
+ formatting_rules=PERFORMANCE_FORMATTING,
62
+ show="open",
63
+ tooltip=dp.Tooltip(key="benchmarks"),
64
+ ),
65
+ ],
66
+ )
67
+
68
+ return [
69
+ dp.Field(
70
+ key=None,
71
+ label="Rolling",
72
+ open_by_default=False,
73
+ children=[
74
+ _get_performance_field(key="daily", label="Daily"),
75
+ _get_performance_field(
76
+ key="weekly",
77
+ label="Weekly",
78
+ show="open",
79
+ ),
80
+ _get_performance_field(
81
+ key="monthly",
82
+ label="Monthly",
83
+ show="open",
84
+ ),
85
+ _get_performance_field(
86
+ key="quarterly",
87
+ label="Quarterly",
88
+ show="open",
89
+ ),
90
+ _get_performance_field(
91
+ key="yearly",
92
+ label="Yearly",
93
+ show="open",
94
+ ),
95
+ ],
96
+ ),
97
+ dp.Field(
98
+ key=None,
99
+ label="Performance-To-Date (PTD)",
100
+ open_by_default=False,
101
+ children=[
102
+ _get_performance_field(key="week_to_date", label="Week", show="open"),
103
+ _get_performance_field(key="month_to_date", label="Month"),
104
+ _get_performance_field(key="quarter_to_date", label="Quarter", show="open"),
105
+ _get_performance_field(key="year_to_date", label="Year", show="open"),
106
+ ],
107
+ ),
108
+ dp.Field(
109
+ key=None,
110
+ label="Previous Performance",
111
+ open_by_default=False,
112
+ show="open",
113
+ children=[
114
+ _get_performance_field(key="previous_week_to_date", label="Week", show="open"),
115
+ _get_performance_field(key="previous_month_to_date", label="Month"),
116
+ _get_performance_field(key="previous_quarter_to_date", label="Quarter", show="open"),
117
+ _get_performance_field(key="previous_year_to_date", label="Year", show="open"),
118
+ ],
119
+ ),
120
+ _get_performance_field(key="inception", label="Inception"),
121
+ ]
122
+
123
+
124
+ def get_statistic_field():
125
+ return dp.Field(
126
+ key=None,
127
+ label=_("Fundamentals"),
128
+ open_by_default=False,
129
+ children=[
130
+ dp.Field(key="statistic_price", label="Last Price", width=100),
131
+ dp.Field(key="statistic_market_capitalization", label="Market Capitalization", width=100),
132
+ dp.Field(key="statistic_revenue_y_1", label="Revenue Y-1", width=100, show="closed"),
133
+ dp.Field(key="statistic_revenue_y0", label="Revenue Y", width=100, show="closed"),
134
+ dp.Field(key="statistic_revenue_y1", label="Revenue Y+1", width=100),
135
+ dp.Field(key="statistic_volume_50d", label="Avg Volume (3m)", width=100, show="closed"),
136
+ ],
137
+ )
@@ -0,0 +1,245 @@
1
+ from datetime import date
2
+ from typing import TYPE_CHECKING, Optional, Type
3
+
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from django.utils.functional import cached_property
6
+ from wbcore import filters
7
+ from wbcore.metadata.metadata import WBCoreMetadata
8
+ from wbcore.utils.strings import get_aggregate_symbol
9
+ from wbfdm.contrib.metric.models import InstrumentMetric
10
+ from wbfdm.contrib.metric.registry import backend_registry
11
+ from wbfdm.contrib.metric.viewsets.configs.display import (
12
+ InstrumentMetricPivotedListDisplayConfig,
13
+ )
14
+
15
+ from ..dto import MetricKey
16
+
17
+ if TYPE_CHECKING:
18
+ from django.db.models import Model
19
+ from wbcore.models import WBModel
20
+ from wbcore.viewsets import ModelViewSet
21
+
22
+ _Base = ModelViewSet
23
+ else:
24
+ _Base = object
25
+
26
+
27
+ class InstrumentMetricMetaData(WBCoreMetadata):
28
+ def determine_metadata(self, request, view):
29
+ metadata = super().determine_metadata(request, view)
30
+ if "filter_fields" in metadata and view.METRIC_SHOW_FILTERS:
31
+ view_metric_key_choices = backend_registry.get_choices(view.METRIC_KEYS)
32
+ metric_show_filter_field = filters.BooleanFilter(
33
+ default=view.METRIC_SHOW_BY_DEFAULT,
34
+ field_name="metric_show_by_default",
35
+ required=True,
36
+ label="Show Metric Keys",
37
+ ) # TODO implement this as a dependant filter when frontend allows it
38
+ metric_keys_filter_field = filters.MultipleChoiceFilter(
39
+ choices=view_metric_key_choices,
40
+ field_name="metric_keys",
41
+ required=True,
42
+ label="Metric Keys",
43
+ default=list(map(lambda x: x[0], view_metric_key_choices)),
44
+ )
45
+ _, metric_show_by_default_rep = metric_show_filter_field.get_representation(
46
+ request, "Show Metric Keys", view
47
+ )
48
+ metadata["filter_fields"]["metric_show_by_default"] = {
49
+ "label": "Show Metrics",
50
+ "lookup_expr": [metric_show_by_default_rep],
51
+ }
52
+ _, metric_keys_rep = metric_keys_filter_field.get_representation(request, "Metric Keys", view)
53
+ metadata["filter_fields"]["metric_keys"] = {"label": "Metric Keys", "lookup_expr": [metric_keys_rep]}
54
+
55
+ return metadata
56
+
57
+
58
+ class InstrumentMetricMixin(_Base):
59
+ """
60
+ Mixin to register automatically a set of metrics defines in METRIC_KEYS into a particular view
61
+ """
62
+
63
+ METRIC_KEYS: tuple[MetricKey] | tuple[()] = () # The set of MetricKey to inject into the view
64
+ METRIC_BASKET_LABEL: str = "id" # The filter path of the basket in the queryset
65
+ METRIC_INSTRUMENT_LABEL: str | None = None # The filter path of the instrument in the queryset. (default to None)
66
+ METRIC_WITH_PREFIXED_KEYS: bool = (
67
+ False # Set to True if prefixed metrics needs to be appended to the list of metric keys
68
+ )
69
+ METRIC_SHOW_AGGREGATES: bool = True # set to False if no aggregation needs to be shown for the inserted metrics
70
+ METRIC_SHOW_BY_DEFAULT: bool = True # If false, the metric are hidden by default
71
+ METRIC_SHOW_FILTERS: bool = False
72
+
73
+ display_config_class = InstrumentMetricPivotedListDisplayConfig # Default display class that automatically regised display Fields into the ListDisplay
74
+ metadata_class = InstrumentMetricMetaData
75
+
76
+ @property
77
+ def metric_date(self) -> date | None:
78
+ """
79
+ Property to define at which date the metrics need to be fetched from. Expected to be override
80
+ """
81
+ return None
82
+
83
+ @property
84
+ def metric_basket(self) -> Optional["WBModel"]:
85
+ """
86
+ Property to define at for which basket the metrics need to be fetched from. Expected to be override
87
+ """
88
+ return None
89
+
90
+ @property
91
+ def metric_basket_class(self) -> Type["Model"]:
92
+ """
93
+ Define the basket class from which the metrics need to be fetched from.
94
+
95
+ Default to `metric_basket.__class__` if `metric_basket` is defined. Otherwise default to the viewset Model property.
96
+ """
97
+ metric_basket_class = self.metric_basket.__class__ if self.metric_basket else self.model
98
+ if not metric_basket_class:
99
+ raise ValueError("Metric Basket Class needs to be defined")
100
+ return metric_basket_class
101
+
102
+ @cached_property
103
+ def metric_basket_content_type(self) -> ContentType:
104
+ """
105
+ cached property to store the related basket content type instance
106
+ """
107
+ return ContentType.objects.get_for_model(self.metric_basket_class)
108
+
109
+ @property
110
+ def metric_keys(self) -> tuple[MetricKey] | tuple[()]:
111
+ """
112
+ Property used to get the list of metric keys. Can be overridden to define custom logic based on the viewset attributes (e.g. request)
113
+ """
114
+ if self.request.GET.get("metric_show_by_default", "true") == "false":
115
+ return ()
116
+ metric_keys = getattr(self, "METRIC_KEYS", ())
117
+ if metric_keys_filter_repr := self.request.GET.get("metric_keys"):
118
+ metric_keys_filter = metric_keys_filter_repr.split(",")
119
+ metric_keys = tuple(filter(lambda x: x.key in metric_keys_filter, metric_keys))
120
+ return metric_keys
121
+
122
+ @property
123
+ def metric_instrument_label(self) -> str | None:
124
+ """
125
+ Property used to get the list of metric instrument label. Can be overridden to define custom logic based on the viewset attributes (e.g. request)
126
+ """
127
+ return getattr(self, "METRIC_INSTRUMENT_LABEL", None)
128
+
129
+ @property
130
+ def metric_basket_label(self) -> str:
131
+ """
132
+ Property used to get the list of metric basket label. Can be overridden to define custom logic based on the viewset attributes (e.g. request)
133
+ """
134
+ return getattr(self, "METRIC_BASKET_LABEL", "id")
135
+
136
+ @cached_property
137
+ def _metric_serializer_fields(self):
138
+ """
139
+ return the set of serializer fields necessary to display the related metric fields
140
+ """
141
+ extra_serializer_fields = {}
142
+ for metric_key in self.metric_keys:
143
+ metric_backend = backend_registry[metric_key, self.metric_basket_class](self.metric_date)
144
+ extra_serializer_fields.update(
145
+ metric_backend.get_serializer_fields(
146
+ with_prefixed_key=self.METRIC_WITH_PREFIXED_KEYS, metric_key=metric_key
147
+ )
148
+ )
149
+ return extra_serializer_fields
150
+
151
+ def get_ordering_fields(self) -> list[str]:
152
+ """
153
+ Inject the metric field keys as ordering fields
154
+ """
155
+ ordering_fields = list(super().get_ordering_fields())
156
+ for metric_field in self._metric_serializer_fields.keys():
157
+ ordering_fields.append(metric_field)
158
+ return ordering_fields
159
+
160
+ def get_queryset(self):
161
+ """
162
+ Annotate the metrics into the viewset queryset
163
+ """
164
+ base_qs = super().get_queryset()
165
+ for metric_key in self.metric_keys:
166
+ base_qs = InstrumentMetric.annotate_with_metrics(
167
+ base_qs,
168
+ metric_key,
169
+ self.metric_basket_class,
170
+ self.metric_date,
171
+ basket_label=self.metric_basket_label,
172
+ instrument_label=self.metric_instrument_label,
173
+ )
174
+ return base_qs
175
+
176
+ def get_serializer(self, *args, **kwargs):
177
+ """
178
+ Unwrap defined serializer class and inject the metric fields into a new class
179
+ """
180
+
181
+ serializer_class = self.get_serializer_class()
182
+ kwargs.setdefault("context", self.get_serializer_context())
183
+
184
+ BaseMeta = serializer_class.Meta
185
+ fields = list(getattr(BaseMeta, "fields", ()))
186
+ read_only_fields = list(getattr(BaseMeta, "read_only_fields", ()))
187
+ for extra_field in self._metric_serializer_fields.keys():
188
+ fields.append(extra_field)
189
+ read_only_fields.append(extra_field)
190
+
191
+ Meta = type(str("Meta"), (BaseMeta,), {"fields": fields, "read_only_fields": read_only_fields})
192
+ new_class = type(
193
+ serializer_class.__name__,
194
+ (serializer_class,),
195
+ {
196
+ "Meta": Meta,
197
+ **self._metric_serializer_fields,
198
+ "SERIALIZER_CLASS_FOR_REMOTE_ADDITIONAL_RESOURCES": serializer_class,
199
+ },
200
+ )
201
+
202
+ return new_class(*args, **kwargs)
203
+
204
+ def get_aggregates(self, queryset, paginated_queryset):
205
+ """
206
+ Automatically register the metric aggregation
207
+ """
208
+ aggregates = dict()
209
+ if self.METRIC_SHOW_AGGREGATES:
210
+ # we try to inject the instrument metric aggregates automatically
211
+ keys_map = {key.key: key for key in self.metric_keys}
212
+ if self.metric_basket and (ct := self.metric_basket_content_type):
213
+ # for each "general" metric (i.e. without particular instrument attached), we add the raw subfield value as aggregates
214
+ for metric in InstrumentMetric.objects.filter(
215
+ basket_id=self.metric_basket.id,
216
+ basket_content_type=ct,
217
+ date=self.metric_date,
218
+ key__in=keys_map.keys(),
219
+ instrument__isnull=True,
220
+ ):
221
+ metric_key = keys_map[metric.key]
222
+ for subfield_key, subfield_filter in metric_key.subfields_filter_map.items():
223
+ if subfield_key in self._metric_serializer_fields and (
224
+ aggregate_fct := metric_key.subfields_map[subfield_key].aggregate
225
+ ):
226
+ aggregates[subfield_key] = {
227
+ get_aggregate_symbol(aggregate_fct.name): metric.metrics.get(subfield_filter)
228
+ }
229
+ # 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
230
+ missing_aggregate_map = {}
231
+ for metric_key in self.metric_keys:
232
+ for field_key, field_filter in metric_key.subfields_filter_map.items():
233
+ if field_key in self._metric_serializer_fields.keys() and field_key not in aggregates:
234
+ missing_aggregate_map[field_key] = metric_key.subfields_map[field_key]
235
+ missing_aggregate = queryset.aggregate(
236
+ **{
237
+ "agg_" + subfield_key: subfield.aggregate(subfield_key)
238
+ for subfield_key, subfield in missing_aggregate_map.items()
239
+ if subfield.aggregate is not None
240
+ }
241
+ )
242
+ for k, v in missing_aggregate.items():
243
+ key = k.replace("agg_", "")
244
+ aggregates[key] = {get_aggregate_symbol(missing_aggregate_map[key].aggregate.name): v}
245
+ return aggregates
@@ -0,0 +1,40 @@
1
+ from django.db.models import Case, Exists, F, IntegerField, OuterRef, When
2
+ from wbcore import viewsets
3
+ from wbfdm.contrib.metric.filters import InstrumentMetricFilterSet
4
+ from wbfdm.contrib.metric.models import InstrumentMetric
5
+ from wbfdm.contrib.metric.serializers import (
6
+ InstrumentMetricModelSerializer,
7
+ InstrumentMetricRepresentationSerializer,
8
+ )
9
+ from wbfdm.contrib.metric.viewsets.configs import InstrumentMetricDisplayConfig
10
+
11
+
12
+ class InstrumentMetricRepresentationViewSet(viewsets.RepresentationViewSet):
13
+ ordering = ["-date"]
14
+ ordering_fields = [
15
+ "key",
16
+ "date",
17
+ ]
18
+ filterset_class = InstrumentMetricFilterSet
19
+
20
+ serializer_class = InstrumentMetricRepresentationSerializer
21
+ queryset = InstrumentMetric.objects.all()
22
+
23
+
24
+ class InstrumentMetricViewSet(viewsets.ModelViewSet):
25
+ queryset = InstrumentMetric.objects.select_related("basket_content_type", "instrument").annotate(
26
+ has_children=Exists(InstrumentMetric.objects.filter(parent_metric=OuterRef("pk"))),
27
+ _group_key=Case(When(has_children=True, then=F("id")), default=None, output_field=IntegerField()),
28
+ )
29
+
30
+ serializer_class = InstrumentMetricModelSerializer
31
+ filterset_class = InstrumentMetricFilterSet
32
+
33
+ display_config_class = InstrumentMetricDisplayConfig
34
+
35
+ ordering = ["-date"]
36
+ ordering_fields = [
37
+ "key",
38
+ "date",
39
+ ]
40
+ search_fields = ["key", "instrument__computed_str", "basket_repr"]
File without changes
@@ -0,0 +1,92 @@
1
+ from contextlib import suppress
2
+ from datetime import UTC, datetime
3
+ from typing import Generator
4
+
5
+ import requests
6
+ from django.core.cache import cache as cache_layer
7
+ from django.utils import timezone
8
+ from django.utils.functional import cached_property
9
+ from jwt import decode as jwt_decode
10
+ from jwt.exceptions import DecodeError
11
+ from requests.exceptions import ConnectionError
12
+
13
+
14
+ def is_expired(token: str) -> bool:
15
+ with suppress(DecodeError, KeyError, ValueError):
16
+ expiry_ts = int(jwt_decode(token, options={"verify_signature": False})["exp"])
17
+ expiry_datetime = datetime.fromtimestamp(expiry_ts, UTC)
18
+ return expiry_datetime < timezone.now()
19
+ return True
20
+
21
+
22
+ class MSCIClient:
23
+ AUTH_SERVER_ULR: str = "https://accounts.msci.com/oauth/token"
24
+
25
+ def __init__(self, client_id: str, client_secret: str):
26
+ self.client_id = client_id
27
+ self.client_secret = client_secret
28
+
29
+ @cached_property
30
+ def oauth_token(self) -> str:
31
+ token = cache_layer.get("msci_oauth_token")
32
+ if not token or is_expired(token):
33
+ resp = requests.post(
34
+ self.AUTH_SERVER_ULR,
35
+ json={
36
+ "client_id": self.client_id,
37
+ "client_secret": self.client_secret,
38
+ "grant_type": "client_credentials",
39
+ "audience": "https://esg/data",
40
+ },
41
+ )
42
+ if resp.status_code == requests.codes.ok:
43
+ with suppress(KeyError, requests.exceptions.JSONDecodeError):
44
+ resp_json = resp.json()
45
+ token = resp_json["access_token"]
46
+ expires_in = resp_json["expires_in"]
47
+ cache_layer.set("msci_oauth_token", token, expires_in)
48
+ if token:
49
+ return token
50
+ raise ConnectionError()
51
+
52
+ def esg(self, identifiers: list[str], factors: list[str]) -> Generator[dict[str, str], None, None]:
53
+ with suppress(ConnectionError):
54
+ response = requests.post(
55
+ url="https://api2.msci.com/esg/data/v2.0/issuers",
56
+ json={
57
+ "issuer_identifier_list": identifiers,
58
+ "factor_name_list": factors,
59
+ },
60
+ headers={"AUTHORIZATION": f"Bearer {self.oauth_token}"},
61
+ )
62
+ if response.ok:
63
+ for row in response.json().get("result", {}).get("issuers", []):
64
+ yield row
65
+
66
+ def controversies(self, identifiers: list[str], factors: list[str]) -> Generator[dict[str, str], None, None]:
67
+ next_url = "https://api2.msci.com/esg/data/v2.0/issuers"
68
+ offset = 0
69
+ while next_url:
70
+ with suppress(ConnectionError):
71
+ response = requests.post(
72
+ url=next_url,
73
+ json={
74
+ "issuer_identifier_list": identifiers,
75
+ "factor_name_list": factors,
76
+ "limit": 100,
77
+ "offset": offset,
78
+ },
79
+ headers={"AUTHORIZATION": f"Bearer {self.oauth_token}"},
80
+ )
81
+
82
+ if not response.ok:
83
+ next_url = None
84
+ else:
85
+ json_res = response.json()
86
+ try:
87
+ next_url = json_res["paging"]["links"]["next"]
88
+ except KeyError:
89
+ next_url = None
90
+ offset += 100
91
+ for row in json_res.get("result", {}).get("issuers", []):
92
+ yield row
File without changes
@@ -0,0 +1,87 @@
1
+ import enum
2
+ from typing import Iterator
3
+
4
+ from django.conf import settings
5
+ from wbcore.contrib.dataloader.dataloaders import Dataloader
6
+ from wbfdm.dataloaders.protocols import ESGProtocol
7
+ from wbfdm.dataloaders.types import ESGDataDict
8
+ from wbfdm.enums import ESG
9
+
10
+ from ..client import MSCIClient
11
+
12
+
13
+ class MSCIESG(enum.Enum):
14
+ CARBON_EMISSIONS_SCOPE_1 = "CARBON_EMISSIONS_SCOPE_1"
15
+ CARBON_EMISSIONS_SCOPE_1_KEY = "CARBON_EMISSIONS_SCOPE_1_KEY"
16
+ CARBON_EMISSIONS_SCOPE_2 = "CARBON_EMISSIONS_SCOPE_2"
17
+ CARBON_EMISSIONS_SCOPE_2_KEY = "CARBON_EMISSIONS_SCOPE_2_KEY"
18
+ CARBON_EMISSIONS_SCOPE_3_TOTAL = "CARBON_EMISSIONS_SCOPE_3_TOTAL"
19
+ CARBON_EMISSIONS_SCOPE_3_YEAR = "CARBON_EMISSIONS_SCOPE_3_ESTIMATES_YEAR"
20
+ CARBON_EMISSIONS_SCOPE123 = "CARBON_EMISSIONS_SCOPE123"
21
+ CARBON_EMISSIONS_SCOPE123_KEY = "CARBON_EMISSIONS_SCOPE123_KEY"
22
+ CARBON_EMISSIONS_SALES_EUR_SCOPE_ALL = "CARBON_EMISSIONS_SALES_EUR_SCOPE123_INTEN"
23
+ CARBON_EMISSIONS_SCOPE123_EVIC_EUR = "CARBON_EMISSIONS_EVIC_EUR_SCOPE123_INTEN"
24
+ CARBON_EMISSIONS_SOURCE = "CARBON_EMISSIONS_SOURCE"
25
+ CARBON_EMISSIONS_YEAR = "CARBON_EMISSIONS_YEAR"
26
+ ACTIVE_FF_SECTOR_EXPOSURE = "ACTIVE_FF_SECTOR_EXPOSURE"
27
+ ACTIVE_FF_SECTOR_EXPOSURE_SOURCE = "ACTIVE_FF_SECTOR_EXPOSURE_SOURCE"
28
+ ACTIVE_FF_SECTOR_EXPOSURE_YEAR = "ACTIVE_FF_SECTOR_EXPOSURE_YEAR"
29
+ PCT_NON_RENEW_CONSUMPTION_PRODUCTION = "PCT_NONRENEW_CONSUMP_PROD"
30
+ PCT_TOTAL_NON_RENEW_CONSUMPTION = "PCT_TOTL_ERGY_CONSUMP_NONRENEW"
31
+ PCT_TOTAL_NON_RENEW_PRODUCTION = "PCT_TOTL_ERGY_PRODUCT_NONRENEW"
32
+ TOTAL_RENEW_ENERGY_CONSUMPTION = "TOTL_ERGY_CONSUMP_RENEW_GWH"
33
+ TOTAL_RENEW_ENERGY_CONSUMPTION_SOURCE = "TOTL_ERGY_CONSUMP_RENEW_GWH_SOURCE"
34
+ TOTAL_RENEW_ENERGY_CONSUMPTION_YEAR = "TOTL_ERGY_CONSUMP_RENEW_GWH_YEAR"
35
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION = "TOTL_ERGY_CONSUMP_NONRENEW_GWH"
36
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION_SOURCE = "TOTL_ERGY_CONSUMP_NONRENEW_GWH_SOURCE"
37
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION_YEAR = "TOTL_ERGY_CONSUMP_NONRENEW_GWH_YEAR"
38
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION_PRODUCTION = "TOTL_ERGY_CONSUMP_PRODUCT_NONRENEW_GWH"
39
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION_PRODUCTION_SOURCE = "TOTL_ERGY_CONSUMP_PRODUCT_NONRENEW_GWH_SOURCE"
40
+ TOTAL_NON_RENEW_ENERGY_CONSUMPTION_PRODUCTION_YEAR = "TOTL_ERGY_CONSUMP_PRODUCT_NONRENEW_GWH_YEAR"
41
+ ENERGY_CONSUMPTION_INTENSITY_EUR = "ENERGY_CONSUMP_INTEN_EUR"
42
+ ENERGY_CONSUMPTION_INTENSITY_EUR_SOURCE = "ENERGY_CONSUMP_INTEN_EUR_SOURCE"
43
+ ENERGY_CONSUMPTION_INTENSITY_EUR_YEAR = "ENERGY_CONSUMP_INTEN_EUR_YEAR"
44
+ TOTAL_ENERGY_CONSUMPTION = "TOTL_ERGY_CONSUMP_GWH"
45
+ TOTAL_ENERGY_CONSUMPTION_SOURCE = "TOTL_ERGY_CONSUMP_GWH_SOURCE"
46
+ TOTAL_ENERGY_CONSUMPTION_YEAR = "TOTL_ENRGY_CONSUMP_YEAR"
47
+ OPS_BIODIV_CONTROVERSITIES = "OPS_PROT_BIODIV_CONTROVS"
48
+ OPS_BIODIV_AREAS = "OPS_PROT_BIODIV_AREAS"
49
+ WATER_EMISSIONS = "WATER_EM_EFF_METRIC_TONS"
50
+ WATER_EMISSIONS_YEAR = "WATER_EM_EFF_METRIC_TONS_YEAR"
51
+ WATER_EMISSIONS_SOURCE = "WATER_EM_EFF_METRIC_TONS_SOURCE"
52
+ HAZARD_WASTE = "HAZARD_WASTE_METRIC_TON"
53
+ HAZARD_WASTE_YEAR = "HAZARD_WASTE_METRIC_TON_YEAR"
54
+ HAZARD_WASTE_SOURCE = "HAZARD_WASTE_METRIC_TON_SOURCE"
55
+ OECD_ALIGNMENT = "OECD_ALIGNMENT"
56
+ LABOR_DDP = "LABOR_DDIL_POL_ILO"
57
+ COMPLIANCE_GLOBAL_IMPACT = "MECH_UN_GLOBAL_COMPACT"
58
+ GENDER_PAY_GAP_RATIO = "GENDER_PAY_GAP_RATIO"
59
+ GENDER_PAY_GAP_RATIO_YEAR = "GENDER_PAY_GAP_YEAR"
60
+ GENDER_PAY_GAP_RATIO_SOURCE = "GENDER_PAY_GAP_RATIO_SOURCE"
61
+ PCT_FEMALE_DIRECTORS = "FEMALE_DIRECTORS_PCT"
62
+ CONTROVERSIAL_WEAPONS = "CONTRO_WEAP_CBLMBW_ANYTIE"
63
+ CONTROVERSIAL_WEAPONS_SOURCE = "CONTRO_WEAP_CBLMBW_ANYTIE_SOURCE"
64
+ CVI_FACTOR = "CVI_FACTOR"
65
+ EVIC_EUR = "EVIC_EUR"
66
+ NACE_SECTION_CODE = "NACE_SECTION_CODE"
67
+
68
+
69
+ class MSCIESGDataloader(ESGProtocol, Dataloader):
70
+ CACHE_IDENTIFIER_KEY: str = "instrument_id"
71
+ CACHE_SYMBOL_KEY: str = "factor_code"
72
+ CACHE_VALUE_KEY: str = "value"
73
+
74
+ def esg(self, values: list[ESG]) -> Iterator[ESGDataDict]:
75
+ msci_client_id = getattr(settings, "MSCI_CLIENT_ID", None)
76
+ msci_client_secret = getattr(settings, "MSCI_CLIENT_SECRET", None)
77
+ if msci_client_id and msci_client_secret:
78
+ client = MSCIClient(msci_client_id, msci_client_secret)
79
+ lookup = {k: v for k, v in self.entities.values_list("dl_parameters__esg__parameters", "id")}
80
+ for row in client.esg(list(lookup.keys()), [MSCIESG[value.name].value for value in values]):
81
+ if instrument_id := lookup.get(row["ISSUER_ISIN"], None):
82
+ for value in values:
83
+ yield ESGDataDict(
84
+ instrument_id=instrument_id,
85
+ factor_code=value.value,
86
+ value=row.get(MSCIESG[value.name].value, None),
87
+ )