wbportfolio 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 wbportfolio might be problematic. Click here for more details.

Files changed (486) hide show
  1. wbportfolio/__init__.py +1 -0
  2. wbportfolio/admin/__init__.py +12 -0
  3. wbportfolio/admin/asset.py +47 -0
  4. wbportfolio/admin/custodians.py +9 -0
  5. wbportfolio/admin/portfolio.py +127 -0
  6. wbportfolio/admin/portfolio_relationships.py +22 -0
  7. wbportfolio/admin/product_groups.py +42 -0
  8. wbportfolio/admin/products.py +80 -0
  9. wbportfolio/admin/reconciliations.py +14 -0
  10. wbportfolio/admin/registers.py +17 -0
  11. wbportfolio/admin/roles.py +19 -0
  12. wbportfolio/admin/synchronization/__init__.py +2 -0
  13. wbportfolio/admin/synchronization/admin.py +114 -0
  14. wbportfolio/admin/synchronization/portfolio_synchronization.py +18 -0
  15. wbportfolio/admin/synchronization/price_computation.py +21 -0
  16. wbportfolio/admin/transactions/__init__.py +5 -0
  17. wbportfolio/admin/transactions/claim.py +16 -0
  18. wbportfolio/admin/transactions/dividends.py +14 -0
  19. wbportfolio/admin/transactions/fees.py +35 -0
  20. wbportfolio/admin/transactions/trades.py +49 -0
  21. wbportfolio/admin/transactions/transactions.py +37 -0
  22. wbportfolio/analysis/__init__.py +0 -0
  23. wbportfolio/analysis/claims.py +235 -0
  24. wbportfolio/apps.py +5 -0
  25. wbportfolio/contrib/__init__.py +0 -0
  26. wbportfolio/contrib/company_portfolio/__init__.py +0 -0
  27. wbportfolio/contrib/company_portfolio/admin.py +28 -0
  28. wbportfolio/contrib/company_portfolio/apps.py +29 -0
  29. wbportfolio/contrib/company_portfolio/configs/__init__.py +3 -0
  30. wbportfolio/contrib/company_portfolio/configs/display.py +182 -0
  31. wbportfolio/contrib/company_portfolio/configs/endpoints.py +34 -0
  32. wbportfolio/contrib/company_portfolio/configs/previews.py +37 -0
  33. wbportfolio/contrib/company_portfolio/constants.py +1 -0
  34. wbportfolio/contrib/company_portfolio/dynamic_preferences_registry.py +87 -0
  35. wbportfolio/contrib/company_portfolio/factories.py +32 -0
  36. wbportfolio/contrib/company_portfolio/filters.py +127 -0
  37. wbportfolio/contrib/company_portfolio/management.py +19 -0
  38. wbportfolio/contrib/company_portfolio/migrations/0001_initial.py +214 -0
  39. wbportfolio/contrib/company_portfolio/migrations/__init__.py +0 -0
  40. wbportfolio/contrib/company_portfolio/models.py +334 -0
  41. wbportfolio/contrib/company_portfolio/scripts.py +76 -0
  42. wbportfolio/contrib/company_portfolio/serializers.py +303 -0
  43. wbportfolio/contrib/company_portfolio/tasks.py +19 -0
  44. wbportfolio/contrib/company_portfolio/tests/__init__.py +0 -0
  45. wbportfolio/contrib/company_portfolio/tests/conftest.py +161 -0
  46. wbportfolio/contrib/company_portfolio/tests/test_models.py +161 -0
  47. wbportfolio/contrib/company_portfolio/urls.py +29 -0
  48. wbportfolio/contrib/company_portfolio/viewsets.py +195 -0
  49. wbportfolio/defaults/__init__.py +0 -0
  50. wbportfolio/defaults/fees/__init__.py +0 -0
  51. wbportfolio/defaults/fees/default.py +92 -0
  52. wbportfolio/defaults/portfolio/__init__.py +0 -0
  53. wbportfolio/defaults/portfolio/default_rebalancing.py +45 -0
  54. wbportfolio/dynamic_preferences_registry.py +58 -0
  55. wbportfolio/factories/__init__.py +35 -0
  56. wbportfolio/factories/adjustments.py +17 -0
  57. wbportfolio/factories/assets.py +75 -0
  58. wbportfolio/factories/claim.py +39 -0
  59. wbportfolio/factories/custodians.py +11 -0
  60. wbportfolio/factories/dividends.py +14 -0
  61. wbportfolio/factories/fees.py +15 -0
  62. wbportfolio/factories/indexes.py +17 -0
  63. wbportfolio/factories/portfolio_cash_flow.py +20 -0
  64. wbportfolio/factories/portfolio_cash_targets.py +15 -0
  65. wbportfolio/factories/portfolio_swing_pricings.py +15 -0
  66. wbportfolio/factories/portfolios.py +59 -0
  67. wbportfolio/factories/product_groups.py +28 -0
  68. wbportfolio/factories/products.py +56 -0
  69. wbportfolio/factories/pytest_utils.py +121 -0
  70. wbportfolio/factories/reconciliations.py +23 -0
  71. wbportfolio/factories/roles.py +20 -0
  72. wbportfolio/factories/synchronization.py +40 -0
  73. wbportfolio/factories/trades.py +35 -0
  74. wbportfolio/factories/transactions.py +21 -0
  75. wbportfolio/fdm/__init__.py +0 -0
  76. wbportfolio/fdm/tasks.py +12 -0
  77. wbportfolio/filters/__init__.py +32 -0
  78. wbportfolio/filters/assets.py +485 -0
  79. wbportfolio/filters/assets_and_net_new_money_progression.py +42 -0
  80. wbportfolio/filters/custodians.py +10 -0
  81. wbportfolio/filters/esg.py +22 -0
  82. wbportfolio/filters/performances.py +171 -0
  83. wbportfolio/filters/portfolios.py +24 -0
  84. wbportfolio/filters/positions.py +178 -0
  85. wbportfolio/filters/products.py +157 -0
  86. wbportfolio/filters/roles.py +26 -0
  87. wbportfolio/filters/signals.py +92 -0
  88. wbportfolio/filters/transactions/__init__.py +20 -0
  89. wbportfolio/filters/transactions/claim.py +394 -0
  90. wbportfolio/filters/transactions/fees.py +66 -0
  91. wbportfolio/filters/transactions/trades.py +224 -0
  92. wbportfolio/filters/transactions/transactions.py +98 -0
  93. wbportfolio/import_export/__init__.py +0 -0
  94. wbportfolio/import_export/backends/__init__.py +2 -0
  95. wbportfolio/import_export/backends/ubs/__init__.py +3 -0
  96. wbportfolio/import_export/backends/ubs/asset_position.py +45 -0
  97. wbportfolio/import_export/backends/ubs/fees.py +63 -0
  98. wbportfolio/import_export/backends/ubs/instrument_price.py +44 -0
  99. wbportfolio/import_export/backends/ubs/mixin.py +15 -0
  100. wbportfolio/import_export/backends/utils.py +58 -0
  101. wbportfolio/import_export/backends/wbfdm/__init__.py +2 -0
  102. wbportfolio/import_export/backends/wbfdm/adjustment.py +50 -0
  103. wbportfolio/import_export/backends/wbfdm/dividend.py +16 -0
  104. wbportfolio/import_export/backends/wbfdm/mixin.py +15 -0
  105. wbportfolio/import_export/handlers/__init__.py +0 -0
  106. wbportfolio/import_export/handlers/adjustment.py +39 -0
  107. wbportfolio/import_export/handlers/asset_position.py +167 -0
  108. wbportfolio/import_export/handlers/dividend.py +80 -0
  109. wbportfolio/import_export/handlers/fees.py +58 -0
  110. wbportfolio/import_export/handlers/portfolio_cash_flow.py +57 -0
  111. wbportfolio/import_export/handlers/register.py +43 -0
  112. wbportfolio/import_export/handlers/trade.py +191 -0
  113. wbportfolio/import_export/parsers/__init__.py +0 -0
  114. wbportfolio/import_export/parsers/default_mapping.py +30 -0
  115. wbportfolio/import_export/parsers/jpmorgan/__init__.py +0 -0
  116. wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +63 -0
  117. wbportfolio/import_export/parsers/jpmorgan/fees.py +64 -0
  118. wbportfolio/import_export/parsers/jpmorgan/strategy.py +116 -0
  119. wbportfolio/import_export/parsers/jpmorgan/valuation.py +41 -0
  120. wbportfolio/import_export/parsers/leonteq/__init__.py +0 -0
  121. wbportfolio/import_export/parsers/leonteq/customer_trade.py +47 -0
  122. wbportfolio/import_export/parsers/leonteq/equity.py +81 -0
  123. wbportfolio/import_export/parsers/leonteq/fees.py +70 -0
  124. wbportfolio/import_export/parsers/leonteq/trade.py +94 -0
  125. wbportfolio/import_export/parsers/leonteq/valuation.py +39 -0
  126. wbportfolio/import_export/parsers/natixis/__init__.py +0 -0
  127. wbportfolio/import_export/parsers/natixis/customer_trade.py +62 -0
  128. wbportfolio/import_export/parsers/natixis/d1_customer_trade.py +66 -0
  129. wbportfolio/import_export/parsers/natixis/d1_equity.py +80 -0
  130. wbportfolio/import_export/parsers/natixis/d1_fees.py +58 -0
  131. wbportfolio/import_export/parsers/natixis/d1_trade.py +70 -0
  132. wbportfolio/import_export/parsers/natixis/d1_valuation.py +41 -0
  133. wbportfolio/import_export/parsers/natixis/dividend.py +53 -0
  134. wbportfolio/import_export/parsers/natixis/equity.py +60 -0
  135. wbportfolio/import_export/parsers/natixis/fees.py +53 -0
  136. wbportfolio/import_export/parsers/natixis/trade.py +63 -0
  137. wbportfolio/import_export/parsers/natixis/utils.py +76 -0
  138. wbportfolio/import_export/parsers/natixis/valuation.py +46 -0
  139. wbportfolio/import_export/parsers/refinitiv/__init__.py +0 -0
  140. wbportfolio/import_export/parsers/refinitiv/adjustment.py +24 -0
  141. wbportfolio/import_export/parsers/sg_lux/__init__.py +0 -0
  142. wbportfolio/import_export/parsers/sg_lux/custodian_positions.py +70 -0
  143. wbportfolio/import_export/parsers/sg_lux/customer_trade.py +75 -0
  144. wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +140 -0
  145. wbportfolio/import_export/parsers/sg_lux/customer_trade_slk.py +80 -0
  146. wbportfolio/import_export/parsers/sg_lux/customer_trade_without_pw.py +57 -0
  147. wbportfolio/import_export/parsers/sg_lux/equity.py +137 -0
  148. wbportfolio/import_export/parsers/sg_lux/fees.py +56 -0
  149. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +51 -0
  150. wbportfolio/import_export/parsers/sg_lux/portfolio_cash_flow.py +29 -0
  151. wbportfolio/import_export/parsers/sg_lux/portfolio_future_cash_flow.py +36 -0
  152. wbportfolio/import_export/parsers/sg_lux/registers.py +210 -0
  153. wbportfolio/import_export/parsers/sg_lux/sylk.py +248 -0
  154. wbportfolio/import_export/parsers/sg_lux/utils.py +36 -0
  155. wbportfolio/import_export/parsers/sg_lux/valuation.py +53 -0
  156. wbportfolio/import_export/parsers/societe_generale/__init__.py +0 -0
  157. wbportfolio/import_export/parsers/societe_generale/customer_trade.py +54 -0
  158. wbportfolio/import_export/parsers/societe_generale/strategy.py +94 -0
  159. wbportfolio/import_export/parsers/societe_generale/valuation.py +37 -0
  160. wbportfolio/import_export/parsers/tellco/__init__.py +0 -0
  161. wbportfolio/import_export/parsers/tellco/customer_trade.py +64 -0
  162. wbportfolio/import_export/parsers/tellco/equity.py +86 -0
  163. wbportfolio/import_export/parsers/tellco/valuation.py +52 -0
  164. wbportfolio/import_export/parsers/ubs/__init__.py +0 -0
  165. wbportfolio/import_export/parsers/ubs/api/__init__.py +0 -0
  166. wbportfolio/import_export/parsers/ubs/api/asset_position.py +106 -0
  167. wbportfolio/import_export/parsers/ubs/api/fees.py +31 -0
  168. wbportfolio/import_export/parsers/ubs/api/instrument_price.py +20 -0
  169. wbportfolio/import_export/parsers/ubs/api/utils.py +0 -0
  170. wbportfolio/import_export/parsers/ubs/customer_trade.py +60 -0
  171. wbportfolio/import_export/parsers/ubs/equity.py +97 -0
  172. wbportfolio/import_export/parsers/ubs/historical_customer_trade.py +67 -0
  173. wbportfolio/import_export/parsers/ubs/valuation.py +52 -0
  174. wbportfolio/import_export/parsers/vontobel/__init__.py +0 -0
  175. wbportfolio/import_export/parsers/vontobel/asset_position.py +97 -0
  176. wbportfolio/import_export/parsers/vontobel/customer_trade.py +54 -0
  177. wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +40 -0
  178. wbportfolio/import_export/parsers/vontobel/instrument.py +34 -0
  179. wbportfolio/import_export/parsers/vontobel/management_fees.py +86 -0
  180. wbportfolio/import_export/parsers/vontobel/performance_fees.py +35 -0
  181. wbportfolio/import_export/parsers/vontobel/trade.py +38 -0
  182. wbportfolio/import_export/parsers/vontobel/utils.py +17 -0
  183. wbportfolio/import_export/parsers/vontobel/valuation.py +29 -0
  184. wbportfolio/import_export/resources/__init__.py +0 -0
  185. wbportfolio/import_export/resources/assets.py +68 -0
  186. wbportfolio/import_export/resources/trades.py +41 -0
  187. wbportfolio/import_export/utils.py +42 -0
  188. wbportfolio/metric/__init__.py +0 -0
  189. wbportfolio/metric/backends/__init__.py +2 -0
  190. wbportfolio/metric/backends/base.py +86 -0
  191. wbportfolio/metric/backends/constants.py +222 -0
  192. wbportfolio/metric/backends/portfolio_base.py +255 -0
  193. wbportfolio/metric/backends/portfolio_esg.py +66 -0
  194. wbportfolio/metric/tests/__init__.py +0 -0
  195. wbportfolio/metric/tests/conftest.py +4 -0
  196. wbportfolio/metric/tests/test_portfolio_base.py +135 -0
  197. wbportfolio/metric/tests/test_portfolio_esg.py +69 -0
  198. wbportfolio/migrations/0001_initial_squashed.py +13848 -0
  199. wbportfolio/migrations/0002_product_default_sub_account_squashed_0039_alter_assetallocation_company_and_more.py +3836 -0
  200. wbportfolio/migrations/0040_instrument_financial_instrument.py +26 -0
  201. wbportfolio/migrations/0041_remove_listresearch_research_ptr_and_more.py +129 -0
  202. wbportfolio/migrations/0042_instrumentlist_instrumentlistthroughmodel_and_more.py +71 -0
  203. wbportfolio/migrations/0043_alter_instrumentlistthroughmodel_options_and_more.py +238 -0
  204. wbportfolio/migrations/0044_alter_instrumentlist_identifier.py +35 -0
  205. wbportfolio/migrations/0045_alter_instrument_financial_instrument.py +26 -0
  206. wbportfolio/migrations/0046_add_product_default_account.py +166 -0
  207. wbportfolio/migrations/0047_remove_product_default_sub_account.py +14 -0
  208. wbportfolio/migrations/0048_alter_trade_status.py +29 -0
  209. wbportfolio/migrations/0049_trade_claimed_shares.py +25 -0
  210. wbportfolio/migrations/0050_fees_fee_date_fees_wbportfolio_transac_1f7a29_idx.py +44 -0
  211. wbportfolio/migrations/0051_delete_macroreview.py +11 -0
  212. wbportfolio/migrations/0052_remove_cash_instrument_ptr_and_more.py +888 -0
  213. wbportfolio/migrations/0053_remove_product_group.py +132 -0
  214. wbportfolio/migrations/0054_portfolioinstrumentpreferredclassificationthroughmodel_and_more.py +270 -0
  215. wbportfolio/migrations/0055_remove_product__custom_management_rebates_and_more.py +139 -0
  216. wbportfolio/migrations/0056_remove_companyportfoliodata_assets_under_management_currency_and_more.py +56 -0
  217. wbportfolio/migrations/0057_alter_portfolio_preferred_instrument_classifications_and_more.py +36 -0
  218. wbportfolio/migrations/0058_pmsinstrument.py +23 -0
  219. wbportfolio/migrations/0059_fees_unique_fees.py +51 -0
  220. wbportfolio/migrations/0060_alter_portfolioportfoliothroughmodel_type.py +21 -0
  221. wbportfolio/migrations/0061_portfolio_bank_accounts_product_bank_account_and_more.py +175 -0
  222. wbportfolio/migrations/0062_alter_dailyportfoliocashflow_options.py +20 -0
  223. wbportfolio/migrations/0063_accountreconciliation_accountreconciliationline_and_more.py +133 -0
  224. wbportfolio/migrations/0064_alter_portfolio_managers_portfolio_is_tracked_and_more.py +40 -0
  225. wbportfolio/migrations/0065_alter_portfolio_managers_claim_as_shares_and_more.py +73 -0
  226. wbportfolio/migrations/0066_assetposition_initial_shares_at_custodian_and_more.py +108 -0
  227. wbportfolio/migrations/0067_assetposition_unique_asset_position.py +77 -0
  228. wbportfolio/migrations/0068_trade_internal_trade_trade_marked_as_internal_and_more.py +59 -0
  229. wbportfolio/migrations/0069_remove_portfolio_is_invested_and_more.py +56 -0
  230. wbportfolio/migrations/0070_remove_assetposition_unique_asset_position_and_more.py +82 -0
  231. wbportfolio/migrations/0071_alter_trade_options_alter_trade_order.py +22 -0
  232. wbportfolio/migrations/__init__.py +0 -0
  233. wbportfolio/models/__init__.py +26 -0
  234. wbportfolio/models/adjustments.py +246 -0
  235. wbportfolio/models/asset.py +869 -0
  236. wbportfolio/models/custodians.py +101 -0
  237. wbportfolio/models/indexes.py +33 -0
  238. wbportfolio/models/mixins/__init__.py +0 -0
  239. wbportfolio/models/mixins/instruments.py +127 -0
  240. wbportfolio/models/mixins/liquidity_stress_test.py +1307 -0
  241. wbportfolio/models/portfolio.py +1039 -0
  242. wbportfolio/models/portfolio_cash_flow.py +167 -0
  243. wbportfolio/models/portfolio_cash_targets.py +46 -0
  244. wbportfolio/models/portfolio_relationship.py +135 -0
  245. wbportfolio/models/portfolio_swing_pricings.py +51 -0
  246. wbportfolio/models/product_groups.py +230 -0
  247. wbportfolio/models/products.py +569 -0
  248. wbportfolio/models/reconciliations/__init__.py +2 -0
  249. wbportfolio/models/reconciliations/account_reconciliation_lines.py +192 -0
  250. wbportfolio/models/reconciliations/account_reconciliations.py +102 -0
  251. wbportfolio/models/reconciliations/reconciliations.py +25 -0
  252. wbportfolio/models/registers.py +132 -0
  253. wbportfolio/models/roles.py +208 -0
  254. wbportfolio/models/synchronization/__init__.py +3 -0
  255. wbportfolio/models/synchronization/portfolio_synchronization.py +292 -0
  256. wbportfolio/models/synchronization/price_computation.py +200 -0
  257. wbportfolio/models/synchronization/synchronization.py +188 -0
  258. wbportfolio/models/transactions/__init__.py +7 -0
  259. wbportfolio/models/transactions/claim.py +634 -0
  260. wbportfolio/models/transactions/dividends.py +31 -0
  261. wbportfolio/models/transactions/expiry.py +7 -0
  262. wbportfolio/models/transactions/fees.py +153 -0
  263. wbportfolio/models/transactions/trade_proposals.py +502 -0
  264. wbportfolio/models/transactions/trades.py +704 -0
  265. wbportfolio/models/transactions/transactions.py +211 -0
  266. wbportfolio/models/utils.py +12 -0
  267. wbportfolio/permissions.py +13 -0
  268. wbportfolio/pms/__init__.py +0 -0
  269. wbportfolio/pms/statistics/__init__.py +0 -0
  270. wbportfolio/pms/trading/__init__.py +1 -0
  271. wbportfolio/pms/trading/handler.py +164 -0
  272. wbportfolio/pms/typing.py +194 -0
  273. wbportfolio/preferences.py +6 -0
  274. wbportfolio/reports/__init__.py +0 -0
  275. wbportfolio/reports/monthly_position_report.py +74 -0
  276. wbportfolio/risk_management/__init__.py +0 -0
  277. wbportfolio/risk_management/backends/__init__.py +11 -0
  278. wbportfolio/risk_management/backends/accounts.py +166 -0
  279. wbportfolio/risk_management/backends/controversy_portfolio.py +63 -0
  280. wbportfolio/risk_management/backends/exposure_portfolio.py +203 -0
  281. wbportfolio/risk_management/backends/instrument_list_portfolio.py +89 -0
  282. wbportfolio/risk_management/backends/liquidity_risk.py +86 -0
  283. wbportfolio/risk_management/backends/liquidity_stress_instrument.py +86 -0
  284. wbportfolio/risk_management/backends/mixins.py +220 -0
  285. wbportfolio/risk_management/backends/product_integrity.py +111 -0
  286. wbportfolio/risk_management/backends/stop_loss_instrument.py +24 -0
  287. wbportfolio/risk_management/backends/stop_loss_portfolio.py +36 -0
  288. wbportfolio/risk_management/backends/ucits_portfolio.py +63 -0
  289. wbportfolio/risk_management/tests/__init__.py +0 -0
  290. wbportfolio/risk_management/tests/conftest.py +15 -0
  291. wbportfolio/risk_management/tests/test_accounts.py +98 -0
  292. wbportfolio/risk_management/tests/test_controversy_portfolio.py +33 -0
  293. wbportfolio/risk_management/tests/test_exposure_portfolio.py +94 -0
  294. wbportfolio/risk_management/tests/test_instrument_list_portfolio.py +60 -0
  295. wbportfolio/risk_management/tests/test_liquidity_risk.py +47 -0
  296. wbportfolio/risk_management/tests/test_product_integrity.py +55 -0
  297. wbportfolio/risk_management/tests/test_stop_loss_instrument.py +110 -0
  298. wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +119 -0
  299. wbportfolio/risk_management/tests/test_ucits_portfolio.py +39 -0
  300. wbportfolio/serializers/__init__.py +42 -0
  301. wbportfolio/serializers/adjustments.py +24 -0
  302. wbportfolio/serializers/assets.py +166 -0
  303. wbportfolio/serializers/custodians.py +26 -0
  304. wbportfolio/serializers/portfolio_cash_flow.py +48 -0
  305. wbportfolio/serializers/portfolio_cash_targets.py +20 -0
  306. wbportfolio/serializers/portfolio_relationship.py +53 -0
  307. wbportfolio/serializers/portfolio_swing_pricing.py +20 -0
  308. wbportfolio/serializers/portfolios.py +143 -0
  309. wbportfolio/serializers/positions.py +76 -0
  310. wbportfolio/serializers/product_group.py +88 -0
  311. wbportfolio/serializers/products.py +331 -0
  312. wbportfolio/serializers/reconciliations.py +171 -0
  313. wbportfolio/serializers/registers.py +72 -0
  314. wbportfolio/serializers/roles.py +60 -0
  315. wbportfolio/serializers/signals.py +157 -0
  316. wbportfolio/serializers/synchronization.py +18 -0
  317. wbportfolio/serializers/transactions/__init__.py +24 -0
  318. wbportfolio/serializers/transactions/claim.py +310 -0
  319. wbportfolio/serializers/transactions/dividends.py +18 -0
  320. wbportfolio/serializers/transactions/expiry.py +18 -0
  321. wbportfolio/serializers/transactions/fees.py +32 -0
  322. wbportfolio/serializers/transactions/trades.py +315 -0
  323. wbportfolio/serializers/transactions/transactions.py +84 -0
  324. wbportfolio/tasks.py +125 -0
  325. wbportfolio/tests/__init__.py +0 -0
  326. wbportfolio/tests/conftest.py +164 -0
  327. wbportfolio/tests/models/__init__.py +0 -0
  328. wbportfolio/tests/models/test_account_reconciliation.py +191 -0
  329. wbportfolio/tests/models/test_assets.py +193 -0
  330. wbportfolio/tests/models/test_custodians.py +12 -0
  331. wbportfolio/tests/models/test_customer_trades.py +113 -0
  332. wbportfolio/tests/models/test_dividends.py +7 -0
  333. wbportfolio/tests/models/test_imports.py +192 -0
  334. wbportfolio/tests/models/test_instrument_mixins.py +48 -0
  335. wbportfolio/tests/models/test_merge.py +133 -0
  336. wbportfolio/tests/models/test_portfolio_cash_flow.py +112 -0
  337. wbportfolio/tests/models/test_portfolio_cash_targets.py +27 -0
  338. wbportfolio/tests/models/test_portfolio_swing_pricings.py +42 -0
  339. wbportfolio/tests/models/test_portfolios.py +676 -0
  340. wbportfolio/tests/models/test_product_groups.py +80 -0
  341. wbportfolio/tests/models/test_products.py +187 -0
  342. wbportfolio/tests/models/test_roles.py +82 -0
  343. wbportfolio/tests/models/test_splits.py +233 -0
  344. wbportfolio/tests/models/test_synchronization.py +617 -0
  345. wbportfolio/tests/models/transactions/__init__.py +0 -0
  346. wbportfolio/tests/models/transactions/test_claim.py +129 -0
  347. wbportfolio/tests/models/transactions/test_fees.py +65 -0
  348. wbportfolio/tests/models/transactions/test_trades.py +204 -0
  349. wbportfolio/tests/models/utils.py +13 -0
  350. wbportfolio/tests/serializers/__init__.py +0 -0
  351. wbportfolio/tests/serializers/test_claims.py +21 -0
  352. wbportfolio/tests/signals.py +151 -0
  353. wbportfolio/tests/tests.py +31 -0
  354. wbportfolio/tests/viewsets/__init__.py +0 -0
  355. wbportfolio/tests/viewsets/test_assets.py +67 -0
  356. wbportfolio/tests/viewsets/test_performances.py +72 -0
  357. wbportfolio/tests/viewsets/test_products.py +92 -0
  358. wbportfolio/tests/viewsets/transactions/__init__.py +0 -0
  359. wbportfolio/tests/viewsets/transactions/test_claims.py +146 -0
  360. wbportfolio/urls.py +247 -0
  361. wbportfolio/utils.py +30 -0
  362. wbportfolio/viewsets/__init__.py +57 -0
  363. wbportfolio/viewsets/adjustments.py +46 -0
  364. wbportfolio/viewsets/assets.py +562 -0
  365. wbportfolio/viewsets/assets_and_net_new_money_progression.py +117 -0
  366. wbportfolio/viewsets/charts/__init__.py +1 -0
  367. wbportfolio/viewsets/charts/assets.py +247 -0
  368. wbportfolio/viewsets/configs/__init__.py +6 -0
  369. wbportfolio/viewsets/configs/buttons/__init__.py +23 -0
  370. wbportfolio/viewsets/configs/buttons/adjustments.py +13 -0
  371. wbportfolio/viewsets/configs/buttons/assets.py +145 -0
  372. wbportfolio/viewsets/configs/buttons/claims.py +83 -0
  373. wbportfolio/viewsets/configs/buttons/custodians.py +76 -0
  374. wbportfolio/viewsets/configs/buttons/fees.py +14 -0
  375. wbportfolio/viewsets/configs/buttons/mixins.py +88 -0
  376. wbportfolio/viewsets/configs/buttons/portfolios.py +115 -0
  377. wbportfolio/viewsets/configs/buttons/products.py +41 -0
  378. wbportfolio/viewsets/configs/buttons/reconciliations.py +65 -0
  379. wbportfolio/viewsets/configs/buttons/registers.py +11 -0
  380. wbportfolio/viewsets/configs/buttons/signals.py +68 -0
  381. wbportfolio/viewsets/configs/buttons/trade_proposals.py +25 -0
  382. wbportfolio/viewsets/configs/buttons/trades.py +144 -0
  383. wbportfolio/viewsets/configs/display/__init__.py +61 -0
  384. wbportfolio/viewsets/configs/display/adjustments.py +81 -0
  385. wbportfolio/viewsets/configs/display/assets.py +265 -0
  386. wbportfolio/viewsets/configs/display/claim.py +299 -0
  387. wbportfolio/viewsets/configs/display/custodians.py +24 -0
  388. wbportfolio/viewsets/configs/display/esg.py +88 -0
  389. wbportfolio/viewsets/configs/display/fees.py +133 -0
  390. wbportfolio/viewsets/configs/display/portfolio_cash_flow.py +103 -0
  391. wbportfolio/viewsets/configs/display/portfolio_relationship.py +38 -0
  392. wbportfolio/viewsets/configs/display/portfolios.py +125 -0
  393. wbportfolio/viewsets/configs/display/positions.py +75 -0
  394. wbportfolio/viewsets/configs/display/product_groups.py +54 -0
  395. wbportfolio/viewsets/configs/display/product_performance.py +241 -0
  396. wbportfolio/viewsets/configs/display/products.py +249 -0
  397. wbportfolio/viewsets/configs/display/reconciliations.py +151 -0
  398. wbportfolio/viewsets/configs/display/registers.py +71 -0
  399. wbportfolio/viewsets/configs/display/roles.py +49 -0
  400. wbportfolio/viewsets/configs/display/trade_proposals.py +97 -0
  401. wbportfolio/viewsets/configs/display/trades.py +359 -0
  402. wbportfolio/viewsets/configs/display/transactions.py +55 -0
  403. wbportfolio/viewsets/configs/endpoints/__init__.py +75 -0
  404. wbportfolio/viewsets/configs/endpoints/adjustments.py +17 -0
  405. wbportfolio/viewsets/configs/endpoints/assets.py +115 -0
  406. wbportfolio/viewsets/configs/endpoints/claim.py +106 -0
  407. wbportfolio/viewsets/configs/endpoints/custodians.py +6 -0
  408. wbportfolio/viewsets/configs/endpoints/esg.py +14 -0
  409. wbportfolio/viewsets/configs/endpoints/fees.py +26 -0
  410. wbportfolio/viewsets/configs/endpoints/portfolio_relationship.py +23 -0
  411. wbportfolio/viewsets/configs/endpoints/portfolios.py +43 -0
  412. wbportfolio/viewsets/configs/endpoints/positions.py +18 -0
  413. wbportfolio/viewsets/configs/endpoints/product_groups.py +11 -0
  414. wbportfolio/viewsets/configs/endpoints/product_performance.py +29 -0
  415. wbportfolio/viewsets/configs/endpoints/products.py +37 -0
  416. wbportfolio/viewsets/configs/endpoints/reconciliations.py +31 -0
  417. wbportfolio/viewsets/configs/endpoints/roles.py +9 -0
  418. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +17 -0
  419. wbportfolio/viewsets/configs/endpoints/trades.py +82 -0
  420. wbportfolio/viewsets/configs/endpoints/transactions.py +17 -0
  421. wbportfolio/viewsets/configs/menu/__init__.py +30 -0
  422. wbportfolio/viewsets/configs/menu/adjustments.py +8 -0
  423. wbportfolio/viewsets/configs/menu/assets.py +8 -0
  424. wbportfolio/viewsets/configs/menu/claim.py +41 -0
  425. wbportfolio/viewsets/configs/menu/custodians.py +11 -0
  426. wbportfolio/viewsets/configs/menu/fees.py +13 -0
  427. wbportfolio/viewsets/configs/menu/instrument_prices.py +10 -0
  428. wbportfolio/viewsets/configs/menu/portfolio_cash_flow.py +8 -0
  429. wbportfolio/viewsets/configs/menu/portfolios.py +15 -0
  430. wbportfolio/viewsets/configs/menu/positions.py +14 -0
  431. wbportfolio/viewsets/configs/menu/product_groups.py +10 -0
  432. wbportfolio/viewsets/configs/menu/product_performance.py +25 -0
  433. wbportfolio/viewsets/configs/menu/products.py +15 -0
  434. wbportfolio/viewsets/configs/menu/reconciliations.py +7 -0
  435. wbportfolio/viewsets/configs/menu/registers.py +10 -0
  436. wbportfolio/viewsets/configs/menu/roles.py +16 -0
  437. wbportfolio/viewsets/configs/menu/trades.py +18 -0
  438. wbportfolio/viewsets/configs/menu/transactions.py +8 -0
  439. wbportfolio/viewsets/configs/previews/__init__.py +1 -0
  440. wbportfolio/viewsets/configs/previews/portfolios.py +21 -0
  441. wbportfolio/viewsets/configs/titles/__init__.py +65 -0
  442. wbportfolio/viewsets/configs/titles/adjustments.py +19 -0
  443. wbportfolio/viewsets/configs/titles/assets.py +57 -0
  444. wbportfolio/viewsets/configs/titles/assets_and_net_new_money_progression.py +6 -0
  445. wbportfolio/viewsets/configs/titles/claim.py +81 -0
  446. wbportfolio/viewsets/configs/titles/custodians.py +12 -0
  447. wbportfolio/viewsets/configs/titles/esg.py +10 -0
  448. wbportfolio/viewsets/configs/titles/fees.py +25 -0
  449. wbportfolio/viewsets/configs/titles/instrument_prices.py +20 -0
  450. wbportfolio/viewsets/configs/titles/portfolios.py +32 -0
  451. wbportfolio/viewsets/configs/titles/positions.py +11 -0
  452. wbportfolio/viewsets/configs/titles/product_groups.py +12 -0
  453. wbportfolio/viewsets/configs/titles/product_performance.py +16 -0
  454. wbportfolio/viewsets/configs/titles/products.py +6 -0
  455. wbportfolio/viewsets/configs/titles/registers.py +12 -0
  456. wbportfolio/viewsets/configs/titles/roles.py +23 -0
  457. wbportfolio/viewsets/configs/titles/trades.py +51 -0
  458. wbportfolio/viewsets/configs/titles/transactions.py +8 -0
  459. wbportfolio/viewsets/custodians.py +66 -0
  460. wbportfolio/viewsets/esg.py +165 -0
  461. wbportfolio/viewsets/mixins.py +48 -0
  462. wbportfolio/viewsets/portfolio_cash_flow.py +31 -0
  463. wbportfolio/viewsets/portfolio_cash_targets.py +8 -0
  464. wbportfolio/viewsets/portfolio_relationship.py +46 -0
  465. wbportfolio/viewsets/portfolio_swing_pricing.py +8 -0
  466. wbportfolio/viewsets/portfolios.py +154 -0
  467. wbportfolio/viewsets/positions.py +292 -0
  468. wbportfolio/viewsets/product_groups.py +84 -0
  469. wbportfolio/viewsets/product_performance.py +646 -0
  470. wbportfolio/viewsets/products.py +529 -0
  471. wbportfolio/viewsets/reconciliations.py +160 -0
  472. wbportfolio/viewsets/registers.py +75 -0
  473. wbportfolio/viewsets/roles.py +44 -0
  474. wbportfolio/viewsets/signals.py +42 -0
  475. wbportfolio/viewsets/synchronization.py +25 -0
  476. wbportfolio/viewsets/transactions/__init__.py +40 -0
  477. wbportfolio/viewsets/transactions/claim.py +933 -0
  478. wbportfolio/viewsets/transactions/fees.py +190 -0
  479. wbportfolio/viewsets/transactions/mixins.py +19 -0
  480. wbportfolio/viewsets/transactions/trade_proposals.py +93 -0
  481. wbportfolio/viewsets/transactions/trades.py +395 -0
  482. wbportfolio/viewsets/transactions/transactions.py +123 -0
  483. wbportfolio-2.2.1.dist-info/METADATA +21 -0
  484. wbportfolio-2.2.1.dist-info/RECORD +486 -0
  485. wbportfolio-2.2.1.dist-info/WHEEL +5 -0
  486. wbportfolio-2.2.1.dist-info/licenses/LICENSE +4 -0
