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,202 @@
1
+ import calendar
2
+ from datetime import date, timedelta
3
+ from decimal import Decimal
4
+ from unittest.mock import patch
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ import pytest
9
+ from faker import Faker
10
+ from pandas.tseries.offsets import BDay
11
+ from wbfdm.dataloaders.proxies import InstrumentDataloaderProxy
12
+ from wbfdm.factories.instrument_prices import InstrumentPrice
13
+ from wbfdm.models import Instrument, RelatedInstrumentThroughModel
14
+
15
+ fake = Faker()
16
+
17
+
18
+ @pytest.mark.django_db
19
+ class TestInstrumentModel:
20
+ def test_init(self, instrument):
21
+ assert instrument.id is not None
22
+
23
+ def test_get_prices(self, instrument_factory, instrument_price_factory):
24
+ instrument = instrument_factory.create()
25
+ other_instrument = instrument_factory.create()
26
+ price = instrument_price_factory.create(instrument=instrument, outstanding_shares=100)
27
+ price.refresh_from_db()
28
+ instrument_price_factory.create(instrument=other_instrument) # Noise
29
+ res = list(instrument.get_prices())
30
+ assert len(res) == 1
31
+ assert res[0]["valuation_date"] == price.date
32
+ assert res[0]["open"] == float(price.net_value)
33
+ assert res[0]["close"] == float(price.net_value)
34
+ assert res[0]["high"] == float(price.net_value)
35
+ assert res[0]["low"] == float(price.net_value)
36
+ assert res[0]["volume"] == price.volume
37
+ assert res[0]["market_capitalization"] == price.market_capitalization
38
+ assert res[0]["outstanding_shares"] == float(price.outstanding_shares)
39
+
40
+ def test_get_price(self, instrument_factory, instrument_price_factory):
41
+ instrument = instrument_factory.create()
42
+ other_instrument = instrument_factory.create()
43
+ price = instrument_price_factory.create(instrument=instrument)
44
+ price.refresh_from_db()
45
+ instrument_price_factory.create(instrument=other_instrument) # Noise
46
+ assert instrument.get_price(price.date) == float(price.net_value)
47
+ assert instrument.get_price((price.date + BDay(1)).date()) == float(price.net_value)
48
+ assert instrument.get_price((price.date + BDay(2)).date()) == float(price.net_value)
49
+ assert instrument.get_price((price.date + BDay(3)).date()) == float(price.net_value)
50
+ with pytest.raises(ValueError):
51
+ instrument.get_price((price.date + BDay(4)).date()) # for return the latest valid price 3 days earlier.
52
+
53
+ # if the instrument is considered cash, we always return a value of 1
54
+ instrument.is_cash = True
55
+ instrument.save()
56
+ assert instrument.get_price(price.date) == Decimal(1)
57
+
58
+ def test_extract_daily_performance_df(self):
59
+ tidx = pd.date_range("2016-07-01", periods=4, freq="B")
60
+ data = np.random.randn(4)
61
+ df = pd.Series(data, index=tidx, name="close")
62
+ res = Instrument.extract_daily_performance_df(df)
63
+ assert res.performance[0] == df[1] / df[0] - 1
64
+
65
+ def test_extract_monthly_performance_df(self):
66
+ tidx = pd.date_range("2016-07-01", periods=4, freq="ME")
67
+ data = np.random.randn(4)
68
+ df = pd.Series(data, index=tidx, name="close")
69
+ df.index = pd.to_datetime(df.index)
70
+ res = Instrument.extract_monthly_performance_df(df)
71
+ assert res.performance[0] == df[1] / df[0] - 1
72
+
73
+ def test_extract_inception_performance_df(self):
74
+ tidx = pd.date_range("2016-07-01", periods=4, freq="D")
75
+ data = np.random.randn(4)
76
+ df = pd.Series(data, index=tidx, name="close")
77
+ res = Instrument.extract_inception_performance_df(df)
78
+
79
+ assert res == df[3] / df[0] - 1
80
+
81
+ def test_extract_annual_performance_df(self):
82
+ tidx = pd.date_range("2016-07-01", periods=4, freq="BYE")
83
+ data = np.random.randn(4)
84
+ df = pd.Series(data, index=tidx, name="close")
85
+ res = Instrument.extract_annual_performance_df(df)
86
+
87
+ assert res.performance[0] == df[1] / df[0] - 1
88
+
89
+ def test_get_monthly_return_summary(self, instrument, instrument_price_factory):
90
+ instrument_price_factory.create_batch(10, instrument=instrument)
91
+ res, _ = instrument.get_monthly_return_summary()
92
+ assert "performance" in res.columns
93
+ assert "month" in res.columns
94
+ assert "year" in res.columns
95
+
96
+ def test_get_monthly_return_summary_dict(self, instrument, instrument_price_factory):
97
+ instrument_price_factory.create_batch(10, instrument=instrument)
98
+ res = instrument.get_monthly_return_summary_dict()
99
+ for year in res.keys():
100
+ for month in res[year].keys():
101
+ assert month in calendar.month_abbr or month == "annual"
102
+ assert "performance" in res[year][month]
103
+
104
+ def test_get_prices_df(self, instrument, instrument_price_factory):
105
+ price = instrument_price_factory.create(instrument=instrument)
106
+ previous_price = instrument_price_factory.create( # noqa
107
+ date=price.date - timedelta(days=1), instrument=instrument
108
+ )
109
+ assert instrument.get_prices_df(from_date=price.date)[0] == float(price.net_value)
110
+
111
+ @patch.object(Instrument, "get_prices_df")
112
+ def test_build_benchmark_df(self, mock_get_prices_df, instrument, instrument_factory, instrument_price_factory):
113
+ instrument_price_factory.create_batch(5, instrument=instrument)
114
+ start_val = InstrumentPrice.objects.earliest("date").date
115
+ end_val = InstrumentPrice.objects.latest("date").date
116
+ tidx = pd.date_range(start=start_val, end=end_val, freq="D")
117
+ mock_data = pd.Series(data=np.random.random_sample((tidx.shape[0],)), index=tidx)
118
+ mock_get_prices_df.return_value = mock_data
119
+
120
+ # Test without risk or benchmark assigned to instrument
121
+ res = instrument.build_benchmark_df(end_val)
122
+ assert res.empty
123
+
124
+ # Assign benchmark and risk instrument
125
+ RelatedInstrumentThroughModel.objects.create(
126
+ instrument=instrument,
127
+ related_instrument=instrument_factory.create(),
128
+ is_primary=True,
129
+ related_type=RelatedInstrumentThroughModel.RelatedTypeChoices.BENCHMARK,
130
+ )
131
+ RelatedInstrumentThroughModel.objects.create(
132
+ instrument=instrument,
133
+ is_primary=True,
134
+ related_instrument=instrument_factory.create(),
135
+ related_type=RelatedInstrumentThroughModel.RelatedTypeChoices.RISK_INSTRUMENT,
136
+ )
137
+ instrument.refresh_from_db()
138
+ res = instrument.build_benchmark_df(end_val)
139
+ assert res.rate.equals(mock_data)
140
+ assert res.benchmark_net_value.equals(mock_data)
141
+ assert "net_value" in res.columns
142
+
143
+ @patch.object(InstrumentDataloaderProxy, "market_data")
144
+ def test_save_prices_in_db(self, mock_fct, instrument, instrument_price_factory, currency_fx_rates_factory):
145
+ # we check that the import also handle missing data with a forward fill
146
+ from_date = date(2024, 1, 1)
147
+ to_date = date(2024, 1, 3)
148
+ currency_fx_rates_factory.create(currency=instrument.currency, date=from_date)
149
+ currency_fx_rates_factory.create(currency=instrument.currency, date=from_date + timedelta(days=1))
150
+ currency_fx_rates_factory.create(currency=instrument.currency, date=to_date)
151
+ mock_fct.return_value = [
152
+ {
153
+ "valuation_date": from_date,
154
+ "close": 1,
155
+ "volume": 1,
156
+ "market_capitalization": 1,
157
+ },
158
+ {
159
+ "valuation_date": to_date,
160
+ "close": 2,
161
+ "volume": 2,
162
+ "market_capitalization": 2,
163
+ },
164
+ ]
165
+ instrument.save_prices_in_db(from_date, to_date + timedelta(days=1))
166
+ assert InstrumentPrice.objects.get(
167
+ instrument=instrument, date=from_date, calculated=False
168
+ ).net_value == Decimal(1)
169
+ assert InstrumentPrice.objects.get(
170
+ instrument=instrument, date=from_date + timedelta(days=1), calculated=True
171
+ ).net_value == Decimal(1)
172
+ assert InstrumentPrice.objects.get(instrument=instrument, date=to_date, calculated=False).net_value == Decimal(
173
+ 2
174
+ )
175
+
176
+ def test_security_instrument_type(self, instrument_factory, instrument_type_factory):
177
+ company_type = instrument_type_factory.create(is_security=False)
178
+ security_type = instrument_type_factory.create(is_security=True)
179
+ quote_type = instrument_type_factory.create(is_security=False)
180
+
181
+ company = instrument_factory.create(instrument_type=company_type)
182
+ security = instrument_factory.create(instrument_type=security_type, parent=company)
183
+ quote = instrument_factory.create(instrument_type=quote_type, parent=security)
184
+
185
+ assert company.security_instrument_type == company_type
186
+ assert security.security_instrument_type == security_type
187
+ assert quote.security_instrument_type == security_type
188
+
189
+ def test_pre_save(self, instrument, instrument_factory):
190
+ parent = instrument_factory.create()
191
+ instrument.parent = parent
192
+ instrument.save()
193
+
194
+ instrument.name_repr = "test"
195
+ instrument.save()
196
+ parent.refresh_from_db()
197
+ assert parent.name_repr == "test"
198
+
199
+ parent.name_repr = "test2"
200
+ parent.save()
201
+ instrument.refresh_from_db()
202
+ assert instrument.name_repr == "test2"
@@ -0,0 +1,99 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from wbfdm.factories.instruments import InstrumentFactory
4
+ from wbfdm.models import (
5
+ Instrument,
6
+ InstrumentClassificationThroughModel,
7
+ RelatedInstrumentThroughModel,
8
+ )
9
+
10
+ fake = Faker()
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestMergeInstrument:
15
+ @pytest.fixture()
16
+ def merged_instrument(self):
17
+ return InstrumentFactory.create()
18
+
19
+ @pytest.fixture()
20
+ def main_instrument(self):
21
+ return InstrumentFactory.create()
22
+
23
+ def test_default(
24
+ self,
25
+ main_instrument,
26
+ merged_instrument,
27
+ exchange_factory,
28
+ instrument_favorite_group_factory,
29
+ related_instrument_through_model_factory,
30
+ instrument_classification_through_model_factory,
31
+ ):
32
+ # Prepare data: add exchange
33
+ main_exchange = main_instrument.exchange
34
+
35
+ # Prepare data: Create Favorite group with the merged instrument within
36
+ favorite_group = instrument_favorite_group_factory.create(instruments=[merged_instrument])
37
+
38
+ # prepare data: related instruments
39
+ main_related_instrument_rel = related_instrument_through_model_factory.create(instrument=main_instrument)
40
+ merged_related_instrument_rel = related_instrument_through_model_factory.create(instrument=merged_instrument)
41
+ reversed_related_instrument_rel = related_instrument_through_model_factory.create(
42
+ related_instrument=merged_instrument
43
+ )
44
+
45
+ # prepare data: classification
46
+ main_classification_rel = instrument_classification_through_model_factory.create(instrument=main_instrument)
47
+ merged_classification_rel = instrument_classification_through_model_factory.create(
48
+ instrument=merged_instrument
49
+ )
50
+ reversed_classification_rel = instrument_classification_through_model_factory.create(
51
+ related_instruments=[merged_instrument]
52
+ )
53
+
54
+ main_instrument.merge(merged_instrument)
55
+
56
+ # Test if the merged instrument is indeed deleted
57
+ with pytest.raises(Instrument.DoesNotExist):
58
+ merged_instrument.refresh_from_db()
59
+
60
+ # Check Exchanges
61
+ assert {main_instrument.exchange} == {
62
+ main_exchange
63
+ } # check that the merged instrument exchange is forward to the main instrument
64
+
65
+ # Check favorite Group
66
+ assert list(favorite_group.instruments.all()) == [
67
+ main_instrument
68
+ ] # Check that the favorite group with the merged instrument removed it and included the main instrument
69
+
70
+ # Check related instruments
71
+ assert RelatedInstrumentThroughModel.objects.filter(
72
+ instrument=main_instrument,
73
+ related_instrument=merged_related_instrument_rel.related_instrument,
74
+ is_primary=merged_related_instrument_rel.is_primary,
75
+ related_type=merged_related_instrument_rel.related_type,
76
+ ).exists() # Check that the related instrument with the merged instrument removed it and included the main instrument
77
+ assert main_instrument.related_instruments.filter(
78
+ id=main_related_instrument_rel.related_instrument.id
79
+ ).exists() # Check that the main instrument related instrument is still there
80
+ assert RelatedInstrumentThroughModel.objects.filter(
81
+ related_instrument=main_instrument, instrument=reversed_related_instrument_rel.instrument
82
+ ).exists() # Check that the reverse related instrument relationship has been forward to the main instrument
83
+
84
+ # Check classification
85
+ assert InstrumentClassificationThroughModel.objects.filter(
86
+ instrument=main_instrument,
87
+ classification=merged_classification_rel.classification,
88
+ is_favorite=merged_classification_rel.is_favorite,
89
+ reason=merged_classification_rel.reason,
90
+ pure_player=merged_classification_rel.pure_player,
91
+ top_player=merged_classification_rel.top_player,
92
+ percent_of_revenue=merged_classification_rel.percent_of_revenue,
93
+ ).exists()
94
+ assert main_instrument.classifications.filter(
95
+ id=main_classification_rel.classification.id
96
+ ).exists() # Check that the main instrument classification relationship is still there
97
+ assert InstrumentClassificationThroughModel.objects.filter(
98
+ related_instruments=main_instrument, classification=reversed_classification_rel.classification
99
+ ).exists() # Check that the reverse classificaiton instrument relationship has been forward to the main instrument
@@ -0,0 +1,69 @@
1
+ import pytest
2
+ from django.forms.models import model_to_dict
3
+ from faker import Faker
4
+ from wbfdm.import_export.handlers.option import (
5
+ OptionAggregateImportHandler,
6
+ OptionImportHandler,
7
+ )
8
+ from wbfdm.models import Option, OptionAggregate
9
+
10
+ fake = Faker()
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestOptionAggregateModel:
15
+ def test_option_creation(self, option_aggregate):
16
+ assert option_aggregate.id is not None
17
+
18
+ def test_import_option(self, import_source, instrument, option_aggregate_factory):
19
+ def serialize(option_aggregate):
20
+ data = model_to_dict(option_aggregate)
21
+ data["date"] = option_aggregate.date.strftime("%Y-%m-%d")
22
+ data["instrument"] = {"id": instrument.id}
23
+ del data["id"]
24
+ del data["import_source"]
25
+ return data
26
+
27
+ option_aggregate = option_aggregate_factory.build()
28
+ data = {"data": [serialize(option_aggregate)]}
29
+ handler = OptionAggregateImportHandler(import_source)
30
+
31
+ # Import non existing data
32
+ handler.process(data)
33
+ assert OptionAggregate.objects.count() == 1
34
+
35
+ # Import already existing data
36
+ # import_source.data['data'][0]['shares'] *= 2
37
+
38
+ handler.process(data)
39
+ assert OptionAggregate.objects.count() == 1
40
+
41
+
42
+ @pytest.mark.django_db
43
+ class TestOptionModel:
44
+ def test_option_creation(self, option):
45
+ assert option.id is not None
46
+
47
+ def test_import_option(self, import_source, instrument, option_factory):
48
+ def serialize(option):
49
+ data = model_to_dict(option)
50
+ data["date"] = option.date.strftime("%Y-%m-%d")
51
+ data["expiration_date"] = option.expiration_date.strftime("%Y-%m-%d")
52
+ data["instrument"] = {"id": instrument.id}
53
+ del data["id"]
54
+ del data["import_source"]
55
+ return data
56
+
57
+ option = option_factory.build()
58
+ data = {"data": [serialize(option)]}
59
+ handler = OptionImportHandler(import_source)
60
+
61
+ # Import non existing data
62
+ handler.process(data)
63
+ assert Option.objects.count() == 1
64
+
65
+ # Import already existing data
66
+ # import_source.data['data'][0]['shares'] *= 2
67
+
68
+ handler.process(data)
69
+ assert Option.objects.count() == 1
@@ -0,0 +1,6 @@
1
+ import pytest
2
+
3
+
4
+ @pytest.mark.django_db
5
+ def test_daily_update_of_investable_universe_data(instrument):
6
+ pass
wbfdm/tests/tests.py ADDED
@@ -0,0 +1,10 @@
1
+ # from wbcore.test import default_config
2
+ #
3
+ # config = {}
4
+ # for key, value in default_config.items():
5
+ # config[key] = list(
6
+ # filter(
7
+ # lambda x: x.__module__.startswith("wbfdm") and False,
8
+ # value
9
+ # )
10
+ # )
wbfdm/urls.py ADDED
@@ -0,0 +1,222 @@
1
+ from django.urls import include, path
2
+ from wbcore.routers import WBCoreRouter
3
+ from wbfdm import viewsets
4
+
5
+ router = WBCoreRouter()
6
+ router.register(r"instrument", viewsets.InstrumentModelViewSet, basename="instrument")
7
+ router.register(
8
+ r"instrumentrepresentation", viewsets.InstrumentRepresentationViewSet, basename="instrumentrepresentation"
9
+ )
10
+ router.register(
11
+ r"instrumenttyperepresentation",
12
+ viewsets.InstrumentTypeRepresentationViewSet,
13
+ basename="instrumenttyperepresentation",
14
+ )
15
+ router.register(r"exchange", viewsets.ExchangeModelViewSet, basename="exchange")
16
+ router.register(r"exchangerepresentation", viewsets.ExchangeRepresentationViewSet, basename="exchangerepresentation")
17
+
18
+ router.register(r"instrumentrequest", viewsets.InstrumentRequestModelViewSet, basename="instrumentrequest")
19
+ router.register(
20
+ r"instrumentrequestrepresentation",
21
+ viewsets.InstrumentRequestRepresentationViewSet,
22
+ basename="instrumentrequestrepresentation",
23
+ )
24
+
25
+
26
+ router.register(
27
+ r"favoritegrouprepresentation",
28
+ viewsets.InstrumentFavoriteGroupRepresentationViewSet,
29
+ basename="favoritegrouprepresentation",
30
+ )
31
+ router.register(r"favoritegroup", viewsets.InstrumentFavoriteGroupModelViewSet, basename="favoritegroup")
32
+
33
+ router.register(
34
+ r"classificationrepresentation",
35
+ viewsets.ClassificationRepresentationViewSet,
36
+ basename="classificationrepresentation",
37
+ )
38
+ router.register(
39
+ r"classificationgrouprepresentation",
40
+ viewsets.ClassificationGroupRepresentationViewSet,
41
+ basename="classificationgrouprepresentation",
42
+ )
43
+ router.register(r"classification", viewsets.ClassificationModelViewSet, basename="classification")
44
+ router.register(r"classificationgroup", viewsets.ClassificationGroupModelViewSet, basename="classificationgroup")
45
+ router.register(r"classifiedinstrument", viewsets.ClassifiedInstrumentModelViewSet, basename="classifiedinstrument")
46
+ router.register(
47
+ r"instrumentlistrepresentation",
48
+ viewsets.InstrumentListRepresentationModelViewSet,
49
+ basename="instrumentlistrepresentation",
50
+ )
51
+ router.register(r"instrumentlist", viewsets.InstrumentListModelViewSet, basename="instrumentlist")
52
+ router.register(r"instrumentlistthrough", viewsets.InstrumentListThroughModelViewSet, basename="instrumentlistthrough")
53
+
54
+ # Financials viewsets
55
+
56
+
57
+ classification_router = WBCoreRouter()
58
+ classification_router.register(
59
+ r"instrument", viewsets.ClassificationInstrumentThroughInstrumentModelViewSet, basename="classification-instrument"
60
+ )
61
+
62
+ classification_through_router = WBCoreRouter()
63
+ classification_through_router.register(
64
+ "related_instrument", viewsets.InstrumentClassificationRelatedInstrumentModelViewSet, basename="related_instrument"
65
+ )
66
+ classification_through_router.register(
67
+ "related_instrument_representation",
68
+ viewsets.InstrumentClassificationRelatedInstrumentRepresentationViewSet,
69
+ basename="related_instrument_representation",
70
+ )
71
+
72
+
73
+ classification_group_router = WBCoreRouter()
74
+ classification_group_router.register(
75
+ r"classification",
76
+ viewsets.ClassificationClassificationGroupModelViewSet,
77
+ basename="classificationgroup-classification",
78
+ )
79
+ classification_group_router.register(
80
+ r"treechart", viewsets.ClassificationTreeChartView, basename="classificationgroup-treechart"
81
+ )
82
+ classification_group_router.register(
83
+ r"iciclechart", viewsets.ClassificationIcicleChartView, basename="classificationgroup-iciclechart"
84
+ )
85
+
86
+ parent_classification_router = WBCoreRouter()
87
+ parent_classification_router.register(
88
+ r"classification",
89
+ viewsets.ChildClassificationParentClassificationModelViewSet,
90
+ basename="classificationparent-classification",
91
+ )
92
+
93
+
94
+ router.register(
95
+ r"instrumentclassificationrelationship",
96
+ viewsets.InstrumentClassificationThroughModelViewSet,
97
+ basename="instrumentclassificationrelationship",
98
+ )
99
+ router.register(r"price", viewsets.InstrumentPriceModelViewSet, basename="price")
100
+
101
+
102
+ instrument_router = WBCoreRouter()
103
+ instrument_router.register(r"price", viewsets.InstrumentPriceInstrumentModelViewSet, basename="instrument-price")
104
+ instrument_router.register(r"children", viewsets.ChildrenInstrumentModelViewSet, basename="instrument-children")
105
+ instrument_router.register(
106
+ r"relatedinstrument",
107
+ viewsets.RelatedInstrumentThroughInstrumentModelViewSet,
108
+ basename="instrument-relatedinstrument",
109
+ )
110
+ instrument_router.register(
111
+ r"pricestatisticchart",
112
+ viewsets.InstrumentPriceInstrumentStatisticsChartView,
113
+ basename="instrument-pricestatisticchart",
114
+ )
115
+ instrument_router.register(
116
+ r"distributionreturnschart",
117
+ viewsets.InstrumentPriceInstrumentDistributionReturnsChartView,
118
+ basename="instrument-distributionreturnschart",
119
+ )
120
+ instrument_router.register(
121
+ r"bestandworstreturns",
122
+ viewsets.BestAndWorstReturnsInstrumentPandasView,
123
+ basename="instrument-bestandworstreturns",
124
+ )
125
+ instrument_router.register(
126
+ r"financialstatistics",
127
+ viewsets.FinancialStatisticsInstrumentPandasView,
128
+ basename="instrument-financialstatistics",
129
+ )
130
+ instrument_router.register(
131
+ r"summarytablechart", viewsets.SummaryTableInstrumentChartViewSet, basename="instrument-summarytablechart"
132
+ )
133
+
134
+ instrument_router.register(
135
+ r"financialsgraphchart", viewsets.FinancialsGraphInstrumentChartViewSet, basename="instrument-financialsgraphchart"
136
+ )
137
+
138
+ instrument_router.register(
139
+ r"profitabilityratioschart",
140
+ viewsets.ProfitabilityRatiosInstrumentChartViewSet,
141
+ basename="instrument-profitabilityratioschart",
142
+ )
143
+
144
+ instrument_router.register(
145
+ r"cashflowanalysistablechart",
146
+ viewsets.CashFlowAnalysisInstrumentTableViewSet,
147
+ basename="instrument-cashflowanalysistablechart",
148
+ )
149
+
150
+ instrument_router.register(
151
+ r"cashflowanalysisbarchart",
152
+ viewsets.CashFlowAnalysisInstrumentBarChartViewSet,
153
+ basename="instrument-cashflowanalysisbarchart",
154
+ )
155
+
156
+ instrument_router.register(
157
+ r"netdebtandebitdachart",
158
+ viewsets.NetDebtAndEbitdaInstrumentChartViewSet,
159
+ basename="instrument-netdebtandebitdachart",
160
+ )
161
+
162
+ instrument_router.register(
163
+ r"earningschart",
164
+ viewsets.EarningsInstrumentChartViewSet,
165
+ basename="instrument-earningschart",
166
+ )
167
+
168
+ instrument_router.register(
169
+ r"classification",
170
+ viewsets.InstrumentClassificationThroughInstrumentModelViewSet,
171
+ basename="instrument-classification",
172
+ )
173
+ instrument_router.register(
174
+ r"valuationratios", viewsets.ValuationRatiosChartView, basename="instrument-valuationratios"
175
+ )
176
+
177
+ instrument_router.register(
178
+ r"instrumentlistthrough",
179
+ viewsets.InstrumentListThroughModelInstrumentViewSet,
180
+ basename="instrument-instrumentlistthrough",
181
+ )
182
+
183
+ instrument_router.register(r"controversies", viewsets.InstrumentESGControversiesViewSet, basename="controversies")
184
+ instrument_router.register(r"pai", viewsets.InstrumentESGPAIViewSet, basename="pai")
185
+ instrument_router.register(r"officers", viewsets.OfficerViewSet, basename="officers")
186
+ instrument_router.register(r"market_data", viewsets.MarketDataChartViewSet, basename="market_data")
187
+ instrument_router.register(
188
+ r"performance_summary", viewsets.PerformanceSummaryChartViewSet, basename="performance_summary"
189
+ )
190
+ instrument_router.register(
191
+ r"financial_metric_analysis", viewsets.FinancialMetricAnalysisPandasViewSet, basename="financial_metric_analysis"
192
+ )
193
+ instrument_router.register(
194
+ r"monthly_performances", viewsets.MonthlyPerformancesInstrumentPandasViewSet, basename="monthly_performances"
195
+ )
196
+ instrument_router.register(r"valuation_ratios", viewsets.ValuationRatioChartViewSet, basename="valuation_ratios")
197
+ instrument_router.register(r"prices", viewsets.InstrumentPriceViewSet, basename="prices")
198
+
199
+ instrument_statement_router = WBCoreRouter()
200
+ instrument_statement_router.register(
201
+ r"statementwithestimates", viewsets.StatementWithEstimatesPandasViewSet, basename="statementwithestimates"
202
+ )
203
+ instrument_statement_router.register(r"statement", viewsets.StatementPandasViewSet, basename="statement")
204
+
205
+ instrument_list = WBCoreRouter()
206
+ instrument_list.register(
207
+ r"instrumentlistthrough",
208
+ viewsets.InstrumentListThroughModelInstrumentListViewSet,
209
+ basename="instrumentlist-instrumentlistthrough",
210
+ )
211
+
212
+
213
+ urlpatterns = [
214
+ path("", include(router.urls)),
215
+ path("instrument/<int:instrument_id>/", include(instrument_router.urls)),
216
+ path("classificationgroup/<int:group_id>/", include(classification_group_router.urls)),
217
+ path("classificationparent/<int:parent_id>/", include(parent_classification_router.urls)),
218
+ path("classification/<int:classification_id>/", include(classification_router.urls)),
219
+ path("classification_through/<int:classified_instrument_id>/", include(classification_through_router.urls)),
220
+ path("instrument_list/<int:instrument_list_id>/", include(instrument_list.urls)),
221
+ path("instrument/<int:instrument_id>/<str:statement>/", include(instrument_statement_router.urls)),
222
+ ]
wbfdm/utils.py ADDED
@@ -0,0 +1,54 @@
1
+ from datetime import date
2
+ from typing import TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from pandas.core.indexes.base import Index
6
+
7
+
8
+ def rename_period_index_level_to_repr(df):
9
+ """
10
+ given a dataframe with multi index (year, interim, **other), with interim as a index number from 0 - 4, rename the level 1 index to its string representation (e.g. Q for quarter, S for semester, T for trimister and Y for yearly)
11
+
12
+ Returns:
13
+ The dataframe whose index level 1 has been renamed according to its period representation
14
+
15
+ """
16
+
17
+ if "period_type" in df.index.names:
18
+ df = df.reset_index()
19
+ df["interim"] = df["period_type"] + df["interim"].astype(str)
20
+ df.loc[df["interim"] == "Y0", "interim"] = "Y"
21
+ df = df.drop(columns=["period_type"])
22
+ df = df.set_index(["year", "interim"])
23
+ else:
24
+ renamed_index = []
25
+ for row in df.index:
26
+ if row[1] == 0:
27
+ period_repr = "Y"
28
+ else:
29
+ interim_count = (
30
+ df.drop(0, level=1, errors="ignore")
31
+ .loc[(row[0], slice(None), slice(None)), :]
32
+ .index.get_level_values(1)
33
+ .max()
34
+ )
35
+ if interim_count == 2:
36
+ period_repr = f"S{row[1]}"
37
+ elif interim_count == 3:
38
+ period_repr = f"T{row[1]}"
39
+ elif interim_count == 4:
40
+ period_repr = f"Q{row[1]}"
41
+ else:
42
+ period_repr = f"P{row[1]}"
43
+
44
+ renamed_index.append((row[0], period_repr, *(row[2:] if len(row) > 2 else ())))
45
+ df = df.set_index([renamed_index])
46
+ return df
47
+
48
+
49
+ def rename_date_columns(columns: "Index", fmt: str) -> dict[str, str]:
50
+ column_mapping = dict()
51
+ for col in filter(lambda col: isinstance(col, date), columns):
52
+ column_mapping[col] = col.strftime(fmt)
53
+
54
+ return column_mapping
@@ -0,0 +1,10 @@
1
+ from .esg import InstrumentESGControversiesViewSet, InstrumentESGPAIViewSet
2
+ from .instruments import *
3
+ from .market_data import MarketDataChartViewSet, PerformanceSummaryChartViewSet
4
+ from .prices import InstrumentPriceViewSet
5
+ from .officers import OfficerViewSet
6
+
7
+ from .financial_analysis import *
8
+ from .technical_analysis import *
9
+ from .statements import *
10
+ from .exchanges import *
@@ -0,0 +1,5 @@
1
+ from .display import *
2
+ from .endpoints import *
3
+ from .titles import *
4
+ from .menus import *
5
+ from .buttons import *