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,271 @@
1
+ from datetime import date
2
+ from math import pow
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+
8
+ class FinancialStatistics:
9
+ def __init__(self, prices: pd.Series):
10
+ prices = prices.copy().astype("float")
11
+ if type(prices) is not pd.Series:
12
+ raise ValueError("prices is expected to be a pandas Series")
13
+ self.prices = prices.sort_index()
14
+ if not prices.empty:
15
+ self.start = self.prices.index[0]
16
+ self.end = self.prices.index[-1]
17
+ else:
18
+ self.start = self.end = None
19
+ self.prices.index = pd.to_datetime(self.prices.index)
20
+
21
+ def is_valid(self):
22
+ return not self.prices.empty
23
+
24
+ def extract_monthly_performance_df(self) -> pd.Series:
25
+ return self.compute_performance()
26
+
27
+ def extract_annual_performance_df(self) -> pd.Series:
28
+ return self.compute_performance(freq="BYE")
29
+
30
+ def extract_inception_performance_df(self) -> float:
31
+ if not self.prices.empty and self.prices.iloc[0]:
32
+ return self.prices.iloc[-1] / self.prices.iloc[0] - 1
33
+ return 0
34
+
35
+ def extract_daily_performance_df(self) -> pd.Series:
36
+ return self.compute_performance(freq="B")
37
+
38
+ # ---------------------------- Financial Statistics ----------------------------
39
+ # ------------------------------------------------------------------------------
40
+
41
+ def compute_performance(self, freq: str | int = "BME", extrapolate: bool = True) -> pd.Series:
42
+ perfs = self.prices
43
+ if not perfs.empty:
44
+ if isinstance(freq, int):
45
+ perfs = perfs.resample(f"{freq}D", origin="end").ffill().pct_change()
46
+ else:
47
+ perfs = perfs.asfreq(freq, method="ffill")
48
+ if extrapolate:
49
+ perfs.loc[self.prices.index[0]] = self.prices.iloc[0]
50
+ perfs.loc[self.prices.index[-1]] = self.prices.iloc[-1]
51
+ perfs = perfs.sort_index()
52
+ perfs = perfs / perfs.shift(1) - 1
53
+ return perfs.dropna(how="any").rename("performance")
54
+
55
+ def get_mean_return(self, freq: str = "B") -> float | None:
56
+ """Calculates the mean of returns"""
57
+ if self.is_valid():
58
+ return self.compute_performance(freq=freq).mean()
59
+ return None
60
+
61
+ def get_best_and_worst_returns(self, top: int = 10, freq: str = "B") -> pd.DataFrame:
62
+ """Returns a DataFrame with the top best returns and top worst returns"""
63
+ df = pd.DataFrame(columns=["Date Best Return", "Best Return", "Date Worst Return", "Worst Return"])
64
+ if self.is_valid():
65
+ sort_returns = self.compute_performance(freq=freq).sort_values()
66
+ df.loc[:, "Worst Return"] = sort_returns[0:top].values
67
+ df.loc[:, "Date Worst Return"] = sort_returns[0:top].index
68
+ sort_returns = sort_returns.sort_values(ascending=False)
69
+ df.loc[:, "Best Return"] = sort_returns[0:top].values
70
+ df.loc[:, "Date Best Return"] = sort_returns[0:top].index
71
+ df["Date Worst Return"] = pd.to_datetime(df["Date Worst Return"])
72
+ df["Date Best Return"] = pd.to_datetime(df["Date Best Return"])
73
+ return df
74
+ return df
75
+
76
+ def get_drawdowns(self, freq: str = "B") -> pd.Series:
77
+ """
78
+ Investopedia: A drawdown is a peak-to-trough decline during a specific period for an investment,
79
+ trading account, or fund. A drawdown is usually quoted as the percentage between the peak and the
80
+ subsequent trough.
81
+ """
82
+ high_net_value = self.prices.cummax() # keep highest price the column
83
+ drawdowns = self.prices / high_net_value - 1
84
+ if freq:
85
+ drawdowns = drawdowns.asfreq(freq, method="ffill")
86
+ return drawdowns
87
+
88
+ def get_last_drawdowns(self) -> pd.Series:
89
+ """Returns a Dataframe with the last drawdowns"""
90
+ df = self.get_drawdowns()
91
+ last_highest_price_date = df[df == 0].index[-1].strftime("%Y-%m-%d")
92
+ return df.loc[last_highest_price_date : df.index[-1]]
93
+
94
+ def get_maximum_drawdown(self) -> float:
95
+ """Returns the maximum drawdown."""
96
+ df = self.get_drawdowns()
97
+ maximum_dd = df.min() # maximum in negative value
98
+ return maximum_dd
99
+
100
+ def get_last_recent_maximum_drawdown(self) -> float:
101
+ """Returns the last recent maximum drawdown."""
102
+ df = self.get_last_drawdowns()
103
+ maximum_dd = df.min() # maximum in negative value
104
+ return maximum_dd
105
+
106
+ def get_maximum_drawdown_date(self) -> float:
107
+ df = self.get_drawdowns()
108
+ mdd_date = df[df == df.min()].index.to_pydatetime()[0].strftime("%Y-%m-%d")
109
+ return mdd_date
110
+
111
+ def get_last_recent_maximum_drawdown_date(self) -> date:
112
+ """Returns the last recent maximum drawdown date."""
113
+ df = self.get_last_drawdowns()
114
+ mdd_date = df[df == df.min()].index.to_pydatetime()[0].strftime("%Y-%m-%d")
115
+ return mdd_date
116
+
117
+ def get_longest_drawdown_period(self) -> int:
118
+ """Returns the longest number of day during drawdown"""
119
+ df = self.get_drawdowns()
120
+ df["Drawdown Period"] = df.mask(df < 0.0, 1)
121
+ a = df["Drawdown Period"] != 0
122
+ df1 = a.cumsum() - a.cumsum().where(~a).ffill().fillna(0).astype(int)
123
+ return df1.max()
124
+
125
+ def get_cumulative_returns(self, freq: str = "B") -> pd.Series:
126
+ """Returns a dataframe with the cumulative return each day."""
127
+ returns = self.compute_performance(freq=freq)
128
+ # R = [(1+r1) * (1 + r2) * ... * (1+rn)] - 1
129
+ return returns.add(1).cumprod() - 1
130
+
131
+ def get_last_cumulative_return(self, freq: str = "B") -> float:
132
+ """Returns the last cumulative return."""
133
+ cumulative_returns = self.get_cumulative_returns(freq=freq)
134
+ return cumulative_returns.iat[-1]
135
+
136
+ def get_positive_outliers(self, quantile: float = 0.95, freq: str = "B") -> pd.Series:
137
+ """Extracts the outliers in a DataFrame."""
138
+ returns = self.compute_performance(freq=freq)
139
+ return returns[returns > returns.quantile(quantile)].dropna(how="all")
140
+
141
+ def get_value_at_risk(self, alpha: float = 0.05, freq: str = "B") -> float:
142
+ """Calculates the value at risk for a given confident interval."""
143
+ if alpha < 0 or alpha > 1:
144
+ raise ValueError(f"Alpha = {alpha} is not possible, alpha must be between 0 and 1.")
145
+
146
+ returns = self.compute_performance(freq=freq)
147
+ return returns.quantile(alpha, interpolation="higher")
148
+
149
+ def get_conditional_value_at_risk(self, alpha: float = 0.05, freq: str = "B") -> float:
150
+ """Calculates the conditional value at risk for a given confident interval."""
151
+ if alpha < 0 or alpha > 1:
152
+ raise ValueError(f"Alpha = {alpha} is not possible, alpha must be between 0 and 1.")
153
+
154
+ returns = self.compute_performance(freq=freq)
155
+ return returns[returns <= returns.quantile(alpha, interpolation="lower")].dropna(how="all").mean()
156
+
157
+ def get_skewness(self, freq: str = "B") -> float:
158
+ """Calculates the skewness of the returns."""
159
+ returns = self.compute_performance(freq=freq)
160
+ return returns.skew()
161
+
162
+ def get_kurtosis(self, freq: str = "B") -> float:
163
+ """Calculates the Kurtosis of the returns."""
164
+ returns = self.compute_performance(freq=freq)
165
+ return returns.kurtosis()
166
+
167
+ def get_excess_kurtosis(self, freq: str = "B") -> float:
168
+ return self.get_kurtosis(freq=freq) - 3.0
169
+
170
+ def get_compound_annual_growth_rate(self) -> float:
171
+ """Calculates the compound annual growth rate (Last Price / First Price)(¹/year) - 1"""
172
+ first_price, last_price = self.prices.iat[0], self.prices.iat[-1]
173
+ years = (self.prices.index[-1] - self.prices.index[0]).days / 365.0
174
+ return (last_price / first_price) ** (1 / years) - 1 if first_price != 0 and years != 0 else 0
175
+
176
+ def get_sortino_ratio(self, target_ratio: float = 0, freq: str = "B") -> float:
177
+ """
178
+ Calculates the adjusted Sortino ratio:
179
+ Sortino ratio = (Rp - target_ratio) / below_target_semi_deviation
180
+ below_target_semi_deviation = √(1 / T * Σ (d²))
181
+ d = (Rt-1 - target_ratio) only if the result is negative
182
+ """
183
+ returns = self.compute_performance(freq=freq)
184
+ below_target_sd = np.sqrt((returns[returns < target_ratio] ** 2).sum() / len(returns))
185
+ annualized_return = self.get_compound_annual_growth_rate()
186
+ below_target_annualized = below_target_sd * np.sqrt(260)
187
+ return (annualized_return - target_ratio) / below_target_annualized
188
+
189
+ def get_adjusted_sortino_ratio(self, freq: str = "B") -> float:
190
+ """Calculates the adjusted Sortino ratio (Sortino Ratio / √(2))"""
191
+ return self.get_sortino_ratio(freq=freq) / np.sqrt(2)
192
+
193
+ def get_calmar_ratio(self) -> float:
194
+ """Calculates the calmar ratio (CAGR% / MaxDD%)"""
195
+ compound_annual_growth_rate = self.get_compound_annual_growth_rate()
196
+ maximum_dd = self.get_maximum_drawdown()
197
+ return compound_annual_growth_rate / abs(maximum_dd)
198
+
199
+ def get_risk_free_rate(self, risk_free_rate_prices_df: pd.Series) -> float | None:
200
+ # if the instrument has no prices, we do not know the lifetime of instrument, so we do not know for the risk
201
+ # instrument too
202
+ if not risk_free_rate_prices_df.empty:
203
+ nb_days = (self.prices.index[-1] - self.prices.index[0]).days
204
+ nb_days = 1 if nb_days == 0 else nb_days
205
+ exp = 365.0 / nb_days
206
+ annualized_free_rate = 1 + risk_free_rate_prices_df.sort_index(ascending=False) / (365 * 100)
207
+ tmp = annualized_free_rate.shift(1, fill_value=1).cumprod().dropna()
208
+ risk_free_rate = pow(tmp.iloc[-1], exp) - 1
209
+ return risk_free_rate
210
+
211
+ def get_sterling_ratio(self, risk_free_rate_prices_df: pd.Series) -> float | None:
212
+ """Calculates the adjusted Sterling ratio ((Rp- Rf) / MDD)"""
213
+ if not risk_free_rate_prices_df.empty:
214
+ maximum_dd = self.get_maximum_drawdown()
215
+ annualized_return = self.get_compound_annual_growth_rate()
216
+ rf = self.get_risk_free_rate(risk_free_rate_prices_df)
217
+ rf = 0 if not rf else rf
218
+ return (annualized_return - rf) / abs(maximum_dd)
219
+
220
+ def get_burke_ratio(self, risk_free_rate_prices_df: pd.Series) -> float | None:
221
+ """Calculates the adjusted Burke ratio ((Rp- Rf) / √(Σ (DD²)))"""
222
+ if not risk_free_rate_prices_df.empty:
223
+ drawdowns = self.get_drawdowns()
224
+ drawdowns = np.sqrt(drawdowns[drawdowns != 0].pow(2).sum())
225
+ annualized_return = self.get_compound_annual_growth_rate()
226
+ rf = self.get_risk_free_rate(risk_free_rate_prices_df)
227
+ rf = 0 if not rf else rf
228
+ return (annualized_return - rf) / drawdowns
229
+
230
+ def get_volatility(self, freq: str = "B") -> float:
231
+ """Calculates the volatility of returns"""
232
+ adj = 1
233
+ if freq == "B":
234
+ adj = 260
235
+ elif freq == "W-MON":
236
+ adj = 52
237
+ elif freq == "BME":
238
+ adj = 12
239
+ return self.compute_performance(freq=freq).std() * np.sqrt(adj)
240
+
241
+ def prepare_daily_returns_instrument_vs_benchmark(self, benchmark_prices_df: pd.Series) -> pd.DataFrame:
242
+ if benchmark_prices_df.empty:
243
+ raise ValueError("benchmark price cannot be empty")
244
+ df = pd.DataFrame()
245
+ df["benchmark"] = FinancialStatistics(benchmark_prices_df).extract_daily_performance_df()
246
+ df["instrument"] = self.extract_daily_performance_df()
247
+ return df
248
+
249
+ def get_beta(self, benchmark_prices_df: pd.Series) -> float | None:
250
+ if not benchmark_prices_df.empty:
251
+ df = self.prepare_daily_returns_instrument_vs_benchmark(benchmark_prices_df)
252
+ cov = df["instrument"].cov(df["benchmark"])
253
+ var = df["benchmark"].var()
254
+ beta = cov / var
255
+ return beta
256
+
257
+ def get_correlation(self, benchmark_prices_df: pd.Series) -> float | None:
258
+ if not benchmark_prices_df.empty:
259
+ df = self.prepare_daily_returns_instrument_vs_benchmark(benchmark_prices_df)
260
+ return df["instrument"].corr(df["benchmark"])
261
+
262
+ def get_sharpe_ratio(self, risk_free_rate_prices_df: pd.Series) -> float | None:
263
+ if not risk_free_rate_prices_df.empty:
264
+ annualized_volatility = self.get_volatility()
265
+ annualized_return = self.get_compound_annual_growth_rate()
266
+ rf = self.get_risk_free_rate(risk_free_rate_prices_df)
267
+ rf = 0 if not rf else rf
268
+ return (annualized_return - rf) / annualized_volatility
269
+
270
+ # -------------------------- End of Financial Statistics -----------------------
271
+ # ------------------------------------------------------------------------------