@@ -0,0 +1,33 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from wbfdm.enums import ESGControveryFlag
4
+ from wbportfolio.risk_management.backends.controversy_portfolio import (
5
+ RuleBackend as ControversyPortfolio,
6
+ )
7
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
8
+
9
+ fake = Faker()
10
+
11
+
12
+ @pytest.mark.django_db
13
+ class TestControversyPortfolioRUle(PortfolioTestMixin):
14
+ @pytest.mark.parametrize("flag", ESGControveryFlag.values)
15
+ def test_check_rule_exclude(
16
+ self, weekday, portfolio, asset_position_factory, controversy_factory, rule_threshold, flag
17
+ ):
18
+ rule_backend = ControversyPortfolio(
19
+ weekday, portfolio, thresholds=[rule_threshold], json_parameters={"flags": [flag]}
20
+ )
21
+ a1 = asset_position_factory.create(portfolio=portfolio, date=weekday)
22
+ asset_position_factory.create(portfolio=portfolio, date=weekday) # noise position
23
+
24
+ # No controversy yet, so this shouldn't trigger the rule
25
+ res = list(rule_backend.check_rule())
26
+ assert len(res) == 0
27
+
28
+ controversy = controversy_factory.create(flag=flag, instrument=a1.underlying_instrument) # noqa
29
+
30
+ res = list(rule_backend.check_rule())[0]
31
+ assert res.breached_object == a1.underlying_instrument
32
+ assert res.breached_object_repr == str(a1.underlying_instrument)
33
+ assert res.severity == rule_threshold.severity
@@ -0,0 +1,94 @@
1
+ import random
2
+
3
+ import pytest
4
+ from faker import Faker
5
+ from psycopg.types.range import NumericRange
6
+ from wbcompliance.factories.risk_management import RuleThresholdFactory
7
+ from wbportfolio.risk_management.backends.exposure_portfolio import (
8
+ RuleBackend as ExposurePortfolioRuleBackend,
9
+ )
10
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
11
+
12
+ fake = Faker()
13
+
14
+
15
+ @pytest.mark.django_db
16
+ class TestExposurePortfolioRuleModel(PortfolioTestMixin):
17
+ @pytest.mark.parametrize(
18
+ "group_by",
19
+ [groupby for groupby in ExposurePortfolioRuleBackend.GroupbyChoices.values],
20
+ )
21
+ def test_check_rule_groupby_weighting(
22
+ self,
23
+ weekday,
24
+ group_by,
25
+ instrument_factory,
26
+ cash_factory,
27
+ asset_position_factory,
28
+ instrument_price_factory,
29
+ country_factory,
30
+ currency_factory,
31
+ instrument_type_factory,
32
+ portfolio,
33
+ classification,
34
+ ):
35
+ only_type = instrument_type_factory.create()
36
+ only_country = country_factory.create()
37
+ only_currency = currency_factory.create()
38
+ parameters = {
39
+ "group_by": group_by,
40
+ "field": ExposurePortfolioRuleBackend.Field.WEIGHTING.value,
41
+ "asset_classes": [only_type.id],
42
+ "countries": [only_country.id],
43
+ "currencies": [only_currency.id],
44
+ "classifications": [classification.id],
45
+ }
46
+
47
+ lower = random.random()
48
+ upper = random.uniform(lower, 1)
49
+ exposure_portfolio_backend = ExposurePortfolioRuleBackend(
50
+ weekday,
51
+ portfolio,
52
+ parameters,
53
+ [RuleThresholdFactory.create(range=NumericRange(lower=lower, upper=upper))], # type: ignore
54
+ )
55
+
56
+ i1 = instrument_factory.create(
57
+ country=only_country, currency=only_currency, instrument_type=only_type, classifications=[classification]
58
+ )
59
+
60
+ threshold = exposure_portfolio_backend.thresholds[0]
61
+ instrument_price_factory.create(instrument=i1, date=weekday)
62
+ asset_position_factory.create(
63
+ date=weekday,
64
+ weighting=random.uniform(threshold.range.lower, threshold.range.upper),
65
+ underlying_instrument=i1,
66
+ portfolio=portfolio,
67
+ ) # Breached position
68
+
69
+ asset_position_factory.create(
70
+ date=weekday,
71
+ weighting=random.uniform(threshold.range.lower, threshold.range.upper),
72
+ portfolio=portfolio,
73
+ ) # Breached position but filtered out
74
+
75
+ i2 = cash_factory.create()
76
+ instrument_price_factory.create(instrument=i2, date=weekday)
77
+ asset_position_factory.create(
78
+ date=weekday,
79
+ weighting=random.uniform(0, threshold.range.lower),
80
+ underlying_instrument=i2,
81
+ portfolio=portfolio,
82
+ ) # None breached position
83
+
84
+ incidents = list(exposure_portfolio_backend.check_rule())
85
+ assert len(incidents) == 1
86
+ if group_by == ExposurePortfolioRuleBackend.GroupbyChoices.CURRENCY:
87
+ assert incidents[0].breached_object == only_currency
88
+ elif group_by == ExposurePortfolioRuleBackend.GroupbyChoices.COUNTRY:
89
+ assert incidents[0].breached_object == only_country
90
+ elif group_by in [
91
+ ExposurePortfolioRuleBackend.GroupbyChoices.PRIMARY_CLASSIFICATION,
92
+ ExposurePortfolioRuleBackend.GroupbyChoices.FAVORITE_CLASSIFICATION,
93
+ ]:
94
+ assert incidents[0].breached_object == classification
@@ -0,0 +1,60 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from wbfdm.models.instruments.instrument_lists import InstrumentListThroughModel
4
+ from wbportfolio.risk_management.backends.instrument_list_portfolio import (
5
+ RuleBackend as InstrumentListPortfolio,
6
+ )
7
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
8
+
9
+ fake = Faker()
10
+
11
+
12
+ @pytest.mark.django_db
13
+ class TestInstrumentListPortfolio(PortfolioTestMixin):
14
+ def test_check_rule_exclude(self, weekday, portfolio, asset_position_factory, instrument_list, rule_threshold):
15
+ rule_backend = InstrumentListPortfolio(
16
+ weekday, portfolio, json_parameters={"instrument_lists": [instrument_list.id]}, thresholds=[rule_threshold]
17
+ )
18
+ a1 = asset_position_factory.create(portfolio=portfolio, date=weekday)
19
+ asset_position_factory.create(portfolio=portfolio, date=weekday) # noise position
20
+
21
+ rel = InstrumentListThroughModel.objects.create(
22
+ instrument=a1.underlying_instrument, instrument_list=instrument_list, validated=False
23
+ )
24
+ # we check that even though the insturment is in the list, it's still not validated, so this shouldn't trigger the rule
25
+ res = list(rule_backend.check_rule())
26
+ assert len(res) == 0
27
+
28
+ rel.validated = True
29
+ rel.save()
30
+ res = list(rule_backend.check_rule())[0]
31
+ assert res.breached_object == a1.underlying_instrument
32
+ assert res.breached_object_repr == str(a1.underlying_instrument)
33
+ assert res.severity == rule_threshold.severity
34
+
35
+ def test_check_rule_include(self, weekday, portfolio, asset_position_factory, instrument_list, rule_threshold):
36
+ rule_backend = InstrumentListPortfolio(
37
+ weekday,
38
+ portfolio,
39
+ json_parameters={"exclude": False, "instrument_lists": [instrument_list.id]},
40
+ thresholds=[rule_threshold],
41
+ )
42
+ a1 = asset_position_factory.create(portfolio=portfolio, date=weekday)
43
+ a2 = asset_position_factory.create(portfolio=portfolio, date=weekday)
44
+
45
+ InstrumentListThroughModel.objects.create(
46
+ instrument=a1.underlying_instrument, instrument_list=instrument_list, validated=True
47
+ )
48
+ InstrumentListThroughModel.objects.create(
49
+ instrument=a2.underlying_instrument, instrument_list=instrument_list, validated=True
50
+ )
51
+ # ground truth
52
+ res = list(rule_backend.check_rule())
53
+ assert len(res) == 0
54
+
55
+ a3 = asset_position_factory.create(portfolio=portfolio, date=weekday)
56
+
57
+ res = list(rule_backend.check_rule())[0]
58
+ assert res.breached_object == a3.underlying_instrument
59
+ assert res.breached_object_repr == str(a3.underlying_instrument)
60
+ assert res.severity == rule_threshold.severity
@@ -0,0 +1,47 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from psycopg.types.range import NumericRange
4
+ from wbcompliance.factories.risk_management import RuleThresholdFactory
5
+ from wbportfolio.risk_management.backends.liquidity_risk import (
6
+ RuleBackend as LiquidityRiskRuleBackend,
7
+ )
8
+
9
+ fake = Faker()
10
+
11
+
12
+ @pytest.mark.django_db
13
+ class TestProductRuleModel:
14
+ @pytest.fixture
15
+ def liquidity_risk_backend(
16
+ self,
17
+ weekday,
18
+ instrument,
19
+ ):
20
+ return LiquidityRiskRuleBackend(
21
+ weekday,
22
+ instrument,
23
+ json_parameters={"liquidation_factor": 3, "redemption_pct": 0.80},
24
+ thresholds=[
25
+ RuleThresholdFactory.create(range=NumericRange(lower=5, upper=None)),
26
+ ],
27
+ )
28
+
29
+ def test_check_rule_product_liquidity(
30
+ self, weekday, instrument, instrument_price_factory, asset_position_factory, liquidity_risk_backend
31
+ ):
32
+ res = list(liquidity_risk_backend.check_rule()) # no position, no risk incident
33
+ assert len(res) == 0
34
+
35
+ shares = 100
36
+
37
+ volume_50d = (shares * liquidity_risk_backend.redemption_pct * liquidity_risk_backend.liquidation_factor) / 6
38
+ instrument_price_factory.create(date=weekday, instrument=instrument, volume_50d=volume_50d, calculated=False)
39
+ asset_position_factory.create(
40
+ date=weekday, underlying_instrument=instrument, is_estimated=False, initial_shares=shares
41
+ )
42
+
43
+ res = list(liquidity_risk_backend.check_rule()) # no position, no risk incident
44
+ assert len(res) == 1
45
+ incident = res[0]
46
+ assert incident.report_details["Total Shares"] == shares
47
+ assert incident.report_details["Volume 50D"] == volume_50d
@@ -0,0 +1,55 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from pandas.tseries.offsets import BDay
4
+ from psycopg.types.range import NumericRange
5
+ from wbcompliance.factories.risk_management import RuleThresholdFactory
6
+ from wbportfolio.risk_management.backends.product_integrity import (
7
+ RuleBackend as ProductRuleBackend,
8
+ )
9
+
10
+ fake = Faker()
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestProductRuleModel:
15
+ @pytest.fixture
16
+ def product_backend(
17
+ self,
18
+ weekday,
19
+ product,
20
+ ):
21
+ return ProductRuleBackend(
22
+ weekday,
23
+ product,
24
+ thresholds=[
25
+ RuleThresholdFactory.create(range=NumericRange(lower=1, upper=2), severity__name="LOW"),
26
+ RuleThresholdFactory.create(range=NumericRange(lower=2, upper=None), severity__name="HIGH"),
27
+ ], # detect any -20% perf
28
+ )
29
+
30
+ def test_check_rule_product_data_integrity(
31
+ self, weekday, product, instrument_price_factory, asset_position_factory, product_backend
32
+ ):
33
+ asset_position_factory.create(date=weekday - BDay(2), portfolio=product.portfolio, is_estimated=False)
34
+ instrument_price_factory.create(date=weekday - BDay(1), instrument=product, calculated=False)
35
+
36
+ res = list(product_backend.check_rule())
37
+ low_severity = list(filter(lambda x: x.severity.name == "LOW", res))[0]
38
+ high_severity = list(filter(lambda x: x.severity.name == "HIGH", res))[0]
39
+
40
+ assert high_severity.report_details["Data Type"] == "Asset Position"
41
+ assert low_severity.report_details["Data Type"] == "Valuation"
42
+ assert high_severity.report_details["Last Datapoint"] == f"{(weekday - BDay(2)).date():%d.%m.%Y}"
43
+ assert low_severity.report_details["Last Datapoint"] == f"{(weekday - BDay(1)).date():%d.%m.%Y}"
44
+
45
+ instrument_price_factory.create(date=weekday, instrument=product, calculated=False)
46
+ asset_position_factory.create(date=weekday - BDay(1), portfolio=product.portfolio, is_estimated=False)
47
+
48
+ res = list(product_backend.check_rule())
49
+ assert len(res) == 1
50
+ assert res[0].report_details["Data Type"] == "Asset Position"
51
+ assert res[0].report_details["Last Datapoint"] == f"{(weekday - BDay(1)).date():%d.%m.%Y}"
52
+
53
+ asset_position_factory.create(date=weekday, portfolio=product.portfolio, is_estimated=False)
54
+ res = list(product_backend.check_rule())
55
+ assert len(res) == 0
@@ -0,0 +1,110 @@
1
+ import random
2
+ from decimal import Decimal
3
+
4
+ import pytest
5
+ from faker import Faker
6
+ from psycopg.types.range import NumericRange
7
+ from wbcompliance.factories.risk_management import RuleThresholdFactory
8
+ from wbfdm.models import RelatedInstrumentThroughModel
9
+ from wbportfolio.risk_management.backends.stop_loss_instrument import (
10
+ RuleBackend as StopLossInstrumentRuleBackend,
11
+ )
12
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
13
+
14
+ fake = Faker()
15
+
16
+
17
+ @pytest.mark.django_db
18
+ class TestStopLossInstrumentRuleModel(PortfolioTestMixin):
19
+ @pytest.fixture
20
+ def stop_loss_instrument_backend(self, weekday, date_interval_option, freq, product):
21
+ parameters = {"freq": freq, "date_interval_option": date_interval_option}
22
+ lower = random.random()
23
+ upper = random.uniform(lower, 1)
24
+ return StopLossInstrumentRuleBackend(
25
+ weekday,
26
+ product,
27
+ parameters,
28
+ [RuleThresholdFactory.create(range=NumericRange(lower=lower, upper=upper))], # type: ignore
29
+ )
30
+
31
+ @pytest.mark.parametrize(
32
+ "date_interval_option, freq",
33
+ [
34
+ ("ROLLING_WINDOWS", StopLossInstrumentRuleBackend.FreqChoices.BUSINESS_DAY),
35
+ *[("FREQUENCY", option) for option in StopLossInstrumentRuleBackend.FreqChoices.values],
36
+ ],
37
+ )
38
+ def test_check_rule_frequency(
39
+ self,
40
+ weekday,
41
+ date_interval_option,
42
+ freq,
43
+ product,
44
+ instrument_price_factory,
45
+ stop_loss_instrument_backend,
46
+ ):
47
+ d1 = stop_loss_instrument_backend._get_start_interval()
48
+ threshold = stop_loss_instrument_backend.thresholds[0]
49
+
50
+ breach_perf = random.uniform(threshold.range.lower, threshold.range.upper)
51
+
52
+ i1 = instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
53
+ instrument_price_factory.create(
54
+ date=weekday, net_value=Decimal(breach_perf + 1) * i1.net_value, calculated=False, instrument=product
55
+ )
56
+
57
+ res = list(stop_loss_instrument_backend.check_rule())
58
+ assert len(res) == 1
59
+ assert res[0].breached_object.id == product.id
60
+
61
+ @pytest.mark.parametrize(
62
+ "date_interval_option, freq",
63
+ [
64
+ ("FREQUENCY", StopLossInstrumentRuleBackend.FreqChoices.WEEKLY_FRIDAY),
65
+ ("ROLLING_WINDOWS", StopLossInstrumentRuleBackend.FreqChoices.WEEKLY_FRIDAY),
66
+ ],
67
+ )
68
+ def test_check_rule_frequency_with_benchmark(
69
+ self,
70
+ weekday,
71
+ date_interval_option,
72
+ freq,
73
+ product,
74
+ instrument_factory,
75
+ instrument_price_factory,
76
+ stop_loss_instrument_backend,
77
+ ):
78
+ benchmark = instrument_factory.create()
79
+
80
+ threshold = stop_loss_instrument_backend.thresholds[0]
81
+ threshold.range = NumericRange(upper=-0.5, lower=None) # type: ignore
82
+ threshold.save()
83
+
84
+ d1 = stop_loss_instrument_backend._get_start_interval()
85
+
86
+ instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
87
+ instrument_price_factory.create(date=weekday, net_value=100, calculated=False, instrument=product)
88
+
89
+ instrument_price_factory.create(date=d1, net_value=500, calculated=False, instrument=benchmark)
90
+ benchmark_price_2 = instrument_price_factory.create(
91
+ date=weekday, net_value=500, calculated=False, instrument=benchmark
92
+ )
93
+ RelatedInstrumentThroughModel.objects.create(instrument=product, related_instrument=benchmark, is_primary=True)
94
+ setattr(stop_loss_instrument_backend, "dynamic_benchmark_type", "PRIMARY_BENCHMARK")
95
+
96
+ res = list(stop_loss_instrument_backend.check_rule())
97
+ assert len(res) == 0
98
+
99
+ # artificially
100
+ benchmark_price_2.net_value = 1000
101
+ benchmark_price_2.save()
102
+
103
+ res = list(stop_loss_instrument_backend.check_rule())
104
+ assert len(res) == 1
105
+ assert res[0].breached_object.id == product.id
106
+
107
+ setattr(stop_loss_instrument_backend, "static_benchmark", benchmark)
108
+ res = list(stop_loss_instrument_backend.check_rule())
109
+ assert len(res) == 1
110
+ assert res[0].breached_object.id == product.id
@@ -0,0 +1,119 @@
1
+ import random
2
+ from decimal import Decimal
3
+
4
+ import pytest
5
+ from faker import Faker
6
+ from psycopg.types.range import NumericRange
7
+ from wbcompliance.factories.risk_management import RuleThresholdFactory
8
+ from wbportfolio.risk_management.backends.stop_loss_instrument import (
9
+ RuleBackend as StopLossInstrumentRuleBackend,
10
+ )
11
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
12
+
13
+ fake = Faker()
14
+
15
+
16
+ @pytest.mark.django_db
17
+ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
18
+ @pytest.fixture
19
+ def stop_loss_portfolio_backend(self, weekday, date_interval_option, freq, product):
20
+ parameters = {"freq": freq, "date_interval_option": date_interval_option}
21
+ lower = random.random()
22
+ upper = random.uniform(lower, 1)
23
+ return StopLossInstrumentRuleBackend(
24
+ weekday,
25
+ product,
26
+ parameters,
27
+ [RuleThresholdFactory.create(range=NumericRange(lower=lower, upper=upper))], # type: ignore
28
+ )
29
+
30
+ @pytest.mark.parametrize(
31
+ "date_interval_option, freq",
32
+ [
33
+ ("ROLLING_WINDOWS", StopLossInstrumentRuleBackend.FreqChoices.BUSINESS_DAY),
34
+ *[("FREQUENCY", option) for option in StopLossInstrumentRuleBackend.FreqChoices.values],
35
+ ],
36
+ )
37
+ def test_check_rule_frequency(
38
+ self,
39
+ weekday,
40
+ date_interval_option,
41
+ freq,
42
+ product,
43
+ portfolio,
44
+ instrument_price_factory,
45
+ asset_position_factory,
46
+ stop_loss_portfolio_backend,
47
+ instrument_portfolio_through_model_factory,
48
+ ):
49
+ instrument_portfolio_through_model_factory.create(
50
+ instrument=product,
51
+ portfolio=portfolio,
52
+ primary_portfolio=True,
53
+ )
54
+
55
+ d1 = stop_loss_portfolio_backend._get_start_interval()
56
+
57
+ threshold = stop_loss_portfolio_backend.thresholds[0]
58
+ breach_perf = random.uniform(threshold.range.lower, threshold.range.upper)
59
+
60
+ i1 = instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
61
+ instrument_price_factory.create(
62
+ date=weekday, net_value=Decimal(breach_perf + 1) * i1.net_value, calculated=False, instrument=product
63
+ )
64
+
65
+ asset_position_factory.create(date=weekday, underlying_instrument=product, portfolio=portfolio)
66
+ asset_position_factory.create(date=weekday, underlying_instrument=product, portfolio=portfolio)
67
+
68
+ res = list(stop_loss_portfolio_backend.check_rule())
69
+ assert len(res) == 1
70
+ assert res[0].breached_object.id == product.id
71
+
72
+ @pytest.mark.parametrize(
73
+ "date_interval_option, freq",
74
+ [
75
+ ("ROLLING_WINDOWS", StopLossInstrumentRuleBackend.FreqChoices.BUSINESS_DAY),
76
+ *[("FREQUENCY", option) for option in StopLossInstrumentRuleBackend.FreqChoices.values],
77
+ ],
78
+ )
79
+ def test_check_rule_frequency_2(
80
+ self,
81
+ weekday,
82
+ date_interval_option,
83
+ freq,
84
+ product,
85
+ portfolio,
86
+ instrument_price_factory,
87
+ asset_position_factory,
88
+ instrument_factory,
89
+ stop_loss_portfolio_backend,
90
+ instrument_portfolio_through_model_factory,
91
+ ):
92
+ instrument_portfolio_through_model_factory.create(
93
+ instrument=product,
94
+ portfolio=portfolio,
95
+ primary_portfolio=True,
96
+ )
97
+ d1 = stop_loss_portfolio_backend._get_start_interval()
98
+ benchmark = instrument_factory.create()
99
+
100
+ threshold = stop_loss_portfolio_backend.thresholds[0]
101
+ threshold.range = NumericRange(upper=-0.5, lower=None) # type: ignore
102
+ threshold.save()
103
+
104
+ instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
105
+ instrument_price_factory.create(date=weekday, net_value=100, calculated=False, instrument=product)
106
+
107
+ instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=benchmark)
108
+ instrument_price_factory.create(date=weekday, net_value=300, calculated=False, instrument=benchmark)
109
+
110
+ asset_position_factory.create(date=weekday, underlying_instrument=product, portfolio=portfolio)
111
+ asset_position_factory.create(date=weekday, underlying_instrument=product, portfolio=portfolio)
112
+
113
+ res = list(stop_loss_portfolio_backend.check_rule())
114
+ assert len(res) == 0
115
+
116
+ setattr(stop_loss_portfolio_backend, "static_benchmark", benchmark)
117
+ res = list(stop_loss_portfolio_backend.check_rule())
118
+ assert len(res) == 1
119
+ assert res[0].breached_object.id == product.id
@@ -0,0 +1,39 @@
1
+ import pytest
2
+ from faker import Faker
3
+ from wbportfolio.risk_management.backends.ucits_portfolio import (
4
+ RuleBackend as UCITPortfolioRuleBackend,
5
+ )
6
+ from wbportfolio.tests.models.utils import PortfolioTestMixin
7
+
8
+ fake = Faker()
9
+
10
+
11
+ @pytest.mark.django_db
12
+ class TestUcitsRuleModel(PortfolioTestMixin):
13
+ @pytest.fixture
14
+ def ucits_backend(self, weekday, portfolio):
15
+ return UCITPortfolioRuleBackend(weekday, portfolio, [])
16
+
17
+ def testcheck_rule_1(self, weekday, portfolio, asset_position_factory, ucits_backend):
18
+ # Check No single asset can represent more than 10% of the fund's assets;
19
+ asset_position_factory.create(date=weekday, weighting=0.05, portfolio=portfolio)
20
+ asset_position_factory.create(date=weekday, weighting=0.05, portfolio=portfolio)
21
+ a3 = asset_position_factory.create(date=weekday, weighting=0.90, portfolio=portfolio)
22
+
23
+ res = list(ucits_backend.check_rule())
24
+ assert len(res) == 1
25
+ assert res[0].breached_object == a3.underlying_instrument
26
+
27
+ def testcheck_rule_2(self, weekday, portfolio, asset_position_factory, ucits_backend):
28
+ # Check that stock below 5% don't trigger rule
29
+ asset_position_factory.create_batch(4, date=weekday, weighting=0.05, portfolio=portfolio)
30
+ asset_position_factory.create_batch(20, date=weekday, weighting=0.04, portfolio=portfolio)
31
+
32
+ res = list(ucits_backend.check_rule())
33
+ assert len(res) == 0
34
+
35
+ def testcheck_rule_3(self, weekday, portfolio, asset_position_factory, ucits_backend):
36
+ # Check holdings of more than 5% cannot in aggregate exceed 40% of the fund's assets
37
+ asset_position_factory.create_batch(20, date=weekday, weighting=0.05, portfolio=portfolio)
38
+ res = list(ucits_backend.check_rule())
39
+ assert len(res) == 20
@@ -0,0 +1,42 @@
1
+ from .assets import (
2
+ AssetPositionInstrumentModelSerializer,
3
+ AssetPositionModelSerializer,
4
+ AssetPositionPortfolioModelSerializer,
5
+ AssetPositionAggregatedPortfolioModelSerializer,
6
+ CashPositionPortfolioModelSerializer,
7
+ )
8
+ from .custodians import CustodianModelSerializer, CustodianRepresentationSerializer
9
+ from .portfolio_relationship import (
10
+ InstrumentPreferedClassificationThroughProductModelSerializer,
11
+ InstrumentPortfolioThroughModelSerializer,
12
+ )
13
+ from .portfolios import (
14
+ ModelPortfolioModelSerializer,
15
+ PortfolioModelSerializer,
16
+ PortfolioPortfolioThroughModelSerializer,
17
+ PortfolioRepresentationSerializer,
18
+ )
19
+ from .portfolio_swing_pricing import PortfolioSwingPricingModelSerializer
20
+ from .portfolio_cash_targets import PortfolioCashTargetModelSerializer
21
+ from .positions import AggregatedAssetPositionModelSerializer
22
+ from .registers import RegisterModelSerializer, RegisterRepresentationSerializer
23
+ from .roles import PortfolioRoleModelSerializer, PortfolioRoleProjectModelSerializer
24
+ from .signals import *
25
+ from .synchronization import (
26
+ PortfolioSynchronizationRepresentationSerializer,
27
+ PriceComputationRepresentationSerializer,
28
+ )
29
+ from .transactions import *
30
+ from .products import (
31
+ ProductRepresentationSerializer,
32
+ ProductCustomerRepresentationSerializer,
33
+ ProductUnlinkedRepresentationSerializer,
34
+ ProductListModelSerializer,
35
+ ProductModelSerializer,
36
+ ProductCustomerModelSerializer,
37
+ ProductFeesModelSerializer,
38
+ )
39
+ from .adjustments import AdjustmentModelSerializer
40
+ from .product_group import ProductGroupModelSerializer, ProductGroupRepresentationSerializer
41
+ from .portfolio_cash_flow import DailyPortfolioCashFlowModelSerializer
42
+ from .reconciliations import AccountReconciliationModelSerializer, AccountReconciliationLineModelSerializer
@@ -0,0 +1,24 @@
1
+ from wbcore import serializers as wb_serializers
2
+ from wbcore.contrib.directory.serializers import PersonRepresentationSerializer
3
+ from wbfdm.serializers import SecurityRepresentationSerializer
4
+ from wbportfolio.models import Adjustment
5
+
6
+
7
+ class AdjustmentModelSerializer(wb_serializers.ModelSerializer):
8
+ _instrument = SecurityRepresentationSerializer(source="instrument")
9
+ _last_handler = PersonRepresentationSerializer(source="last_handler")
10
+
11
+ class Meta:
12
+ model = Adjustment
13
+ fields = (
14
+ "id",
15
+ "date",
16
+ "instrument",
17
+ "_instrument",
18
+ "status",
19
+ "factor",
20
+ "cumulative_factor",
21
+ "_last_handler",
22
+ "last_handler",
23
+ "_additional_resources",
24
+ )