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,66 @@
1
+ from django.shortcuts import get_object_or_404
2
+ from rest_framework import filters
3
+ from rest_framework.decorators import action
4
+ from rest_framework.response import Response
5
+ from wbcore import viewsets
6
+ from wbcore.permissions.permissions import InternalUserPermissionMixin
7
+ from wbportfolio.filters import CustodianFilterSet
8
+ from wbportfolio.models import Custodian
9
+ from wbportfolio.serializers import (
10
+ CustodianModelSerializer,
11
+ CustodianRepresentationSerializer,
12
+ )
13
+
14
+ from .configs import (
15
+ CustodianButtonConfig,
16
+ CustodianDisplayConfig,
17
+ CustodianEndpointConfig,
18
+ CustodianTitleConfig,
19
+ )
20
+
21
+
22
+ class CustodianRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
23
+ IDENTIFIER = "wbportfolio:custodian"
24
+
25
+ filter_backends = (
26
+ filters.SearchFilter,
27
+ filters.OrderingFilter,
28
+ )
29
+
30
+ queryset = Custodian.objects.all()
31
+ serializer_class = CustodianRepresentationSerializer
32
+
33
+ search_fields = ("name",)
34
+ ordering_fields = search_fields
35
+ ordering = ["name"]
36
+
37
+
38
+ class CustodianModelViewSet(InternalUserPermissionMixin, viewsets.ModelViewSet):
39
+ queryset = Custodian.objects.all()
40
+ filterset_class = CustodianFilterSet
41
+ serializer_class = CustodianModelSerializer
42
+
43
+ search_fields = ("name", "mapping")
44
+ ordering_fields = search_fields
45
+ ordering = ["name"]
46
+
47
+ display_config_class = CustodianDisplayConfig
48
+ button_config_class = CustodianButtonConfig
49
+ title_config_class = CustodianTitleConfig
50
+ endpoint_config_class = CustodianEndpointConfig
51
+
52
+ @action(detail=True, methods=["PATCH"])
53
+ def merge(self, request, pk=None):
54
+ if (merged_custodian_id := request.POST.get("merged_custodian", None)) and merged_custodian_id != pk:
55
+ merged_custodian = get_object_or_404(Custodian, pk=merged_custodian_id)
56
+ base_custodian = get_object_or_404(Custodian, pk=pk)
57
+ base_custodian.merge(merged_custodian)
58
+ return Response({"send": True})
59
+ return Response({"send": True})
60
+
61
+ @action(detail=True, methods=["PATCH"])
62
+ def split(self, request, pk=None):
63
+ if mapping_choice := request.POST.get("mapping_choice", None):
64
+ custodian = get_object_or_404(Custodian, pk=pk)
65
+ custodian.split_off(mapping_choice)
66
+ return Response({"send": True})
@@ -0,0 +1,165 @@
1
+ from contextlib import suppress
2
+ from datetime import date, datetime
3
+
4
+ import pandas as pd
5
+ from django.contrib.messages import warning
6
+ from django.db.models import Q
7
+ from django.utils.functional import cached_property
8
+ from wbcore.contrib.io.viewsets import ExportPandasAPIViewSet
9
+ from wbcore.pandas import fields as pf
10
+ from wbcore.utils.strings import format_number
11
+ from wbfdm.analysis.esg.enums import AggregationMethod, ESGAggregation
12
+ from wbfdm.analysis.esg.esg_analysis import DataLoader
13
+ from wbfdm.models import Instrument
14
+ from wbportfolio.filters import ESGMetricAggregationPortfolioPandasFilterSet
15
+ from wbportfolio.models import AssetPosition
16
+ from wbportfolio.viewsets.configs import (
17
+ ESGMetricAggregationPortfolioPandasDisplayConfig,
18
+ ESGMetricAggregationPortfolioPandasEndpointConfig,
19
+ ESGMetricAggregationPortfolioPandasTitleConfig,
20
+ )
21
+
22
+ from .mixins import UserPortfolioRequestPermissionMixin
23
+
24
+
25
+ class ESGMetricAggregationPortfolioPandasViewSet(UserPortfolioRequestPermissionMixin, ExportPandasAPIViewSet):
26
+ IDENTIFIER = "wbportfolio:esgmetricportfolio"
27
+ filterset_class = ESGMetricAggregationPortfolioPandasFilterSet
28
+ queryset = AssetPosition.objects.all()
29
+
30
+ display_config_class = ESGMetricAggregationPortfolioPandasDisplayConfig
31
+ title_config_class = ESGMetricAggregationPortfolioPandasTitleConfig
32
+ endpoint_config_class = ESGMetricAggregationPortfolioPandasEndpointConfig
33
+
34
+ search_fields = ["underlying_instrument_repr"]
35
+
36
+ @cached_property
37
+ def val_date(self) -> date:
38
+ if validity_date_repr := self.request.GET.get("date"):
39
+ val_date = datetime.strptime(validity_date_repr, "%Y-%m-%d")
40
+ else:
41
+ val_date = date.today()
42
+
43
+ qs = AssetPosition.objects.filter(portfolio=self.portfolio, date__lte=val_date)
44
+ if qs.exists():
45
+ return qs.latest("date").date
46
+ else:
47
+ warning(
48
+ self.request,
49
+ f"No position were found before the {val_date:%Y-%m-%d}. Please select another date",
50
+ )
51
+ return val_date
52
+
53
+ @cached_property
54
+ def base_df(self) -> pd.DataFrame:
55
+ if self.action != "metadata":
56
+ with suppress(KeyError):
57
+ df = (
58
+ pd.DataFrame(
59
+ AssetPosition.objects.filter(portfolio=self.portfolio, date=self.val_date)
60
+ .exclude(
61
+ Q(underlying_instrument__is_cash=True) | Q(underlying_instrument__is_cash_equivalent=True)
62
+ )
63
+ .values("underlying_instrument", "weighting", "total_value_fx_usd")
64
+ )
65
+ .groupby("underlying_instrument")
66
+ .sum()
67
+ .astype(float)
68
+ )
69
+ df["weighting"] = df["weighting"] / df["weighting"].sum()
70
+ return df
71
+ return pd.DataFrame(columns=["weighting", "total_value_fx_usd"])
72
+
73
+ @cached_property
74
+ def esg_aggregation(self) -> ESGAggregation | None:
75
+ return ESGAggregation[self.request.GET.get("esg_aggregation", ESGAggregation.GHG_EMISSIONS_SCOPE_1.name)]
76
+
77
+ @cached_property
78
+ def esg_data(self) -> pd.Series:
79
+ if self.action != "metadata":
80
+ return self.esg_aggregation.get_esg_data(Instrument.objects.filter(id__in=self.base_df.index))
81
+ return pd.Series(dtype="float64")
82
+
83
+ @cached_property
84
+ def dataloader(self) -> DataLoader:
85
+ dataloader = DataLoader(
86
+ self.base_df["weighting"],
87
+ self.esg_data,
88
+ self.val_date,
89
+ total_value_fx_usd=self.base_df["total_value_fx_usd"],
90
+ )
91
+ self.metric = dataloader.compute(self.esg_aggregation).rename("metric")
92
+ return dataloader
93
+
94
+ @cached_property
95
+ def metric_columns(self) -> list[str]:
96
+ return (
97
+ ["weighting", "total_value_fx_usd", "esg_data", "weights_in_coverage", "metric"]
98
+ + list(map(lambda x: x.series.name, self.dataloader.extra_esg_data_logs))
99
+ + list(map(lambda x: x.series.name, self.dataloader.intermediary_logs))
100
+ )
101
+
102
+ def get_pandas_fields(self, request):
103
+ fields = [
104
+ pf.PKField(key="underlying_instrument", label="ID"),
105
+ pf.CharField(key="underlying_instrument_repr", label="Instrument"),
106
+ pf.FloatField(key="weighting", label="Weight", percent=True),
107
+ pf.FloatField(key="total_value_fx_usd", label="Total Asset (USD)"),
108
+ pf.FloatField(key="esg_data", label="ESG Data", precision=4),
109
+ pf.FloatField(key="weights_in_coverage", label="Weights in Coverage", percent=True),
110
+ ]
111
+ for extra_esg_data_log in self.dataloader.extra_esg_data_logs:
112
+ fields.append(
113
+ pf.FloatField(
114
+ key=extra_esg_data_log.series.name,
115
+ label=extra_esg_data_log.label,
116
+ percent=extra_esg_data_log.is_percent,
117
+ precision=extra_esg_data_log.precision,
118
+ )
119
+ )
120
+ for intermediary_log in self.dataloader.intermediary_logs:
121
+ fields.append(
122
+ pf.FloatField(
123
+ key=intermediary_log.series.name,
124
+ label=intermediary_log.label,
125
+ percent=intermediary_log.is_percent,
126
+ precision=intermediary_log.precision,
127
+ )
128
+ )
129
+ if self.esg_aggregation.get_aggregation() == AggregationMethod.PERCENTAGE_SUM:
130
+ fields.append(pf.FloatField(key="metric", label="Metric", percent=True))
131
+ else:
132
+ fields.append(pf.FloatField(key="metric", label="Metric", precision=4))
133
+ return pf.PandasFields(fields=fields)
134
+
135
+ def get_dataframe(self, request, queryset, **kwargs):
136
+ dataloader = self.dataloader
137
+ df = pd.concat(
138
+ [
139
+ self.base_df["weighting"],
140
+ self.base_df["total_value_fx_usd"],
141
+ self.esg_data.rename("esg_data"),
142
+ *map(lambda x: x.series, dataloader.extra_esg_data_logs),
143
+ dataloader.weights_in_coverage.rename("weights_in_coverage"),
144
+ *map(lambda x: x.series, dataloader.intermediary_logs),
145
+ self.metric,
146
+ ],
147
+ axis=1,
148
+ )
149
+ return df
150
+
151
+ def manipulate_dataframe(self, df):
152
+ df["underlying_instrument_repr"] = df.index.map(
153
+ dict(self.dataloader.instruments.values_list("id", "computed_str"))
154
+ )
155
+ return df.reset_index()
156
+
157
+ def get_aggregates(self, request, df):
158
+ aggregates = {}
159
+ for column in self.metric_columns:
160
+ with suppress(TypeError, KeyError): # silent aggregation on str type
161
+ aggregates[column] = {"Σ": format_number(df[column].sum(), decimal=4)}
162
+ return aggregates
163
+
164
+ def get_ordering_fields(self) -> list[str]:
165
+ return ["underlying_instrument_repr"] + self.metric_columns
@@ -0,0 +1,48 @@
1
+ from django.shortcuts import get_object_or_404
2
+ from django.utils.functional import cached_property
3
+ from wbfdm.models import Instrument
4
+ from wbportfolio.models import Portfolio, PortfolioRole
5
+
6
+
7
+ class UserPortfolioRequestPermissionMixin:
8
+ @cached_property
9
+ def instrument(self) -> Instrument:
10
+ return get_object_or_404(Instrument, pk=self.kwargs["instrument_id"])
11
+
12
+ @cached_property
13
+ def portfolio(self) -> Portfolio:
14
+ return get_object_or_404(Portfolio, id=self.kwargs["portfolio_id"])
15
+
16
+ @cached_property
17
+ def instrument_or_none(self) -> Instrument | None:
18
+ try:
19
+ return get_object_or_404(Instrument, pk=self.kwargs["instrument_id"])
20
+ except Exception:
21
+ return None
22
+
23
+ @cached_property
24
+ def portfolio_or_none(self) -> Portfolio | None:
25
+ try:
26
+ return get_object_or_404(Portfolio, id=self.kwargs["portfolio_id"])
27
+ except Exception:
28
+ return None
29
+
30
+ @cached_property
31
+ def is_manager(self) -> bool:
32
+ return PortfolioRole.is_manager(self.request.user.profile)
33
+
34
+ @cached_property
35
+ def is_portfolio_manager(self) -> bool:
36
+ return PortfolioRole.is_portfolio_manager(
37
+ self.request.user.profile, portfolio=self.portfolio_or_none, instrument=self.instrument_or_none
38
+ )
39
+
40
+ @cached_property
41
+ def is_analyst(self) -> bool:
42
+ return PortfolioRole.is_analyst(
43
+ self.request.user.profile, portfolio=self.portfolio_or_none, instrument=self.instrument_or_none
44
+ )
45
+
46
+ @cached_property
47
+ def has_portfolio_access(self) -> bool:
48
+ return self.is_portfolio_manager or self.is_analyst
@@ -0,0 +1,31 @@
1
+ from django.db.models import Case, F, Value, When
2
+ from wbcore import viewsets
3
+ from wbportfolio.models import DailyPortfolioCashFlow
4
+ from wbportfolio.serializers import DailyPortfolioCashFlowModelSerializer
5
+ from wbportfolio.viewsets.configs.display import DailyPortfolioCashFlowDisplayConfig
6
+ from wbportfolio.viewsets.configs.titles import DailyPortfolioCashFlowTitleConfig
7
+
8
+
9
+ class DailyPortfolioCashFlowModelViewSet(viewsets.ModelViewSet):
10
+ queryset = DailyPortfolioCashFlow.objects.all()
11
+ serializer_class = DailyPortfolioCashFlowModelSerializer
12
+
13
+ display_config_class = DailyPortfolioCashFlowDisplayConfig
14
+ title_config_class = DailyPortfolioCashFlowTitleConfig
15
+
16
+ ordering_fields = ("value_date",)
17
+ ordering = ("-value_date",)
18
+
19
+ def get_queryset(self):
20
+ queryset = super().get_queryset()
21
+
22
+ if portfolio_id := self.kwargs.get("portfolio_id", None):
23
+ queryset = queryset.filter(portfolio_id=portfolio_id)
24
+
25
+ return queryset.annotate(
26
+ swing_pricing_indicator=Case(
27
+ When(cash_flow_asset_ratio__lt=F("swing_pricing__negative_threshold"), then=Value("neg")),
28
+ When(cash_flow_asset_ratio__gt=F("swing_pricing__positive_threshold"), then=Value("pos")),
29
+ default=None,
30
+ )
31
+ )
@@ -0,0 +1,8 @@
1
+ from wbcore import viewsets
2
+ from wbportfolio.models import PortfolioCashTarget
3
+ from wbportfolio.serializers import PortfolioCashTargetModelSerializer
4
+
5
+
6
+ class PortfolioCashTargetModelViewSet(viewsets.ModelViewSet):
7
+ queryset = PortfolioCashTarget.objects.all()
8
+ serializer_class = PortfolioCashTargetModelSerializer
@@ -0,0 +1,46 @@
1
+ from wbcore import viewsets
2
+ from wbcore.permissions.permissions import InternalUserPermissionMixin
3
+ from wbportfolio.models import (
4
+ InstrumentPortfolioThroughModel,
5
+ PortfolioInstrumentPreferredClassificationThroughModel,
6
+ )
7
+ from wbportfolio.serializers import (
8
+ InstrumentPortfolioThroughModelSerializer,
9
+ InstrumentPreferedClassificationThroughProductModelSerializer,
10
+ )
11
+
12
+ from .configs import (
13
+ InstrumentPortfolioThroughPortfolioModelDisplayConfig,
14
+ InstrumentPortfolioThroughPortfolioModelEndpointConfig,
15
+ PortfolioInstrumentPreferredClassificationThroughDisplayConfig,
16
+ PortfolioInstrumentPreferredClassificationThroughEndpointConfig,
17
+ )
18
+
19
+
20
+ class InstrumentPreferedClassificationThroughProductModelViewSet(InternalUserPermissionMixin, viewsets.ModelViewSet):
21
+ serializer_class = InstrumentPreferedClassificationThroughProductModelSerializer
22
+ queryset = PortfolioInstrumentPreferredClassificationThroughModel.objects.all()
23
+
24
+ search_fields = ("instrument__computed_str", "classification__computed_str")
25
+ ordering_fields = ordering = ["instrument__computed_str"]
26
+ filterset_fields = {"instrument": ["exact"], "classification": ["exact"]}
27
+ display_config_class = PortfolioInstrumentPreferredClassificationThroughDisplayConfig
28
+ endpoint_config_class = PortfolioInstrumentPreferredClassificationThroughEndpointConfig
29
+
30
+ def get_queryset(self):
31
+ return super().get_queryset().filter(portfolio=self.kwargs["portfolio_id"])
32
+
33
+
34
+ class InstrumentPortfolioThroughPortfolioModelViewSet(InternalUserPermissionMixin, viewsets.ModelViewSet):
35
+ serializer_class = InstrumentPortfolioThroughModelSerializer
36
+ queryset = InstrumentPortfolioThroughModel.objects.all()
37
+
38
+ search_fields = ("instrument__name",)
39
+ ordering_fields = ["instrument__name"]
40
+ ordering = ["instrument__name"]
41
+
42
+ display_config_class = InstrumentPortfolioThroughPortfolioModelDisplayConfig
43
+ endpoint_config_class = InstrumentPortfolioThroughPortfolioModelEndpointConfig
44
+
45
+ def get_queryset(self):
46
+ return super().get_queryset().filter(portfolio=self.kwargs["portfolio_id"])
@@ -0,0 +1,8 @@
1
+ from wbcore import viewsets
2
+ from wbportfolio.models.portfolio_swing_pricings import PortfolioSwingPricing
3
+ from wbportfolio.serializers import PortfolioSwingPricingModelSerializer
4
+
5
+
6
+ class PortfolioSwingPricingModelViewSet(viewsets.ModelViewSet):
7
+ queryset = PortfolioSwingPricing.objects.all()
8
+ serializer_class = PortfolioSwingPricingModelSerializer
@@ -0,0 +1,154 @@
1
+ from datetime import datetime
2
+
3
+ from django.http import HttpResponse
4
+ from django.shortcuts import get_object_or_404
5
+ from rest_framework.decorators import action
6
+ from rest_framework.response import Response
7
+ from wbcore import viewsets
8
+ from wbcore.contrib.currency.models import Currency
9
+ from wbcore.permissions.permissions import InternalUserPermissionMixin
10
+ from wbcore.utils.date import get_date_interval_from_request
11
+ from wbportfolio.filters import PortfolioFilterSet
12
+ from wbportfolio.models import (
13
+ Portfolio,
14
+ PortfolioPortfolioThroughModel,
15
+ PortfolioSynchronization,
16
+ PriceComputation,
17
+ TradeProposal,
18
+ )
19
+ from wbportfolio.models.portfolio import resynchronize_history_as_task
20
+ from wbportfolio.serializers import (
21
+ ModelPortfolioModelSerializer,
22
+ PortfolioModelSerializer,
23
+ PortfolioPortfolioThroughModelSerializer,
24
+ PortfolioRepresentationSerializer,
25
+ )
26
+
27
+ from .configs import (
28
+ ModelPortfolioButtonConfig,
29
+ ModelPortfolioDisplayConfig,
30
+ ModelPortfolioEndpointConfig,
31
+ ModelPortfolioTitleConfig,
32
+ PortfolioButtonConfig,
33
+ PortfolioDisplayConfig,
34
+ PortfolioEndpointConfig,
35
+ PortfolioPortfolioThroughModelDisplayConfig,
36
+ PortfolioPortfolioThroughModelEndpointConfig,
37
+ PortfolioPreviewConfig,
38
+ PortfolioTitleConfig,
39
+ )
40
+ from .mixins import UserPortfolioRequestPermissionMixin
41
+
42
+
43
+ class PortfolioRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
44
+ IDENTIFIER = "wbportfolio:portfolio"
45
+
46
+ ordering_fields = ordering = search_fields = ("name",)
47
+ queryset = Portfolio.objects.all()
48
+ serializer_class = PortfolioRepresentationSerializer
49
+ filterset_class = PortfolioFilterSet
50
+
51
+
52
+ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPermissionMixin, viewsets.ModelViewSet):
53
+ filterset_class = PortfolioFilterSet
54
+ serializer_class = PortfolioModelSerializer
55
+ queryset = Portfolio.objects.all()
56
+
57
+ search_fields = ("currency__key", "name")
58
+ ordering_fields = search_fields
59
+ ordering = ["name"]
60
+
61
+ display_config_class = PortfolioDisplayConfig
62
+ button_config_class = PortfolioButtonConfig
63
+ title_config_class = PortfolioTitleConfig
64
+ endpoint_config_class = PortfolioEndpointConfig
65
+ preview_config_class = PortfolioPreviewConfig
66
+
67
+ def get_queryset(self):
68
+ return (
69
+ super()
70
+ .get_queryset()
71
+ .select_related(
72
+ "currency",
73
+ "portfolio_synchronization",
74
+ )
75
+ .prefetch_related(
76
+ "depends_on",
77
+ "dependent_portfolios",
78
+ )
79
+ )
80
+
81
+ @action(detail=True, methods=["PATCH"])
82
+ def rebalance(self, request, pk=None):
83
+ if (date_str := request.POST.get("trade_date", None)) and (
84
+ model_portfolio_id := request.POST.get("model_portfolio", None)
85
+ ):
86
+ TradeProposal.objects.create(
87
+ portfolio=Portfolio.objects.get(id=pk),
88
+ model_portfolio=Portfolio.objects.get(id=model_portfolio_id),
89
+ trade_date=datetime.strptime(date_str, "%Y-%m-%d"),
90
+ )
91
+ return Response({"send": True})
92
+ raise HttpResponse("Bad Request", status=400)
93
+
94
+ @action(detail=True, methods=["PATCH"])
95
+ def resynchronize(self, request, pk=None):
96
+ if request.user.has_perm("wbportfolio.administrate_instrument"):
97
+ portfolio = get_object_or_404(Portfolio, pk=pk)
98
+ instrument = request.GET.get("instrument", None)
99
+ start, end = get_date_interval_from_request(request, request_type="POST")
100
+ resynchronize_history_as_task.delay(portfolio.id, start, end, instrument_id=instrument)
101
+ return Response({"send": True})
102
+ return HttpResponse("Unauthorized", status=401)
103
+
104
+
105
+ class ModelPortfolioModelViewSet(PortfolioModelViewSet):
106
+ serializer_class = ModelPortfolioModelSerializer
107
+ endpoint_config_class = ModelPortfolioEndpointConfig
108
+ display_config_class = ModelPortfolioDisplayConfig
109
+ title_config_class = ModelPortfolioTitleConfig
110
+ button_config_class = ModelPortfolioButtonConfig
111
+
112
+ @action(detail=False, methods=["POST"])
113
+ def createmodelportfolio(self, request, pk=None):
114
+ if self.is_portfolio_manager:
115
+ name = request.POST["name"]
116
+ currency_id = request.POST["currency"]
117
+ currency = Currency.objects.get(id=currency_id)
118
+ portfolio_synchronization_id = request.POST.get("portfolio_synchronization", None)
119
+ price_computation_id = request.POST.get("price_computation", None)
120
+ portfolio_synchronization = (
121
+ PortfolioSynchronization.objects.get(id=portfolio_synchronization_id)
122
+ if portfolio_synchronization_id
123
+ else None
124
+ )
125
+ create_index = request.POST.get("create_index", "false") == "true"
126
+ index_parameters = {}
127
+ if create_index:
128
+ index_parameters["price_computation"] = (
129
+ PriceComputation.objects.get(id=price_computation_id) if price_computation_id else None
130
+ )
131
+ Portfolio.create_model_portfolio(
132
+ name, currency, portfolio_synchronization=portfolio_synchronization, index_parameters=index_parameters
133
+ )
134
+ return Response({"send": True})
135
+ raise HttpResponse("Unauthorized", status=403)
136
+
137
+ def get_queryset(self):
138
+ model_portfolios = PortfolioPortfolioThroughModel.objects.filter(
139
+ type=PortfolioPortfolioThroughModel.Type.MODEL
140
+ ).values("dependency_portfolio")
141
+ return super().get_queryset().filter(id__in=model_portfolios)
142
+
143
+
144
+ class PortfolioPortfolioThroughModelViewSet(InternalUserPermissionMixin, viewsets.ModelViewSet):
145
+ serializer_class = PortfolioPortfolioThroughModelSerializer
146
+ queryset = PortfolioPortfolioThroughModel.objects.all()
147
+
148
+ search_fields = ["dependency_portfolio__name"]
149
+
150
+ display_config_class = PortfolioPortfolioThroughModelDisplayConfig
151
+ endpoint_config_class = PortfolioPortfolioThroughModelEndpointConfig
152
+
153
+ def get_queryset(self):
154
+ return super().get_queryset().filter(portfolio=self.kwargs["portfolio_id"])