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,77 @@
1
+ from reversion.views import RevisionMixin
2
+ from wbcore import viewsets
3
+ from wbcore.permissions.permissions import InternalUserPermissionMixin
4
+ from wbfdm.models.instruments.instrument_lists import (
5
+ InstrumentList,
6
+ InstrumentListThroughModel,
7
+ )
8
+ from wbfdm.serializers.instruments.instrument_lists import (
9
+ InstrumentListModelSerializer,
10
+ InstrumentListRepresentationSerializer,
11
+ InstrumentListThroughModelSerializer,
12
+ )
13
+
14
+ from ..configs.display.instrument_lists import (
15
+ InstrumentListDisplayConfig,
16
+ InstrumentListThroughModelDisplayConfig,
17
+ )
18
+ from ..configs.endpoints.instrument_lists import (
19
+ InstrumentListThroughModelEndpointConfig,
20
+ InstrumentListThroughModelInstrumentEndpointConfig,
21
+ InstrumentListThroughModelInstrumentListEndpointConfig,
22
+ )
23
+ from ..mixins import InstrumentMixin
24
+
25
+
26
+ class InstrumentListRepresentationModelViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
27
+ search_fields = ["name"]
28
+ queryset = InstrumentList.objects.all()
29
+ serializer_class = InstrumentListRepresentationSerializer
30
+
31
+
32
+ class InstrumentListModelViewSet(InternalUserPermissionMixin, RevisionMixin, viewsets.ModelViewSet):
33
+ queryset = InstrumentList.objects.all()
34
+ serializer_class = InstrumentListModelSerializer
35
+
36
+ display_config_class = InstrumentListDisplayConfig
37
+ ordering = ("name",)
38
+
39
+
40
+ class InstrumentListThroughModelViewSet(InternalUserPermissionMixin, RevisionMixin, viewsets.ModelViewSet):
41
+ queryset = InstrumentListThroughModel.objects.select_related("instrument", "instrument_list")
42
+ serializer_class = InstrumentListThroughModelSerializer
43
+ endpoint_config_class = InstrumentListThroughModelEndpointConfig
44
+
45
+ filterset_fields = {
46
+ "instrument_list": ["exact"],
47
+ "instrument": ["exact"],
48
+ "from_date": ["gte", "lte"],
49
+ "to_date": ["gte", "lte"],
50
+ "validated": ["exact"],
51
+ }
52
+ display_config_class = InstrumentListThroughModelDisplayConfig
53
+ search_fields = ["instrument__name_repr", "instrument_str"]
54
+ ordering_fields = [
55
+ "instrument_list__name",
56
+ "instrument__name",
57
+ "instrument_str",
58
+ "from_date",
59
+ "to_date",
60
+ "validated",
61
+ ]
62
+
63
+
64
+ class InstrumentListThroughModelInstrumentListViewSet(InstrumentListThroughModelViewSet):
65
+ endpoint_config_class = InstrumentListThroughModelInstrumentListEndpointConfig
66
+ ordering = ["instrument__name"]
67
+
68
+ def get_queryset(self):
69
+ return super().get_queryset().filter(instrument_list=self.kwargs["instrument_list_id"])
70
+
71
+
72
+ class InstrumentListThroughModelInstrumentViewSet(InstrumentMixin, InstrumentListThroughModelViewSet):
73
+ endpoint_config_class = InstrumentListThroughModelInstrumentEndpointConfig
74
+ ordering = ["instrument_list__name"]
75
+
76
+ def get_queryset(self):
77
+ return super().get_queryset().filter(instrument__in=self.instrument.get_family())
@@ -0,0 +1,542 @@
1
+ from decimal import Decimal
2
+ from typing import Any
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import plotly.graph_objects as go
7
+ from dateutil.relativedelta import relativedelta
8
+ from django.contrib.messages import warning
9
+ from django.db.models import Case, Exists, F, FloatField, OuterRef, When, Window
10
+ from django.db.models.functions import Lead
11
+ from rest_framework import filters
12
+ from wbcore import viewsets
13
+ from wbcore.contrib.io.viewsets import ExportPandasAPIViewSet
14
+ from wbcore.filters import DjangoFilterBackend
15
+ from wbcore.pandas import fields as pf
16
+ from wbcore.permissions.permissions import InternalUserPermissionMixin
17
+ from wbcore.utils.date import get_date_interval_from_request
18
+ from wbcore.utils.figures import (
19
+ get_default_timeserie_figure,
20
+ get_hovertemplate_timeserie,
21
+ )
22
+ from wbcore.utils.strings import format_number
23
+ from wbfdm.analysis.financial_analysis.financial_statistics_analysis import (
24
+ FinancialStatistics,
25
+ )
26
+ from wbfdm.filters.instrument_prices import (
27
+ InstrumentPriceFilterSet,
28
+ InstrumentPriceFinancialStatisticsChartFilterSet,
29
+ InstrumentPriceFrequencyFilter,
30
+ InstrumentPriceInstrumentFilterSet,
31
+ InstrumentPriceSingleBenchmarkFilterSet,
32
+ )
33
+ from wbfdm.import_export.resources.instrument_prices import (
34
+ InstrumentPriceExportResource,
35
+ )
36
+ from wbfdm.models import Instrument, InstrumentPrice
37
+ from wbfdm.serializers import (
38
+ InstrumentPriceInstrumentModelSerializer,
39
+ InstrumentPriceModelSerializer,
40
+ )
41
+ from wbfdm.viewsets.configs import (
42
+ BestAndWorstReturnsInstrumentEndpointConfig,
43
+ BestAndWorstReturnsInstrumentPandasDisplayConfig,
44
+ BestAndWorstReturnsInstrumentTitleConfig,
45
+ FinancialStatisticsInstrumentButtonConfig,
46
+ FinancialStatisticsInstrumentEndpointConfig,
47
+ FinancialStatisticsInstrumentPandasDisplayConfig,
48
+ FinancialStatisticsInstrumentTitleConfig,
49
+ InstrumentPriceButtonConfig,
50
+ InstrumentPriceDisplayConfig,
51
+ InstrumentPriceInstrumentButtonConfig,
52
+ InstrumentPriceInstrumentDistributionReturnsChartEndpointConfig,
53
+ InstrumentPriceInstrumentDistributionReturnsChartTitleConfig,
54
+ InstrumentPriceInstrumentEndpointConfig,
55
+ InstrumentPriceInstrumentTitleConfig,
56
+ InstrumentPriceStatisticsInstrumentEndpointConfig,
57
+ InstrumentPriceStatisticsInstrumentTitleConfig,
58
+ InstrumentPriceTitleConfig,
59
+ )
60
+
61
+ from ..mixins import InstrumentMixin
62
+
63
+
64
+ class InstrumentPriceModelViewSet(viewsets.ModelViewSet):
65
+ IDENTIFIER = "wbfdm:price"
66
+ filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
67
+ queryset = InstrumentPrice.objects.all()
68
+ serializer_class = InstrumentPriceModelSerializer
69
+
70
+ filterset_class = InstrumentPriceFilterSet
71
+ ordering = ["-date"]
72
+ ordering_fields = [
73
+ "date",
74
+ "gross_value",
75
+ "daily_diff_gross_value",
76
+ "net_value",
77
+ "daily_diff_net_value",
78
+ "market_capitalization",
79
+ "sharpe_ratio",
80
+ "correlation",
81
+ "beta",
82
+ "outstanding_shares_consolidated",
83
+ "volume",
84
+ "volume_50d",
85
+ ]
86
+
87
+ display_config_class = InstrumentPriceDisplayConfig
88
+ title_config_class = InstrumentPriceTitleConfig
89
+ button_config_class = InstrumentPriceButtonConfig
90
+
91
+ def get_queryset(self):
92
+ return (
93
+ super()
94
+ .get_queryset()
95
+ .annotate(
96
+ real_price_exists=Exists(
97
+ InstrumentPrice.objects.filter(
98
+ instrument=OuterRef("instrument_id"), calculated=False, date=OuterRef("date")
99
+ )
100
+ ),
101
+ currency_symbol=F("instrument__currency__symbol"),
102
+ )
103
+ .annotate_base_data()
104
+ .select_related("instrument")
105
+ )
106
+
107
+
108
+ class InstrumentPriceInstrumentModelViewSet(InstrumentMixin, InstrumentPriceModelViewSet):
109
+ IDENTIFIER = "wbfdm:instrument-price"
110
+
111
+ title_config_class = InstrumentPriceInstrumentTitleConfig
112
+ button_config_class = InstrumentPriceInstrumentButtonConfig
113
+ endpoint_config_class = InstrumentPriceInstrumentEndpointConfig
114
+
115
+ filterset_class = InstrumentPriceInstrumentFilterSet
116
+ serializer_class = InstrumentPriceInstrumentModelSerializer
117
+ IMPORT_ALLOWED = False
118
+
119
+ def get_resource_class(self):
120
+ return InstrumentPriceExportResource
121
+
122
+ def get_aggregates(self, queryset, paginated_queryset):
123
+ res = dict()
124
+ if queryset.exists():
125
+ if (first_net_value := queryset.earliest("date").net_value) and (
126
+ last_net_value := queryset.latest("date").net_value
127
+ ):
128
+ diff_net = last_net_value / first_net_value - 1 if first_net_value != 0 else 0
129
+ res["daily_diff_net_value"] = {"∆": format_number(diff_net)}
130
+ if (first_gross_value := queryset.earliest("date").gross_value) and (
131
+ last_gross_value := queryset.latest("date").gross_value
132
+ ):
133
+ diff_gross = last_gross_value / first_gross_value - 1 if first_gross_value != 0 else 0
134
+ res["daily_diff_gross_value"] = {"∆": format_number(diff_gross)}
135
+ return res
136
+
137
+ def get_queryset(self):
138
+ queryset = super().get_queryset().filter(instrument=self.instrument)
139
+ if "calculated" not in self.request.GET:
140
+ queryset = queryset.filter_only_valid_prices()
141
+ return (
142
+ queryset.annotate(
143
+ last_net_value=Window(Lead("net_value"), order_by="-date"),
144
+ last_gross_value=Window(Lead("gross_value"), order_by="-date"),
145
+ daily_diff_net_value=Case(
146
+ When(last_net_value=0, then=None),
147
+ default=F("net_value") / F("last_net_value") - 1,
148
+ output_field=FloatField(),
149
+ ),
150
+ daily_diff_gross_value=Case(
151
+ When(last_gross_value=0, then=None),
152
+ default=F("gross_value") / F("last_gross_value") - 1,
153
+ output_field=FloatField(),
154
+ ),
155
+ )
156
+ .select_related("instrument")
157
+ .select_related("import_source")
158
+ )
159
+
160
+
161
+ class InstrumentPriceInstrumentStatisticsChartView(InstrumentMixin, viewsets.ChartViewSet):
162
+ IDENTIFIER = "wbfdm:instrument-statisticschart"
163
+ queryset = InstrumentPrice.objects.all()
164
+
165
+ title_config_class = InstrumentPriceStatisticsInstrumentTitleConfig
166
+ endpoint_config_class = InstrumentPriceStatisticsInstrumentEndpointConfig
167
+
168
+ def get_queryset(self):
169
+ return InstrumentPrice.objects.filter(instrument=self.instrument, calculated=False)
170
+
171
+ def get_plotly(self, queryset):
172
+ fig = get_default_timeserie_figure()
173
+
174
+ if self.instrument.related_instruments.count() > 0:
175
+ reference = self.instrument.related_instruments.first().name_repr
176
+ risk = self.instrument.primary_risk_instrument.name_repr
177
+ df = pd.DataFrame(queryset.values("date", "sharpe_ratio", "correlation", "beta")).replace(
178
+ [np.inf, -np.inf], np.nan
179
+ )
180
+
181
+ if not df.empty:
182
+ df = df.set_index("date").sort_index().dropna()
183
+ fig.add_trace(
184
+ go.Scatter(
185
+ x=df.index,
186
+ y=df.sharpe_ratio,
187
+ mode="lines",
188
+ name=f"Sharpe Ratio ({risk})",
189
+ hovertemplate=get_hovertemplate_timeserie(currency=""),
190
+ )
191
+ )
192
+ fig.add_trace(
193
+ go.Scatter(
194
+ x=df.index,
195
+ y=df.correlation,
196
+ mode="lines",
197
+ name=f"Correlation ({reference})",
198
+ hovertemplate=get_hovertemplate_timeserie(currency=""),
199
+ )
200
+ )
201
+ fig.add_trace(
202
+ go.Scatter(
203
+ x=df.index,
204
+ y=df.beta,
205
+ mode="lines",
206
+ name=f"Beta ({reference})",
207
+ hovertemplate=get_hovertemplate_timeserie(currency=""),
208
+ )
209
+ )
210
+ else:
211
+ warning(
212
+ self.request,
213
+ "The chart is empty because there is no benchmark or risk free rate associated to this instrument",
214
+ extra_tags="auto_close=0",
215
+ )
216
+
217
+ return fig
218
+
219
+
220
+ class FinancialStatisticsInstrumentPandasView(InstrumentMixin, InternalUserPermissionMixin, ExportPandasAPIViewSet):
221
+ IDENTIFIER = "wbfdm:financialstatistics"
222
+
223
+ LIST_DOCUMENTATION = "wbfdm/markdown/documentation/financial_statistics.md"
224
+
225
+ queryset = InstrumentPrice.objects.all()
226
+ endpoint_config_class = FinancialStatisticsInstrumentEndpointConfig
227
+ title_config_class = FinancialStatisticsInstrumentTitleConfig
228
+ display_config_class = FinancialStatisticsInstrumentPandasDisplayConfig
229
+ button_config_class = FinancialStatisticsInstrumentButtonConfig
230
+ pandas_fields = pf.PandasFields(
231
+ fields=(
232
+ pf.PKField(key="id", label="ID"),
233
+ pf.CharField(key="financial", label="Financial"),
234
+ pf.CharField(key="instrument_statistics", label="Instrument"),
235
+ pf.CharField(key="benchmark_statistics", label="Benchmark"),
236
+ pf.CharField(key="instrument_one_year", label="Instrument - One Year"),
237
+ pf.CharField(key="benchmark_one_year", label="Benchmark - One Year"),
238
+ pf.BooleanField(key="instrument_vs_benchmark", label="Instrument VS Benchmark"),
239
+ pf.BooleanField(key="instrument_vs_benchmark_one_year", label="Instrument VS Benchmark - One Year"),
240
+ )
241
+ )
242
+
243
+ filterset_class = InstrumentPriceSingleBenchmarkFilterSet
244
+ search_fields = ("financial",)
245
+ ordering_fields = (
246
+ "financial" "instrument_statistics",
247
+ "benchmark_statistics",
248
+ "instrument_one_year",
249
+ "benchmark_one_year",
250
+ )
251
+
252
+ def get_dataframe(self, request, queryset, **kwargs):
253
+ starting_date, end_date = get_date_interval_from_request(request, exclude_weekend=True)
254
+
255
+ if request.GET.get("benchmark", None):
256
+ benchmark = Instrument.objects.get(id=request.GET["benchmark"])
257
+ else:
258
+ benchmark = self.instrument.primary_benchmark
259
+
260
+ df = pd.DataFrame()
261
+
262
+ instrument_prices = self.instrument.get_prices_df(from_date=starting_date, to_date=end_date)
263
+ instrument_risk_free_rate_prices_df = (
264
+ self.instrument.primary_risk_instrument.get_prices_df(from_date=starting_date, to_date=end_date)
265
+ if self.instrument.primary_risk_instrument
266
+ else pd.Series(dtype=float)
267
+ )
268
+
269
+ benchmark_prices = (
270
+ benchmark.get_prices_df(from_date=starting_date, to_date=end_date) if benchmark else pd.Series(dtype=float)
271
+ )
272
+ benchmark_risk_free_rate_prices_df = (
273
+ benchmark.primary_risk_instrument.get_prices_df(from_date=starting_date, to_date=end_date)
274
+ if (benchmark and benchmark.primary_risk_instrument)
275
+ else pd.Series(dtype=float)
276
+ )
277
+
278
+ if instrument_prices.empty or instrument_prices.shape[0] < 2:
279
+ return df
280
+
281
+ df["financial"] = [
282
+ "Name",
283
+ "Date From",
284
+ "To Date",
285
+ "Daily Mean Return",
286
+ "Compound Annual Growth Rate",
287
+ "Last Cumulative Return",
288
+ "Ann. Volatility",
289
+ "Beta",
290
+ "Correlation",
291
+ "Risk free rate",
292
+ "Sharpe Ratio",
293
+ "Maximum Drawdown",
294
+ "Maximum Drawdown Date",
295
+ "Longest Drawdown Period",
296
+ "Last Maximum Drawdown",
297
+ "Last Maximum Drawdown Date",
298
+ "Value at Risk 1%",
299
+ "Value at Risk 5%",
300
+ "Value at Risk 10%",
301
+ "Conditional Value at Risk 1%",
302
+ "Conditional Value at Risk 5%",
303
+ "Conditional Value at Risk 10%",
304
+ "Skewness",
305
+ "Excess Kurtosis",
306
+ "Sortino Ratio",
307
+ "Adj. Sortino Ratio",
308
+ "Calmar Ratio",
309
+ "Sterling Ratio",
310
+ "Burke Ratio",
311
+ ]
312
+
313
+ def metric_string_adjusted(metric: [float, None], in_pct: bool = True):
314
+ str_adj = ""
315
+ if metric:
316
+ str_adj = f"{round(metric * 100, 2)}%" if in_pct else f"{round(metric, 2)}"
317
+ return str_adj
318
+
319
+ def fill_df(
320
+ _instrument_title: str,
321
+ _instrument_statistics: FinancialStatistics,
322
+ _benchmark_prices_df: pd.DataFrame,
323
+ _risk_free_rate_prices_df: pd.DataFrame,
324
+ ) -> list[Any]:
325
+ return [
326
+ _instrument_title,
327
+ _instrument_statistics.start.strftime("%Y-%m-%d"),
328
+ _instrument_statistics.end.strftime("%Y-%m-%d"),
329
+ metric_string_adjusted(_instrument_statistics.get_mean_return()),
330
+ metric_string_adjusted(_instrument_statistics.get_compound_annual_growth_rate()),
331
+ metric_string_adjusted(_instrument_statistics.get_last_cumulative_return()),
332
+ metric_string_adjusted(_instrument_statistics.get_volatility()),
333
+ metric_string_adjusted(_instrument_statistics.get_beta(benchmark_prices_df=_benchmark_prices_df)),
334
+ metric_string_adjusted(
335
+ _instrument_statistics.get_correlation(benchmark_prices_df=_benchmark_prices_df)
336
+ ),
337
+ metric_string_adjusted(_instrument_statistics.get_risk_free_rate(_risk_free_rate_prices_df)),
338
+ metric_string_adjusted(
339
+ _instrument_statistics.get_sharpe_ratio(_risk_free_rate_prices_df), in_pct=False
340
+ ),
341
+ metric_string_adjusted(_instrument_statistics.get_maximum_drawdown()),
342
+ _instrument_statistics.get_maximum_drawdown_date(),
343
+ f"{_instrument_statistics.get_longest_drawdown_period()} days",
344
+ metric_string_adjusted(_instrument_statistics.get_last_recent_maximum_drawdown()),
345
+ _instrument_statistics.get_last_recent_maximum_drawdown_date(),
346
+ metric_string_adjusted(_instrument_statistics.get_value_at_risk(alpha=0.01)),
347
+ metric_string_adjusted(_instrument_statistics.get_value_at_risk(alpha=0.05)),
348
+ metric_string_adjusted(_instrument_statistics.get_value_at_risk(alpha=0.1)),
349
+ metric_string_adjusted(_instrument_statistics.get_conditional_value_at_risk(alpha=0.01)),
350
+ metric_string_adjusted(_instrument_statistics.get_conditional_value_at_risk(alpha=0.05)),
351
+ metric_string_adjusted(_instrument_statistics.get_conditional_value_at_risk(alpha=0.10)),
352
+ metric_string_adjusted(_instrument_statistics.get_skewness(), in_pct=False),
353
+ metric_string_adjusted(_instrument_statistics.get_excess_kurtosis(), in_pct=False),
354
+ metric_string_adjusted(_instrument_statistics.get_sortino_ratio(), in_pct=False),
355
+ metric_string_adjusted(_instrument_statistics.get_adjusted_sortino_ratio(), in_pct=False),
356
+ metric_string_adjusted(_instrument_statistics.get_calmar_ratio(), in_pct=False),
357
+ metric_string_adjusted(
358
+ _instrument_statistics.get_sterling_ratio(_risk_free_rate_prices_df), in_pct=False
359
+ ),
360
+ metric_string_adjusted(
361
+ _instrument_statistics.get_burke_ratio(_risk_free_rate_prices_df), in_pct=False
362
+ ),
363
+ ]
364
+
365
+ df["instrument_statistics"] = fill_df(
366
+ self.instrument.name,
367
+ FinancialStatistics(instrument_prices),
368
+ benchmark_prices,
369
+ instrument_risk_free_rate_prices_df,
370
+ )
371
+ instrument_before_previous_year_date = max(
372
+ instrument_prices.index[0], instrument_prices.index[-1] - relativedelta(years=1)
373
+ )
374
+ df["instrument_one_year"] = fill_df(
375
+ self.instrument.name,
376
+ FinancialStatistics(instrument_prices.truncate(before=instrument_before_previous_year_date)),
377
+ benchmark_prices.truncate(before=instrument_before_previous_year_date),
378
+ instrument_risk_free_rate_prices_df.truncate(before=instrument_before_previous_year_date),
379
+ )
380
+
381
+ if not benchmark_prices.empty:
382
+ df["benchmark_statistics"] = fill_df(
383
+ benchmark.name,
384
+ FinancialStatistics(benchmark_prices),
385
+ benchmark_prices,
386
+ benchmark_risk_free_rate_prices_df,
387
+ )
388
+ benchmark_before_previous_year_date = max(
389
+ benchmark_prices.index[0], benchmark_prices.index[-1] - relativedelta(years=1)
390
+ )
391
+
392
+ df["benchmark_one_year"] = fill_df(
393
+ benchmark.name,
394
+ FinancialStatistics(benchmark_prices.truncate(before=benchmark_before_previous_year_date)),
395
+ benchmark_prices.truncate(before=benchmark_before_previous_year_date),
396
+ benchmark_risk_free_rate_prices_df.truncate(before=benchmark_before_previous_year_date),
397
+ )
398
+ df["instrument_vs_benchmark"] = df[["instrument_statistics", "benchmark_statistics"]].apply(
399
+ lambda x: x["instrument_statistics"] - x["benchmark_statistics"] > 0
400
+ if type(x["instrument_statistics"]) in [float, Decimal]
401
+ and type(x["benchmark_statistics"]) in [float, Decimal]
402
+ else False,
403
+ axis=1,
404
+ )
405
+ df["instrument_vs_benchmark_one_year"] = df[["instrument_one_year", "benchmark_one_year"]].apply(
406
+ lambda x: x["instrument_one_year"] - x["benchmark_one_year"] > 0
407
+ if type(x["instrument_one_year"]) in [float, Decimal]
408
+ and type(x["benchmark_one_year"]) in [float, Decimal]
409
+ else False,
410
+ axis=1,
411
+ )
412
+ df["id"] = df.index
413
+ return df
414
+
415
+ def manipulate_dataframe(self, df):
416
+ return df.where(pd.notnull(df), 0)
417
+
418
+
419
+ class InstrumentPriceInstrumentDistributionReturnsChartView(InstrumentMixin, viewsets.ChartViewSet):
420
+ IDENTIFIER = "wbfdm:instrument-distributionreturnschart"
421
+ queryset = InstrumentPrice.objects.all()
422
+
423
+ title_config_class = InstrumentPriceInstrumentDistributionReturnsChartTitleConfig
424
+ endpoint_config_class = InstrumentPriceInstrumentDistributionReturnsChartEndpointConfig
425
+ filterset_class = InstrumentPriceFinancialStatisticsChartFilterSet
426
+
427
+ def get_plotly(self, queryset):
428
+ fig = go.Figure()
429
+
430
+ def update_layout(text):
431
+ fig.update_layout(
432
+ title={"text": text, "y": 0, "x": 0.5, "xanchor": "center", "yanchor": "bottom"},
433
+ yaxis_title="Density",
434
+ xaxis_title="Returns in %",
435
+ legend_title="Instruments",
436
+ )
437
+
438
+ if self.request.GET.get("benchmark", None):
439
+ benchmark = Instrument.objects.get(id=self.request.GET["benchmark"])
440
+ else:
441
+ benchmark = self.instrument.primary_benchmark
442
+
443
+ starting_date, end_date = get_date_interval_from_request(self.request, exclude_weekend=True)
444
+ first_day_available_of_instrument = self.instrument.inception_date.strftime("%Y-%m-%d")
445
+ instrument_prices = self.instrument.get_prices_df(from_date=starting_date, to_date=end_date)
446
+
447
+ if instrument_prices.empty:
448
+ update_layout(f"{self.instrument} has no data or not enough data")
449
+ return fig
450
+ if not starting_date:
451
+ starting_date = instrument_prices.index[0]
452
+ if not end_date:
453
+ end_date = instrument_prices.index[-1]
454
+
455
+ returns_df = pd.DataFrame()
456
+ returns_df[self.instrument] = (
457
+ self.instrument._compute_performance(instrument_prices, freq=self.request.GET.get("frequency"))[
458
+ "performance"
459
+ ]
460
+ * 100
461
+ )
462
+ returns_df.columns = [f"{self.instrument} - #data:{len(returns_df[self.instrument].dropna())}"]
463
+ import plotly.figure_factory as ff
464
+
465
+ if returns_df.empty:
466
+ return go.Figure()
467
+ returns_df = returns_df.astype(float)
468
+ fig = ff.create_distplot(
469
+ [returns_df[c] for c in returns_df.columns],
470
+ [f"{returns_df.columns[x]}" for x in range(len(returns_df.columns))],
471
+ bin_size=0.3,
472
+ )
473
+ if not benchmark:
474
+ update_layout(f"Inception Date -- {self.instrument.name}: {first_day_available_of_instrument}")
475
+ return fig
476
+
477
+ benchmark_prices = benchmark.get_prices_df(from_date=starting_date, to_date=end_date)
478
+ if benchmark_prices.empty:
479
+ update_layout(
480
+ f"Inception Date -- {self.instrument.name}: {first_day_available_of_instrument}"
481
+ f" // {benchmark} has no data"
482
+ )
483
+ return fig
484
+
485
+ if starting_date < benchmark.inception_date:
486
+ update_layout(
487
+ f"Inception Date -- {self.instrument.name}: {first_day_available_of_instrument}"
488
+ f" // {benchmark.name}: {benchmark.inception_date.strftime('%Y-%m-%d')}"
489
+ )
490
+ return fig
491
+ returns_df[benchmark] = benchmark.extract_daily_performance_df(benchmark_prices)["performance"] * 100
492
+ returns_df.columns = [
493
+ returns_df.columns[0],
494
+ f"{benchmark} - #data:{len(returns_df[benchmark].dropna())}",
495
+ ]
496
+ returns_df = returns_df.dropna()
497
+
498
+ fig = ff.create_distplot(
499
+ [returns_df[c] for c in returns_df.columns],
500
+ [f"{returns_df.columns[x]}" for x in range(len(returns_df.columns))],
501
+ bin_size=0.3,
502
+ )
503
+ update_layout(
504
+ f"Inception Date -- {self.instrument.name}: {first_day_available_of_instrument}"
505
+ f" // {benchmark.name}: {benchmark.inception_date.strftime('%Y-%m-%d')}"
506
+ )
507
+ return fig
508
+
509
+
510
+ class BestAndWorstReturnsInstrumentPandasView(InstrumentMixin, InternalUserPermissionMixin, ExportPandasAPIViewSet):
511
+ IDENTIFIER = "wbfdm:bestandworstreturns"
512
+
513
+ queryset = InstrumentPrice.objects.all()
514
+ endpoint_config_class = BestAndWorstReturnsInstrumentEndpointConfig
515
+ title_config_class = BestAndWorstReturnsInstrumentTitleConfig
516
+ display_config_class = BestAndWorstReturnsInstrumentPandasDisplayConfig
517
+
518
+ pandas_fields = pf.PandasFields(
519
+ fields=(
520
+ pf.PKField(key="id", label="ID"),
521
+ pf.DateField(key="date_best_returns", label="Date Best Returns"),
522
+ pf.FloatField(key="best_returns", label="Best Returns", precision=3, percent=True),
523
+ pf.DateField(key="date_worst_returns", label="Date Worst Returns"),
524
+ pf.FloatField(key="worst_returns", label="Worst Returns", precision=3, percent=True),
525
+ )
526
+ )
527
+
528
+ filterset_class = InstrumentPriceFrequencyFilter
529
+ ordering_fields = ("date_best_returns" "best_returns", "date_worst_returns", "worst_returns")
530
+
531
+ def get_dataframe(self, request, queryset, **kwargs):
532
+ df_to_display = pd.DataFrame()
533
+ if not (prices_df := self.instrument.get_prices_df()).empty:
534
+ returns_df = FinancialStatistics(prices_df).get_best_and_worst_returns(freq=request.GET.get("frequency"))
535
+ if not returns_df.empty and returns_df.shape[0] > 0:
536
+ df_to_display["date_best_returns"] = returns_df["Date Best Return"].dt.strftime("%Y-%m-%d")
537
+ df_to_display["best_returns"] = returns_df["Best Return"]
538
+ df_to_display["date_worst_returns"] = returns_df["Date Worst Return"].dt.strftime("%Y-%m-%d")
539
+ df_to_display["worst_returns"] = returns_df["Worst Return"]
540
+
541
+ df_to_display["id"] = df_to_display.index
542
+ return df_to_display
@@ -0,0 +1,51 @@
1
+ from django.utils.functional import cached_property
2
+ from wbcore import viewsets
3
+ from wbfdm.models.instruments import InstrumentRequest
4
+ from wbfdm.serializers.instruments.instrument_requests import (
5
+ InstrumentRequestModelSerializer,
6
+ InstrumentRequestRepresentationSerializer,
7
+ )
8
+
9
+ from ..configs import (
10
+ InstrumentRequestDisplayConfig,
11
+ InstrumentRequestEndpointConfig,
12
+ InstrumentRequestTitleConfig,
13
+ )
14
+
15
+
16
+ class InstrumentRequestRepresentationViewSet(viewsets.RepresentationViewSet):
17
+ queryset = InstrumentRequest.objects.all()
18
+ serializer_class = InstrumentRequestRepresentationSerializer
19
+ search_fields = ("notes", "requester__computed_str")
20
+ ordering_fields = ["created"]
21
+ ordering = ["-created"]
22
+
23
+
24
+ class InstrumentRequestModelViewSet(viewsets.ModelViewSet):
25
+ queryset = InstrumentRequest.objects.select_related("requester", "handler", "created_instrument")
26
+ serializer_class = InstrumentRequestModelSerializer
27
+ filterset_fields = {
28
+ "status": ["exact"],
29
+ "requester": ["exact"],
30
+ "notes": ["icontains"],
31
+ "created": ["gte", "exact", "lte"],
32
+ }
33
+
34
+ ordering_fields = ("status", "requester", "notes", "created")
35
+ ordering = ("-created",)
36
+ search_fields = ("notes", "requester__computed_str", "instrument_data__isin")
37
+
38
+ display_config_class = InstrumentRequestDisplayConfig
39
+ title_config_class = InstrumentRequestTitleConfig
40
+ endpoint_config_class = InstrumentRequestEndpointConfig
41
+
42
+ @cached_property
43
+ def has_validation_permission(self):
44
+ return self.request.user.has_perm("wbfdm.administrate_instrument")
45
+
46
+ # def get_queryset(self):
47
+ # return (
48
+ # super(InstrumentRequestModelViewSet, self)
49
+ # .get_queryset()
50
+ # .annotate(**{field: F(field) for field in INSTRUMENT_BASE_FIELDS})
51
+ # )