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,99 @@
1
+ import pytest
2
+ from wbfdm.models.instruments import (
3
+ Classification,
4
+ ClassificationGroup,
5
+ InstrumentClassificationThroughModel,
6
+ )
7
+
8
+
9
+ @pytest.mark.django_db
10
+ class TestClassificationGroupModel:
11
+ def test_init(self, classification_group):
12
+ assert classification_group.id is not None
13
+
14
+ def test_primary(self, classification_group_factory):
15
+ g1 = classification_group_factory.create()
16
+ assert g1.is_primary
17
+ g2 = ClassificationGroup.objects.create(is_primary=True, name="Other group")
18
+ g1.refresh_from_db()
19
+ assert g2.is_primary
20
+ assert not g1.is_primary
21
+
22
+ def test_get_levels_representation(self, classification_group_factory, classification_factory):
23
+ group = classification_group_factory.create(max_depth=2)
24
+ c0 = classification_factory.create(name="c0", group=group, level=0, parent=None)
25
+ c1, created = Classification.objects.update_or_create(group=group, level=1, parent=c0, defaults={"name": "c1"})
26
+ c2, created = Classification.objects.update_or_create(group=group, level=2, parent=c1, defaults={"name": "c2"})
27
+ assert set(group.get_levels_representation()) == {
28
+ c0.level_representation,
29
+ c1.level_representation,
30
+ c2.level_representation,
31
+ }
32
+
33
+
34
+ @pytest.mark.django_db
35
+ class TestClassificationModel:
36
+ def test_init(self, classification_factory):
37
+ classification = classification_factory.create()
38
+ assert classification.id is not None
39
+
40
+ def test_str(self, classification_factory):
41
+ classification = classification_factory.create()
42
+ assert str(classification) == classification.computed_str
43
+
44
+ def test_get_instruments(self, classification_factory, instrument_factory, classification_group):
45
+ ind1 = classification_factory.create(group=classification_group)
46
+ subind1 = classification_factory.create(parent=ind1, group=classification_group)
47
+ subind2 = classification_factory.create(parent=ind1, group=classification_group)
48
+
49
+ e1 = instrument_factory.create(classifications=(subind1,))
50
+ e2 = instrument_factory.create(classifications=(subind2,))
51
+ InstrumentClassificationThroughModel.objects.filter(instrument=e2, classification=subind2).update(
52
+ is_favorite=True
53
+ )
54
+
55
+ assert ind1.get_classified_instruments().count() == 2
56
+ assert subind1.get_classified_instruments().first() == e1
57
+ assert subind2.get_classified_instruments().first() == e2
58
+
59
+ assert ind1.get_classified_instruments(only_favorites=True).count() == 1
60
+ assert subind1.get_classified_instruments(only_favorites=True).first() is None
61
+ assert subind2.get_classified_instruments(only_favorites=True).first() == e2
62
+
63
+ @pytest.mark.parametrize("classification_group__code_level_digits", [1, 2, 3, 4])
64
+ def test_get_next_valid_code(self, classification_factory, classification_group):
65
+ parent_classification = classification_factory.create(group=classification_group, code_aggregated=None)
66
+ assert parent_classification.code_aggregated == f"{1:0{classification_group.code_level_digits}}"
67
+ c1 = Classification.objects.filter(parent=parent_classification, group=classification_group).first()
68
+ assert (
69
+ c1.code_aggregated
70
+ == f"{1:0{classification_group.code_level_digits}}" + f"{1:0{classification_group.code_level_digits}}"
71
+ )
72
+ c2 = classification_factory.create(
73
+ parent=parent_classification, group=classification_group, code_aggregated=None
74
+ )
75
+ assert (
76
+ c2.code_aggregated
77
+ == f"{1:0{classification_group.code_level_digits}}" + f"{2:0{classification_group.code_level_digits}}"
78
+ )
79
+
80
+ def test_get_default_level_representation(self, classification_factory, classification_group_factory):
81
+ classification_group = classification_group_factory.create(max_depth=2)
82
+
83
+ # Parent Classification has a default "Level 0" level representation.
84
+ parent_classification = classification_factory.create(group=classification_group)
85
+ assert parent_classification.level_representation == "Level 0"
86
+
87
+ # If no siblings, default is the roman style representation of parent level + 1.
88
+ c1 = Classification.objects.filter(parent=parent_classification, group=classification_group).first()
89
+ assert c1.level_representation == "Level I"
90
+
91
+ # C1 is the parent of C2, we write our own level representation for level 2 classifications.
92
+ c2 = Classification.objects.filter(parent=c1, group=classification_group).first()
93
+ assert c2.level_representation == "Level II"
94
+ c2.level_representation = "Level B"
95
+ c2.save()
96
+
97
+ # If we now create a new level 2 classification, it will take the same representation of its siblings.
98
+ c22 = classification_factory.create(parent=c1, group=classification_group)
99
+ assert c22.level_representation == "Level B"
@@ -0,0 +1,7 @@
1
+ import pytest
2
+
3
+
4
+ @pytest.mark.django_db
5
+ class TestExchangeModel:
6
+ def test_init(self, exchange):
7
+ assert exchange.id is not None
@@ -0,0 +1,117 @@
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+ from django.contrib.auth.models import Permission
5
+ from faker import Faker
6
+ from wbcore.contrib.io.exceptions import DeserializationError
7
+ from wbfdm.import_export.handlers.instrument_list import InstrumentListImportHandler
8
+ from wbfdm.models import InstrumentListThroughModel
9
+
10
+ fake = Faker()
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestInstrumentListModel:
15
+ def test_instrument_list_creation(self, instrument_list):
16
+ assert instrument_list.id is not None
17
+
18
+ @pytest.mark.parametrize(
19
+ "from_date,to_date,comment",
20
+ [
21
+ (fake.past_date(), fake.future_date(), fake.sentence()),
22
+ (fake.past_date(), fake.future_date(), None),
23
+ (fake.past_date(), None, fake.sentence()),
24
+ (None, fake.future_date(), fake.sentence()),
25
+ ],
26
+ )
27
+ def test_handler_deserialization(self, instrument_list, instrument, import_source, from_date, to_date, comment):
28
+ data = dict()
29
+ if from_date:
30
+ data["from_date"] = from_date.strftime("%Y-%m-%d")
31
+ if to_date:
32
+ data["to_date"] = to_date.strftime("%Y-%m-%d")
33
+ if comment:
34
+ data["comment"] = comment
35
+
36
+ data["instrument"] = instrument.id
37
+ data["instrument_list"] = instrument_list.id
38
+ handler = InstrumentListImportHandler(import_source)
39
+ handler._deserialize(data)
40
+ assert data["instrument"] == instrument
41
+ assert data["instrument_list"] == instrument_list
42
+ if to_date:
43
+ assert data["to_date"] == to_date
44
+ if from_date:
45
+ assert data["from_date"] == from_date
46
+ if comment:
47
+ assert data["comment"] == comment
48
+ # try with a dictionary
49
+ data = {}
50
+ data["instrument"] = {"instrument_type": instrument.instrument_type, "isin": instrument.isin}
51
+ data["instrument_list"] = {
52
+ "identifier": instrument_list.identifier,
53
+ "name": instrument_list.name,
54
+ "instrument_list_type": instrument_list.instrument_list_type,
55
+ }
56
+ handler._deserialize(data)
57
+ assert data["instrument"] == instrument
58
+ assert data["instrument_list"] == instrument_list
59
+
60
+ # assert missing instrument raise error
61
+ data = {}
62
+ data["instrument"] = instrument.id
63
+ with pytest.raises((DeserializationError,)):
64
+ handler._deserialize(data)
65
+ raise DeserializationError
66
+
67
+ # automatically match the instrument if the string representation is provided and the match has already been done for that list
68
+ data = {}
69
+ data["instrument_list"] = instrument_list.id
70
+ data["instrument_str"] = instrument.name
71
+ InstrumentListThroughModel.objects.create(
72
+ instrument_list=instrument_list, instrument_str=instrument.name, instrument=instrument
73
+ )
74
+ handler._deserialize(data)
75
+ assert data["instrument"].id == instrument.id
76
+
77
+ @patch("wbfdm.import_export.handlers.instrument_list.send_notification")
78
+ def test_post_processing_objects(
79
+ self, mock_fct, import_source, instrument_list_factory, instrument_factory, user_factory
80
+ ):
81
+ initial_call = mock_fct.call_count
82
+ pm = user_factory.create()
83
+ user_factory.create() # normal user
84
+
85
+ pm.user_permissions.add(
86
+ Permission.objects.get(content_type__app_label="wbfdm", codename="administrate_instrumentlist")
87
+ )
88
+ handler = InstrumentListImportHandler(import_source)
89
+
90
+ # Test if the leftover elements are removed on every imports. (e.g. instrument that are not in an exclusion list anymore)
91
+ list1 = instrument_list_factory.create()
92
+ list2 = instrument_list_factory.create()
93
+
94
+ leftover1 = InstrumentListThroughModel.objects.create(
95
+ instrument_list=list1, instrument=instrument_factory.create()
96
+ )
97
+ remaining_obj1 = InstrumentListThroughModel.objects.create(
98
+ instrument_list=list1, instrument=instrument_factory.create()
99
+ )
100
+ leftover2 = InstrumentListThroughModel.objects.create(
101
+ instrument_list=list2, instrument=instrument_factory.create()
102
+ )
103
+ remaining_obj2 = InstrumentListThroughModel.objects.create(
104
+ instrument_list=list2, instrument=instrument_factory.create()
105
+ )
106
+
107
+ handler._post_processing_objects([remaining_obj1, remaining_obj2], [], [])
108
+ with pytest.raises((InstrumentListThroughModel.DoesNotExist)):
109
+ leftover1.refresh_from_db()
110
+ with pytest.raises((InstrumentListThroughModel.DoesNotExist)):
111
+ leftover2.refresh_from_db()
112
+ remaining_obj2.refresh_from_db()
113
+ remaining_obj1.refresh_from_db()
114
+ assert remaining_obj1
115
+ assert remaining_obj2
116
+
117
+ assert mock_fct.call_count - initial_call == 2 # 2 because there are two instrument added to the list
@@ -0,0 +1,306 @@
1
+ from datetime import date
2
+ from decimal import Decimal
3
+
4
+ import pandas as pd
5
+ import pytest
6
+ from faker import Faker
7
+ from pandas.tseries.offsets import BDay
8
+ from wbcore.models import DynamicDecimalField, DynamicFloatField
9
+ from wbfdm.models import Instrument, InstrumentPrice, RelatedInstrumentThroughModel
10
+
11
+ fake = Faker()
12
+
13
+
14
+ @pytest.mark.django_db
15
+ class TestInstrumentPriceModel:
16
+ def test_init(self, instrument_price):
17
+ assert instrument_price.id is not None
18
+
19
+ def test_str(self, instrument_price):
20
+ assert (
21
+ str(instrument_price)
22
+ == f"{instrument_price.instrument.name}: {instrument_price.net_value} {instrument_price.date:%d.%m.%Y}"
23
+ )
24
+
25
+ def test_previous_price(self, instrument, instrument_price_factory):
26
+ previous_price = instrument_price_factory.create(instrument=instrument, date=date(2009, 12, 31))
27
+ price = instrument_price_factory.create(instrument=instrument, date=date(2010, 1, 1))
28
+ assert price.previous_price == previous_price
29
+
30
+ def test_previous_price_does_not_exist(self, instrument_price):
31
+ assert instrument_price.previous_price is None
32
+
33
+ def test_next_price_price(self, instrument, instrument_price_factory):
34
+ next_price = instrument_price_factory.create(instrument=instrument, date=date(2010, 1, 4))
35
+ price = instrument_price_factory.create(instrument=instrument, date=date(2010, 1, 1))
36
+ assert price.next_price == next_price
37
+
38
+ def test_next_price_does_not_exist(self, instrument_price):
39
+ assert instrument_price.next_price is None
40
+
41
+ def test_subquery_closest_value(self, instrument, instrument_price_factory):
42
+ instrument_price_factory.create_batch(5, instrument=instrument)
43
+ latest_valid_price = InstrumentPrice.objects.latest("date")
44
+ queryset = Instrument.objects.all().annotate(
45
+ net_value=InstrumentPrice.subquery_closest_value(
46
+ "net_value", val_date=latest_valid_price.date, instrument_pk_name="pk"
47
+ )
48
+ )
49
+
50
+ assert queryset.get(id=instrument.id).net_value == latest_valid_price.net_value
51
+
52
+ def test_compute_and_update_statistics(self, weekday, instrument_factory, instrument_price_factory):
53
+ product = instrument_factory.create()
54
+ benchmark = instrument_factory.create()
55
+ RelatedInstrumentThroughModel.objects.create(
56
+ instrument=product,
57
+ related_instrument=benchmark,
58
+ related_type=RelatedInstrumentThroughModel.RelatedTypeChoices.BENCHMARK,
59
+ )
60
+ risk_free_rate = instrument_factory.create()
61
+ RelatedInstrumentThroughModel.objects.create(
62
+ instrument=product,
63
+ related_instrument=risk_free_rate,
64
+ related_type=RelatedInstrumentThroughModel.RelatedTypeChoices.RISK_INSTRUMENT,
65
+ )
66
+
67
+ for _d in pd.date_range(end=weekday, periods=5, freq="B"):
68
+ instrument_price_factory.create(
69
+ calculated=False, instrument=benchmark, date=_d.date(), sharpe_ratio=None, correlation=None, beta=None
70
+ )
71
+ instrument_price_factory.create(
72
+ calculated=False,
73
+ instrument=risk_free_rate,
74
+ date=_d.date(),
75
+ sharpe_ratio=None,
76
+ correlation=None,
77
+ beta=None,
78
+ )
79
+ instrument_price_factory.create(
80
+ calculated=False, instrument=product, date=_d.date(), sharpe_ratio=None, correlation=None, beta=None
81
+ )
82
+
83
+ p = product.valuations.get(date=weekday)
84
+ p.compute_and_update_statistics(min_period=5)
85
+ assert p.sharpe_ratio
86
+ assert p.correlation
87
+ assert p.beta
88
+
89
+ # Compute functions tests
90
+ #
91
+ # @pytest.mark.parametrize("instrument_price__custom_beta_180d", [None])
92
+ # def test_compute_custom_beta_180d(self, instrument_price):
93
+ # assert hasattr(instrument_price, "_compute_custom_beta_180d")
94
+ # assert isinstance(instrument_price._meta.get_field("custom_beta_180d"), DynamicFloatField)
95
+ #
96
+ # @pytest.mark.parametrize("instrument_price__custom_beta_1y", [None])
97
+ # def test_compute_custom_beta_1y(self, instrument_price):
98
+ # assert hasattr(instrument_price, "_compute_custom_beta_1y")
99
+ # assert isinstance(instrument_price._meta.get_field("custom_beta_1y"), DynamicFloatField)
100
+ #
101
+ # @pytest.mark.parametrize("instrument_price__custom_beta_2y", [None])
102
+ # def test_compute_custom_beta_2y(self, instrument_price):
103
+ # assert hasattr(instrument_price, "_compute_custom_beta_2y")
104
+ # assert isinstance(instrument_price._meta.get_field("custom_beta_2y"), DynamicFloatField)
105
+ #
106
+ # @pytest.mark.parametrize("instrument_price__custom_beta_3y", [None])
107
+ # def test_compute_custom_beta_3y(self, instrument_price):
108
+ # assert hasattr(instrument_price, "_compute_custom_beta_3y")
109
+ # assert isinstance(instrument_price._meta.get_field("custom_beta_3y"), DynamicFloatField)
110
+ #
111
+ # @pytest.mark.parametrize("instrument_price__custom_beta_5y", [None])
112
+ # def test_compute_custom_beta_5y(self, instrument_price):
113
+ # assert hasattr(instrument_price, "_compute_custom_beta_5y")
114
+ # assert isinstance(instrument_price._meta.get_field("custom_beta_5y"), DynamicFloatField)
115
+ #
116
+ # @pytest.mark.parametrize(
117
+ # "instrument_price__free_float_ratio, instrument_price__outstanding_shares", [(None, decimal.Decimal(10))]
118
+ # )
119
+ # def test_compute_free_float_ratio(self, instrument_price):
120
+ # assert hasattr(instrument_price, "_compute_free_float_ratio")
121
+ # assert isinstance(instrument_price._meta.get_field("free_float_ratio"), DynamicFloatField)
122
+ # assert instrument_price.free_float_ratio == instrument_price.free_float / float(
123
+ # instrument_price.outstanding_shares
124
+ # )
125
+ #
126
+ # @pytest.mark.parametrize(
127
+ # "instrument_price__short_interest_ratio, instrument_price__outstanding_shares", [(None, decimal.Decimal(10))]
128
+ # )
129
+ # def test_compute_short_interest_ratio(self, instrument_price):
130
+ # assert hasattr(instrument_price, "_compute_short_interest_ratio")
131
+ # assert isinstance(instrument_price._meta.get_field("short_interest_ratio"), DynamicFloatField)
132
+ # assert instrument_price.short_interest_ratio == instrument_price.short_interest / float(
133
+ # instrument_price.outstanding_shares
134
+ # )
135
+ #
136
+ # @pytest.mark.parametrize("instrument_price__net_value_usd", [None])
137
+ # def test_compute_net_value_usd(self, instrument_price):
138
+ # assert hasattr(instrument_price, "_compute_net_value_usd")
139
+ # assert isinstance(instrument_price._meta.get_field("net_value_usd"), DynamicFloatField)
140
+ # assert instrument_price.net_value_usd == instrument_price.currency_fx_usd * float(instrument_price.net_value)
141
+ #
142
+ # @pytest.mark.parametrize("instrument_price__volume_usd", [None])
143
+ # def test_compute_volume_usd(self, instrument_price):
144
+ # assert hasattr(instrument_price, "_compute_volume_usd")
145
+ # assert isinstance(instrument_price._meta.get_field("volume_usd"), DynamicFloatField)
146
+ # assert instrument_price.volume_usd == instrument_price.currency_fx_usd * instrument_price.volume * float(
147
+ # instrument_price.net_value
148
+ # )
149
+ #
150
+ # @pytest.mark.parametrize("instrument_price__volume_50d_usd", [None])
151
+ # def test_compute_volume_50d_usd(self, instrument_price):
152
+ # assert hasattr(instrument_price, "_compute_volume_50d_usd")
153
+ # assert isinstance(instrument_price._meta.get_field("volume_50d_usd"), DynamicFloatField)
154
+ # assert instrument_price.volume_50d_usd == instrument_price.net_value_usd * instrument_price.volume_50d
155
+ #
156
+ # @pytest.mark.parametrize("instrument_price__currency_fx_usd", [None])
157
+ # def test_compute_currency_fx_usd(self, instrument_price):
158
+ # fx_rate = instrument_price.instrument.currency.fx_rates.get(date=instrument_price.date)
159
+ # instrument_price.save(update_all_dynamic_fields=True)
160
+ # assert hasattr(instrument_price, "_compute_currency_fx_usd")
161
+ # assert isinstance(instrument_price._meta.get_field("currency_fx_usd"), DynamicFloatField)
162
+ # assert instrument_price.currency_fx_usd == 1 / float(fx_rate.value)
163
+
164
+ @pytest.mark.parametrize("instrument_price__gross_value", [None])
165
+ def test_compute_gross_value(self, instrument_price):
166
+ assert hasattr(instrument_price, "_compute_gross_value")
167
+ assert isinstance(instrument_price._meta.get_field("gross_value"), DynamicDecimalField)
168
+ assert instrument_price.gross_value == instrument_price.net_value
169
+
170
+ @pytest.mark.parametrize("instrument_price__outstanding_shares", [Decimal(10)])
171
+ def test_compute_outstanding_shares(self, instrument_price, instrument_price_factory):
172
+ next_price = instrument_price_factory.create(
173
+ instrument=instrument_price.instrument,
174
+ date=instrument_price.date + BDay(1),
175
+ outstanding_shares=None,
176
+ calculated=instrument_price.calculated,
177
+ )
178
+ assert hasattr(instrument_price, "_compute_outstanding_shares")
179
+ assert isinstance(instrument_price._meta.get_field("outstanding_shares"), DynamicDecimalField)
180
+ assert next_price.outstanding_shares == instrument_price.outstanding_shares
181
+
182
+ @pytest.mark.parametrize("instrument_price__volume_50d", [None])
183
+ def test_compute_volume_50d(self, instrument_price, instrument_price_factory):
184
+ assert hasattr(instrument_price, "_compute_volume_50d")
185
+ assert isinstance(instrument_price._meta.get_field("volume_50d"), DynamicFloatField)
186
+
187
+ sum_volume_50d = instrument_price.volume
188
+ for _d in pd.date_range(end=instrument_price.date, periods=50, freq="B", inclusive="left"):
189
+ p = instrument_price_factory.create(date=_d, instrument=instrument_price.instrument)
190
+ sum_volume_50d += p.volume
191
+ instrument_price.save(update_all_dynamic_fields=True)
192
+ assert instrument_price.volume_50d == sum_volume_50d / 50
193
+
194
+ # @pytest.mark.parametrize("instrument_price__volume_200d", [None])
195
+ # def test_compute_volume_200d(self, instrument_price, instrument_price_factory):
196
+ # assert hasattr(instrument_price, "_compute_volume_200d")
197
+ # assert isinstance(instrument_price._meta.get_field("volume_200d"), DynamicFloatField)
198
+ #
199
+ # sum_volume_200d = instrument_price.volume
200
+ # for _d in pd.date_range(end=instrument_price.date, periods=200, freq="B", inclusive="left"):
201
+ # p = instrument_price_factory.create(date=_d, instrument=instrument_price.instrument)
202
+ # sum_volume_200d += p.volume
203
+ # instrument_price.save(update_all_dynamic_fields=True)
204
+ # assert instrument_price.volume_200d == sum_volume_200d / 200
205
+
206
+ # @pytest.mark.parametrize("instrument_price__market_capitalization_usd", [None])
207
+ # def test_compute_market_capitalization_usd(self, instrument_price):
208
+ # assert hasattr(instrument_price, "market_capitalization_usd")
209
+ # assert isinstance(instrument_price._meta.get_field("market_capitalization_usd"), DynamicFloatField)
210
+ # assert (
211
+ # instrument_price.market_capitalization_usd
212
+ # == instrument_price.market_capitalization * instrument_price.currency_fx_usd
213
+ # )
214
+
215
+ # def test_compute_performance_1d(self, instrument_price_factory):
216
+ # previous_price = instrument_price_factory.create()
217
+ # instrument_price = instrument_price_factory.create(
218
+ # instrument=previous_price.instrument,
219
+ # calculated=previous_price.calculated,
220
+ # date=previous_price.date + BDay(1),
221
+ # performance_1d=None,
222
+ # )
223
+ # assert hasattr(instrument_price, "performance_1d")
224
+ # assert isinstance(instrument_price._meta.get_field("performance_1d"), DynamicDecimalField)
225
+ # assert instrument_price.performance_1d == instrument_price.net_value / previous_price.net_value - 1
226
+ #
227
+ # def test_compute_performance_7d(self, instrument_price_factory):
228
+ # previous_price = instrument_price_factory.create()
229
+ # instrument_price = instrument_price_factory.create(
230
+ # instrument=previous_price.instrument,
231
+ # calculated=previous_price.calculated,
232
+ # date=previous_price.date + BDay(7),
233
+ # performance_7d=None,
234
+ # )
235
+ # assert hasattr(instrument_price, "performance_7d")
236
+ # assert isinstance(instrument_price._meta.get_field("performance_7d"), DynamicDecimalField)
237
+ # assert instrument_price.performance_7d == instrument_price.net_value / previous_price.net_value - 1
238
+ #
239
+ # def test_compute_performance_30d(self, instrument_price_factory):
240
+ # previous_price = instrument_price_factory.create()
241
+ # instrument_price = instrument_price_factory.create(
242
+ # instrument=previous_price.instrument,
243
+ # calculated=previous_price.calculated,
244
+ # date=previous_price.date + BDay(30),
245
+ # performance_30d=None,
246
+ # )
247
+ # assert hasattr(instrument_price, "performance_30d")
248
+ # assert isinstance(instrument_price._meta.get_field("performance_30d"), DynamicDecimalField)
249
+ # assert instrument_price.performance_30d == instrument_price.net_value / previous_price.net_value - 1
250
+ #
251
+ # def test_compute_performance_90d(self, instrument_price_factory):
252
+ # previous_price = instrument_price_factory.create()
253
+ # instrument_price = instrument_price_factory.create(
254
+ # instrument=previous_price.instrument,
255
+ # calculated=previous_price.calculated,
256
+ # date=previous_price.date + BDay(90),
257
+ # performance_90d=None,
258
+ # )
259
+ # assert hasattr(instrument_price, "performance_90d")
260
+ # assert isinstance(instrument_price._meta.get_field("performance_90d"), DynamicDecimalField)
261
+ # assert instrument_price.performance_90d == instrument_price.net_value / previous_price.net_value - 1
262
+ #
263
+ # def test_compute_performance_365d(self, instrument_price_factory):
264
+ # previous_price = instrument_price_factory.create()
265
+ # instrument_price = instrument_price_factory.create(
266
+ # instrument=previous_price.instrument,
267
+ # calculated=previous_price.calculated,
268
+ # date=previous_price.date + BDay(365),
269
+ # performance_365d=None,
270
+ # )
271
+ # assert hasattr(instrument_price, "performance_365d")
272
+ # assert isinstance(instrument_price._meta.get_field("performance_365d"), DynamicDecimalField)
273
+ # assert instrument_price.performance_365d == instrument_price.net_value / previous_price.net_value - 1
274
+ #
275
+ # @pytest.mark.parametrize("instrument_price__performance_ytd", [None])
276
+ # def test_compute_performance_ytd(self, instrument_price, instrument_price_factory):
277
+ # previous_price = instrument_price_factory.create(
278
+ # instrument=instrument_price.instrument,
279
+ # calculated=instrument_price.calculated,
280
+ # date=(date(instrument_price.date.year, 1, 1) - BDay(0)).date(),
281
+ # performance_ytd=None,
282
+ # )
283
+ # instrument_price.save(update_all_dynamic_fields=True)
284
+ # assert hasattr(instrument_price, "performance_ytd")
285
+ # assert isinstance(instrument_price._meta.get_field("performance_ytd"), DynamicDecimalField)
286
+ # assert instrument_price.performance_ytd == instrument_price.net_value / previous_price.net_value - 1
287
+ #
288
+ # def test_compute_performance_inception(self, instrument_price_factory):
289
+ # inception_price = instrument_price_factory.create()
290
+ # inception_price.instrument.inception_date = inception_price.date
291
+ # inception_price.instrument.save()
292
+ # instrument_price_factory.create(
293
+ # instrument=inception_price.instrument,
294
+ # calculated=inception_price.calculated,
295
+ # date=inception_price.date + BDay(1),
296
+ # performance_inception=None,
297
+ # )
298
+ # instrument_price = instrument_price_factory.create(
299
+ # instrument=inception_price.instrument,
300
+ # calculated=inception_price.calculated,
301
+ # date=inception_price.date + BDay(2),
302
+ # performance_inception=None,
303
+ # )
304
+ # assert hasattr(instrument_price, "performance_inception")
305
+ # assert isinstance(instrument_price._meta.get_field("performance_inception"), DynamicDecimalField)
306
+ # assert instrument_price.performance_inception == instrument_price.net_value / inception_price.net_value - 1