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,217 @@
1
+ from dataclasses import dataclass
2
+ from datetime import date
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ from django.utils.functional import cached_property
7
+ from wbcore.contrib.currency.models import Currency, CurrencyFXRates
8
+ from wbfdm.enums import ESG
9
+ from wbfdm.models import Instrument
10
+
11
+ from .enums import AggregationMethod, ESGAggregation
12
+ from .utils import get_esg_df
13
+
14
+
15
+ @dataclass
16
+ class Log:
17
+ series: pd.Series
18
+ label: str
19
+ is_percent: bool = False
20
+ precision: int = 4
21
+ group: str | None = None
22
+
23
+
24
+ class DataLoader:
25
+ def __init__(
26
+ self, weights: pd.Series, esg_data: pd.Series, val_date: date, total_value_fx_usd: pd.Series | None = None
27
+ ):
28
+ self.weights = weights
29
+ self.val_date = val_date
30
+ try:
31
+ self.fx_rate_usd_to_eur = float(
32
+ CurrencyFXRates.objects.get(currency=Currency.objects.get(key="EUR"), date=val_date).value
33
+ )
34
+ except (CurrencyFXRates.DoesNotExist, Currency.DoesNotExist):
35
+ self.fx_rate_usd_to_eur = 1.0
36
+ self.total_value_fx_usd = total_value_fx_usd
37
+ self.instruments = Instrument.objects.filter(id__in=self.weights.index)
38
+ self.empty_series = pd.Series(1.0, index=weights.index, dtype="float64")
39
+ self.esg_data = esg_data
40
+ self.weights_in_coverage = (
41
+ self.weights.loc[~esg_data.reindex(self.weights.index, fill_value=None).isnull()]
42
+ ).rename("weights_in_coverage")
43
+ self.intermediary_logs: list[Log] = []
44
+ self.extra_esg_data_logs: list[Log] = []
45
+
46
+ @cached_property
47
+ def enterprise_value_included_cash(self) -> pd.Series:
48
+ data = get_esg_df(self.instruments, ESG.EVIC_EUR) * 1000000
49
+ self.extra_esg_data_logs.append(Log(series=data.rename("enterprise_value_included_cash"), label="EVIC (EUR)"))
50
+ return data
51
+
52
+ @cached_property
53
+ def current_value_investment_factor(self) -> pd.Series:
54
+ data = get_esg_df(self.instruments, ESG.CVI_FACTOR)
55
+ data = data.reindex(self.weights.index, fill_value=1.0)
56
+ self.extra_esg_data_logs.append(Log(series=data.rename("current_value_investment_factor"), label="CVI Factor"))
57
+ return data
58
+
59
+ @cached_property
60
+ def nace_section_code(self) -> pd.Series:
61
+ data = get_esg_df(self.instruments, ESG.NACE_SECTION_CODE)
62
+ self.extra_esg_data_logs.append(Log(series=data.rename("nace_section_code"), label="NACE_SECTION_CODE"))
63
+ return data
64
+
65
+ def _get_percentage_sum(self, mask_value: str) -> pd.Series:
66
+ df = self.weights_in_coverage.copy()
67
+ df.loc[self.esg_data != mask_value] = 0.0
68
+ return df.dropna()
69
+
70
+ def _get_weighted_avg_normalized(self) -> pd.Series:
71
+ weights_normalized = self.weights_in_coverage / self.weights_in_coverage.sum()
72
+ self.intermediary_logs.append(
73
+ Log(
74
+ series=weights_normalized.rename("weights_normalized"),
75
+ label="Rebased Weights",
76
+ is_percent=True,
77
+ precision=2,
78
+ )
79
+ )
80
+ return (weights_normalized * self.esg_data).dropna()
81
+
82
+ def _get_weighted_avg_normalized_per_category(self) -> pd.Series:
83
+ df = pd.concat([self.weights, self.nace_section_code], keys=["weighting", "nace_section_code"], axis=1)
84
+ sector_codes = ["C", "D", "F", "G", "J", "K", "M", "N", "Q", "S"]
85
+ res = pd.Series(np.nan, index=df.index)
86
+ rebased_weights_logs_per_sector = []
87
+ weighted_average_logs_per_sector = []
88
+ for sector_code in sector_codes:
89
+ try:
90
+ dff = df[df["nace_section_code"] == sector_code]
91
+ weights_normalized_per_sector = dff["weighting"] / dff["weighting"].sum()
92
+ except (TypeError, KeyError):
93
+ weights_normalized_per_sector = pd.Series(np.nan, index=self.weights.index)
94
+
95
+ rebased_weights_logs_per_sector.append(
96
+ Log(
97
+ series=weights_normalized_per_sector.rename(f"weights_normalized_sector_{sector_code.lower()}"),
98
+ label=sector_code,
99
+ group="Weight rebased per NACE sector",
100
+ is_percent=True,
101
+ precision=2,
102
+ )
103
+ )
104
+ weighted_average_per_sector = weights_normalized_per_sector * self.esg_data
105
+ weighted_average_logs_per_sector.append(
106
+ Log(
107
+ series=weighted_average_per_sector.rename(f"weighted_average_sector_{sector_code.lower()}"),
108
+ label=sector_code,
109
+ group="Weight Average per NACE sector",
110
+ precision=4,
111
+ )
112
+ )
113
+
114
+ res[weighted_average_per_sector.dropna().index] = weighted_average_per_sector.dropna()
115
+ self.intermediary_logs.extend(rebased_weights_logs_per_sector)
116
+ self.intermediary_logs.extend(weighted_average_logs_per_sector)
117
+ return res.dropna()
118
+
119
+ def _get_investor_allocation(self, exposure: pd.Series) -> pd.Series:
120
+ exposure_eur = exposure * self.fx_rate_usd_to_eur
121
+ self.intermediary_logs.append(Log(series=exposure.rename("exposure"), label="Exposure"))
122
+ self.intermediary_logs.append(Log(series=exposure_eur.rename("exposure_eur"), label="Exposure (EUR)"))
123
+
124
+ evic_eur = self.enterprise_value_included_cash
125
+ cvi_factor = self.current_value_investment_factor
126
+
127
+ exposure_with_cvi = exposure_eur * cvi_factor
128
+ self.intermediary_logs.append(
129
+ Log(series=exposure_with_cvi.rename("exposure_with_cvi"), label="Exposure With CVI")
130
+ )
131
+ try:
132
+ rebase_factor = exposure_with_cvi.loc[self.weights_in_coverage.index].sum() / exposure_with_cvi.sum()
133
+ exposure_with_cvi_normalized = exposure_with_cvi.loc[self.weights_in_coverage.index] / rebase_factor
134
+ except ZeroDivisionError:
135
+ exposure_with_cvi_normalized = exposure_with_cvi
136
+
137
+ self.intermediary_logs.append(
138
+ Log(
139
+ series=exposure_with_cvi_normalized.rename("exposure_with_cvi_normalized"),
140
+ label="Exposure With CVI (Normalized)",
141
+ )
142
+ )
143
+
144
+ attribution_factor = exposure_with_cvi_normalized / evic_eur
145
+ self.intermediary_logs.append(
146
+ Log(series=attribution_factor.rename("attribution_factor"), label="Attribution Factor", is_percent=True)
147
+ )
148
+
149
+ return (self.esg_data * attribution_factor).dropna()
150
+
151
+ def _get_investor_allocation_per_million(self, exposure: pd.Series) -> pd.Series:
152
+ self.intermediary_logs.append(Log(series=exposure.rename("exposure"), label="Exposure"))
153
+
154
+ evic_eur = self.enterprise_value_included_cash
155
+ cvi_factor = self.current_value_investment_factor
156
+
157
+ exposure_with_cvi = exposure * cvi_factor
158
+ self.intermediary_logs.append(
159
+ Log(series=exposure_with_cvi.rename("exposure_with_cvi"), label="Exposure With CVI")
160
+ )
161
+ try:
162
+ rebase_factor = exposure_with_cvi.loc[self.weights_in_coverage.index].sum() / exposure_with_cvi.sum()
163
+ exposure_with_cvi_normalized = exposure_with_cvi.loc[self.weights_in_coverage.index] / rebase_factor
164
+ except ZeroDivisionError:
165
+ exposure_with_cvi_normalized = exposure_with_cvi
166
+
167
+ self.intermediary_logs.append(
168
+ Log(
169
+ series=exposure_with_cvi_normalized.rename("exposure_with_cvi_normalized"),
170
+ label="Exposure With CVI (Normalized)",
171
+ )
172
+ )
173
+
174
+ weights_normalized = exposure_with_cvi_normalized / exposure_with_cvi_normalized.sum()
175
+ self.intermediary_logs.append(
176
+ Log(
177
+ series=weights_normalized.rename("weights_normalized"),
178
+ label="Weight based on CVI",
179
+ is_percent=True,
180
+ precision=2,
181
+ )
182
+ )
183
+ cvi_based_exposure_per_million = weights_normalized * 1e6
184
+ self.intermediary_logs.append(
185
+ Log(
186
+ series=cvi_based_exposure_per_million.rename("cvi_based_exposure_per_million"),
187
+ label="CVI-based exposure for EUR 1mn",
188
+ )
189
+ )
190
+
191
+ attribution_factor = cvi_based_exposure_per_million / evic_eur
192
+ self.intermediary_logs.append(
193
+ Log(series=attribution_factor.rename("attribution_factor"), label="Attribution Factor", is_percent=True)
194
+ )
195
+
196
+ return (self.esg_data * attribution_factor).dropna()
197
+
198
+ def compute(self, esg_aggregation: ESGAggregation):
199
+ aggregation_method = esg_aggregation.get_aggregation()
200
+ if aggregation_method == AggregationMethod.PERCENTAGE_SUM:
201
+ # we need to apply a mask
202
+ if esg_aggregation in [ESGAggregation.LACK_OF_PROCESS_AND_COMPLIANCE_OF_UN_PRINCIPLES]:
203
+ mask = "No Evidence"
204
+ else:
205
+ mask = "Yes"
206
+
207
+ return self._get_percentage_sum(mask)
208
+ if aggregation_method == AggregationMethod.WEIGHTED_AVG_NORMALIZED:
209
+ return self._get_weighted_avg_normalized()
210
+ if aggregation_method == AggregationMethod.WEIGHTED_AVG_CATEGORY_NORMALIZED:
211
+ return self._get_weighted_avg_normalized_per_category()
212
+ if aggregation_method == AggregationMethod.INVESTOR_ALLOCATION:
213
+ return self._get_investor_allocation(self.total_value_fx_usd)
214
+ if aggregation_method == AggregationMethod.INVESTOR_ALLOCATION_PER_MILLION:
215
+ return self._get_investor_allocation_per_million(self.weights * 1000000)
216
+ else:
217
+ raise ValueError("Aggregation Method not supported")
@@ -0,0 +1,13 @@
1
+ import pandas as pd
2
+ from wbfdm.enums import ESG
3
+ from wbfdm.models.instruments.instruments import InstrumentQuerySet
4
+
5
+
6
+ def get_esg_df(instruments: InstrumentQuerySet, esg: ESG, **kwargs) -> pd.Series:
7
+ df = pd.DataFrame(instruments.dl.esg(values=[esg]))
8
+ if not df.empty:
9
+ return df.pivot_table(
10
+ index="instrument_id", values="value", columns="factor_code", aggfunc="first", dropna=False
11
+ )[esg.value]
12
+ else:
13
+ return pd.Series(dtype="float64")
@@ -0,0 +1 @@
1
+ from .financial_statistics_analysis import FinancialStatistics
@@ -0,0 +1,88 @@
1
+ from datetime import date
2
+
3
+ import pandas as pd
4
+ from wbfdm.enums import Financial, PeriodType, SeriesType
5
+ from wbfdm.models import Instrument
6
+ from wbfdm.utils import rename_period_index_level_to_repr
7
+
8
+
9
+ def financial_metric_estimate_analysis(instrument_id: int, financial: Financial) -> tuple[pd.DataFrame, dict, list]:
10
+ estimate_mapping = {}
11
+ columns = []
12
+ if not (
13
+ df := pd.DataFrame(
14
+ Instrument.objects.filter(id=instrument_id).dl.financials(
15
+ values=[financial],
16
+ series_type=SeriesType.FULL_ESTIMATE,
17
+ period_type=PeriodType.ALL,
18
+ from_year=date.today().year - 5,
19
+ ),
20
+ )
21
+ ).empty:
22
+ df = df.pivot_table(
23
+ index=["year", "interim", "period_type", "estimate"],
24
+ columns=["financial"],
25
+ values=["value", "difference_pct", "value_high", "value_low", "value_amount", "value_stdev"],
26
+ dropna=False,
27
+ )
28
+ df["difference_pct"] = df["difference_pct"] * 100
29
+ df = rename_period_index_level_to_repr(df)
30
+ df = df.set_index([[f"{index[0]}-{index[1]}" for index in df.index]])
31
+
32
+ columns = list(df.index)
33
+
34
+ df = df[["value", "difference_pct", "value_high", "value_low", "value_stdev", "value_amount"]]
35
+ df = df.rename(
36
+ columns={
37
+ "difference_pct": "Diff (%)",
38
+ "value": "Estimate",
39
+ "value_amount": "#",
40
+ "value_high": "High Est.",
41
+ "value_low": "Low Est.",
42
+ "value_stdev": "St.Dev. Est.",
43
+ }
44
+ )
45
+ df = df.T
46
+
47
+ df["financial"] = [index[0] for index in df.index]
48
+ df = df.set_index([[f"{index[0]}-{index[1]}" for index in df.index]])
49
+ df = df.reset_index()
50
+
51
+ return df, estimate_mapping, columns
52
+
53
+
54
+ def financial_metric_growths(instrument_id: int, financial: Financial):
55
+ # Get the base dataframe from a dataloader with the given financial
56
+ if not (
57
+ df := pd.DataFrame(
58
+ Instrument.objects.filter(id=instrument_id).dl.financials(
59
+ values=[financial],
60
+ series_type=SeriesType.COMPLETE,
61
+ period_type=PeriodType.ALL,
62
+ from_year=date.today().year - 5,
63
+ )
64
+ )
65
+ ).empty:
66
+ # Pivot the dataframe to get the financial in the correct format
67
+ df = df.pivot_table(index=["year", "interim", "period_type"], columns="financial", values="value")
68
+
69
+ # Compute the growth factors
70
+ df[f"{financial.value}_qq"] = df.loc[df.index.get_level_values("interim") != 0].pct_change() * 100
71
+ df[f"{financial.value}_yy_y"] = (
72
+ df.loc[df.index.get_level_values("interim") == 0, financial.value].pct_change() * 100
73
+ )
74
+ df[f"{financial.value}_yy_q"] = (
75
+ df.loc[df.index.get_level_values("interim") != 0, financial.value].pct_change(4) * 100
76
+ )
77
+ df[f"{financial.value}_yy"] = df[f"{financial.value}_yy_y"].combine_first(df[f"{financial.value}_yy_q"])
78
+
79
+ # Select on the two growth columns
80
+ df = df[[f"{financial.value}_yy", f"{financial.value}_qq"]]
81
+
82
+ df = rename_period_index_level_to_repr(df)
83
+ df = df.set_index([[f"{index[0]}-{index[1]}" for index in df.index]])
84
+ df = df.rename(columns={f"{financial.value}_yy": "YoY Growth (%)", f"{financial.value}_qq": "QoQ Growth (%)"})
85
+ df = df.T
86
+ df = df.reset_index().reset_index()
87
+
88
+ return df
@@ -0,0 +1,125 @@
1
+ from contextlib import suppress
2
+ from datetime import date
3
+
4
+ import pandas as pd
5
+ from django.db.models import TextChoices
6
+ from wbfdm.enums import Financial, MarketData, PeriodType
7
+ from wbfdm.models import Instrument
8
+
9
+
10
+ class FinancialRatio(TextChoices):
11
+ PE = "pe", "P/E"
12
+ PS = "ps", "P/S"
13
+ PB = "pb", "P/B"
14
+ PFCF = "pfcf", "P/FCF"
15
+
16
+ @classmethod
17
+ def get_financials_for_ratio(cls, ratio: "FinancialRatio") -> list[Financial]:
18
+ financials = {
19
+ cls.PE: [Financial.EPS],
20
+ cls.PS: [Financial.REVENUE, Financial.SHARES_OUTSTANDING],
21
+ cls.PB: [Financial.TANGIBLE_BOOK_VALUE_PER_SHARE],
22
+ cls.PFCF: [Financial.CASH_FLOW_FROM_OPERATIONS, Financial.CAPEX],
23
+ }
24
+
25
+ return financials[ratio]
26
+
27
+ @classmethod
28
+ def get_financials(cls, ratios: list["FinancialRatio"]) -> list[Financial]:
29
+ financials = list()
30
+ for ratio in ratios:
31
+ financials.extend(cls.get_financials_for_ratio(ratio))
32
+ return list(set(financials))
33
+
34
+ def compute_pe(self, df: pd.DataFrame) -> pd.DataFrame:
35
+ if Financial.EPS.value in df:
36
+ df[self.value] = df["close"] / df[Financial.EPS.value]
37
+ return df
38
+
39
+ def compute_ps(self, df: pd.DataFrame) -> pd.DataFrame:
40
+ if "revenue_per_share" in df:
41
+ df[self.value] = df["close"] / df["revenue_per_share"]
42
+ return df
43
+
44
+ def compute_pb(self, df: pd.DataFrame) -> pd.DataFrame:
45
+ if Financial.TANGIBLE_BOOK_VALUE_PER_SHARE.value in df:
46
+ df[self.value] = df["close"] / df[Financial.TANGIBLE_BOOK_VALUE_PER_SHARE.value]
47
+ return df
48
+
49
+ def compute_pfcf(self, df: pd.DataFrame) -> pd.DataFrame:
50
+ if Financial.FREE_CASH_FLOW_PER_SHARE.value in df:
51
+ df[self.value] = df["close"] / df[Financial.FREE_CASH_FLOW_PER_SHARE.value]
52
+ return df
53
+
54
+ def compute(self, df: pd.DataFrame) -> pd.DataFrame:
55
+ compute_methods = {
56
+ self.PE: self.compute_pe,
57
+ self.PS: self.compute_ps,
58
+ self.PB: self.compute_pb,
59
+ self.PFCF: self.compute_pfcf,
60
+ }
61
+ return compute_methods[self](df)
62
+
63
+
64
+ def get_financial_ratios(
65
+ instrument_id: int, ratios: list[FinancialRatio], from_date: date, to_date: date, ttm: bool = True
66
+ ):
67
+ """Computes financial ratios and adds them to the dataframe"""
68
+ if ttm:
69
+ financials_df = pd.DataFrame(
70
+ Instrument.objects.filter(id=instrument_id).dl.statements(
71
+ financials=FinancialRatio.get_financials(ratios),
72
+ period_type=PeriodType.INTERIM,
73
+ )
74
+ )
75
+ else:
76
+ financials_df = pd.DataFrame(
77
+ Instrument.objects.filter(id=instrument_id).dl.financials(
78
+ values=FinancialRatio.get_financials(ratios),
79
+ period_type=PeriodType.INTERIM,
80
+ )
81
+ )
82
+ if financials_df.empty:
83
+ return pd.DataFrame()
84
+ financials_df = financials_df.pivot_table(
85
+ index="period_end_date",
86
+ columns="financial",
87
+ values="value",
88
+ ).sort_index()
89
+ financials_df.index = pd.to_datetime(financials_df.index)
90
+ if (
91
+ FinancialRatio.PS in ratios
92
+ and Financial.REVENUE.value in financials_df
93
+ and Financial.SHARES_OUTSTANDING.value in financials_df
94
+ ):
95
+ financials_df["revenue_per_share"] = (
96
+ financials_df[Financial.REVENUE.value] / financials_df[Financial.SHARES_OUTSTANDING.value]
97
+ )
98
+ if (
99
+ FinancialRatio.PFCF in ratios
100
+ and Financial.FREE_CASH_FLOW_PER_SHARE.value not in financials_df.columns
101
+ and Financial.CASH_FLOW_FROM_OPERATIONS.value in financials_df
102
+ and Financial.CAPEX.value in financials_df
103
+ ):
104
+ financials_df[Financial.FREE_CASH_FLOW_PER_SHARE.value] = (
105
+ financials_df[Financial.CASH_FLOW_FROM_OPERATIONS.value] - financials_df[Financial.CAPEX.value]
106
+ )
107
+ financials_df = financials_df.rolling("365d").sum()
108
+ prices_df = pd.DataFrame(
109
+ Instrument.objects.filter(id=instrument_id).dl.market_data(
110
+ values=[MarketData.CLOSE], from_date=from_date, to_date=to_date
111
+ )
112
+ )
113
+ if not prices_df.empty:
114
+ prices_df = prices_df.set_index("valuation_date").sort_index()[["close"]]
115
+ prices_df.index = pd.to_datetime(prices_df.index)
116
+
117
+ financials_df = pd.merge_asof(
118
+ prices_df, financials_df, left_index=True, right_index=True, direction="backward" if ttm else "forward"
119
+ )
120
+
121
+ for ratio in ratios:
122
+ with suppress(KeyError):
123
+ financials_df = ratio.compute(financials_df)
124
+
125
+ return financials_df