wbportfolio 1.43.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 (506) 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 +153 -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 +248 -0
  92. wbportfolio/filters/transactions/transactions.py +98 -0
  93. wbportfolio/fixtures/product_factsheets.yaml +1 -0
  94. wbportfolio/fixtures/wbportfolio.yaml.gz +0 -0
  95. wbportfolio/fixtures/wbrisk_management.yaml.gz +0 -0
  96. wbportfolio/import_export/__init__.py +0 -0
  97. wbportfolio/import_export/backends/__init__.py +2 -0
  98. wbportfolio/import_export/backends/refinitiv/adjustment.py +40 -0
  99. wbportfolio/import_export/backends/ubs/__init__.py +3 -0
  100. wbportfolio/import_export/backends/ubs/asset_position.py +45 -0
  101. wbportfolio/import_export/backends/ubs/fees.py +63 -0
  102. wbportfolio/import_export/backends/ubs/instrument_price.py +44 -0
  103. wbportfolio/import_export/backends/ubs/mixin.py +15 -0
  104. wbportfolio/import_export/backends/utils.py +58 -0
  105. wbportfolio/import_export/backends/wbfdm/__init__.py +2 -0
  106. wbportfolio/import_export/backends/wbfdm/adjustment.py +50 -0
  107. wbportfolio/import_export/backends/wbfdm/dividend.py +16 -0
  108. wbportfolio/import_export/backends/wbfdm/mixin.py +15 -0
  109. wbportfolio/import_export/handlers/__init__.py +0 -0
  110. wbportfolio/import_export/handlers/adjustment.py +39 -0
  111. wbportfolio/import_export/handlers/asset_position.py +167 -0
  112. wbportfolio/import_export/handlers/dividend.py +80 -0
  113. wbportfolio/import_export/handlers/fees.py +58 -0
  114. wbportfolio/import_export/handlers/portfolio_cash_flow.py +57 -0
  115. wbportfolio/import_export/handlers/register.py +43 -0
  116. wbportfolio/import_export/handlers/trade.py +191 -0
  117. wbportfolio/import_export/parsers/__init__.py +0 -0
  118. wbportfolio/import_export/parsers/default_mapping.py +30 -0
  119. wbportfolio/import_export/parsers/jpmorgan/__init__.py +0 -0
  120. wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +63 -0
  121. wbportfolio/import_export/parsers/jpmorgan/fees.py +64 -0
  122. wbportfolio/import_export/parsers/jpmorgan/strategy.py +116 -0
  123. wbportfolio/import_export/parsers/jpmorgan/valuation.py +41 -0
  124. wbportfolio/import_export/parsers/leonteq/__init__.py +0 -0
  125. wbportfolio/import_export/parsers/leonteq/customer_trade.py +47 -0
  126. wbportfolio/import_export/parsers/leonteq/equity.py +81 -0
  127. wbportfolio/import_export/parsers/leonteq/fees.py +70 -0
  128. wbportfolio/import_export/parsers/leonteq/trade.py +94 -0
  129. wbportfolio/import_export/parsers/leonteq/valuation.py +39 -0
  130. wbportfolio/import_export/parsers/natixis/__init__.py +0 -0
  131. wbportfolio/import_export/parsers/natixis/customer_trade.py +62 -0
  132. wbportfolio/import_export/parsers/natixis/d1_customer_trade.py +66 -0
  133. wbportfolio/import_export/parsers/natixis/d1_equity.py +80 -0
  134. wbportfolio/import_export/parsers/natixis/d1_fees.py +58 -0
  135. wbportfolio/import_export/parsers/natixis/d1_trade.py +70 -0
  136. wbportfolio/import_export/parsers/natixis/d1_valuation.py +41 -0
  137. wbportfolio/import_export/parsers/natixis/dividend.py +53 -0
  138. wbportfolio/import_export/parsers/natixis/equity.py +60 -0
  139. wbportfolio/import_export/parsers/natixis/fees.py +53 -0
  140. wbportfolio/import_export/parsers/natixis/trade.py +63 -0
  141. wbportfolio/import_export/parsers/natixis/utils.py +76 -0
  142. wbportfolio/import_export/parsers/natixis/valuation.py +46 -0
  143. wbportfolio/import_export/parsers/refinitiv/__init__.py +0 -0
  144. wbportfolio/import_export/parsers/refinitiv/adjustment.py +24 -0
  145. wbportfolio/import_export/parsers/sg_lux/__init__.py +0 -0
  146. wbportfolio/import_export/parsers/sg_lux/custodian_positions.py +70 -0
  147. wbportfolio/import_export/parsers/sg_lux/customer_trade.py +75 -0
  148. wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +140 -0
  149. wbportfolio/import_export/parsers/sg_lux/customer_trade_slk.py +80 -0
  150. wbportfolio/import_export/parsers/sg_lux/customer_trade_without_pw.py +57 -0
  151. wbportfolio/import_export/parsers/sg_lux/equity.py +161 -0
  152. wbportfolio/import_export/parsers/sg_lux/fees.py +56 -0
  153. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +51 -0
  154. wbportfolio/import_export/parsers/sg_lux/portfolio_cash_flow.py +29 -0
  155. wbportfolio/import_export/parsers/sg_lux/portfolio_future_cash_flow.py +36 -0
  156. wbportfolio/import_export/parsers/sg_lux/registers.py +210 -0
  157. wbportfolio/import_export/parsers/sg_lux/sylk.py +248 -0
  158. wbportfolio/import_export/parsers/sg_lux/utils.py +36 -0
  159. wbportfolio/import_export/parsers/sg_lux/valuation.py +53 -0
  160. wbportfolio/import_export/parsers/societe_generale/__init__.py +0 -0
  161. wbportfolio/import_export/parsers/societe_generale/customer_trade.py +54 -0
  162. wbportfolio/import_export/parsers/societe_generale/strategy.py +94 -0
  163. wbportfolio/import_export/parsers/societe_generale/valuation.py +37 -0
  164. wbportfolio/import_export/parsers/tellco/__init__.py +0 -0
  165. wbportfolio/import_export/parsers/tellco/customer_trade.py +64 -0
  166. wbportfolio/import_export/parsers/tellco/equity.py +86 -0
  167. wbportfolio/import_export/parsers/tellco/valuation.py +52 -0
  168. wbportfolio/import_export/parsers/ubs/__init__.py +0 -0
  169. wbportfolio/import_export/parsers/ubs/api/__init__.py +0 -0
  170. wbportfolio/import_export/parsers/ubs/api/asset_position.py +106 -0
  171. wbportfolio/import_export/parsers/ubs/api/fees.py +31 -0
  172. wbportfolio/import_export/parsers/ubs/api/instrument_price.py +20 -0
  173. wbportfolio/import_export/parsers/ubs/api/utils.py +0 -0
  174. wbportfolio/import_export/parsers/ubs/customer_trade.py +60 -0
  175. wbportfolio/import_export/parsers/ubs/equity.py +97 -0
  176. wbportfolio/import_export/parsers/ubs/historical_customer_trade.py +67 -0
  177. wbportfolio/import_export/parsers/ubs/valuation.py +52 -0
  178. wbportfolio/import_export/parsers/vontobel/__init__.py +0 -0
  179. wbportfolio/import_export/parsers/vontobel/asset_position.py +97 -0
  180. wbportfolio/import_export/parsers/vontobel/customer_trade.py +54 -0
  181. wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +40 -0
  182. wbportfolio/import_export/parsers/vontobel/instrument.py +34 -0
  183. wbportfolio/import_export/parsers/vontobel/management_fees.py +86 -0
  184. wbportfolio/import_export/parsers/vontobel/performance_fees.py +35 -0
  185. wbportfolio/import_export/parsers/vontobel/trade.py +38 -0
  186. wbportfolio/import_export/parsers/vontobel/utils.py +17 -0
  187. wbportfolio/import_export/parsers/vontobel/valuation.py +29 -0
  188. wbportfolio/import_export/resources/__init__.py +0 -0
  189. wbportfolio/import_export/resources/assets.py +68 -0
  190. wbportfolio/import_export/resources/trades.py +41 -0
  191. wbportfolio/import_export/utils.py +42 -0
  192. wbportfolio/jinja2/wbportfolio/sql/aum_nnm.sql +119 -0
  193. wbportfolio/kpi_handlers/nnm.py +163 -0
  194. wbportfolio/metric/__init__.py +0 -0
  195. wbportfolio/metric/backends/__init__.py +2 -0
  196. wbportfolio/metric/backends/base.py +86 -0
  197. wbportfolio/metric/backends/constants.py +222 -0
  198. wbportfolio/metric/backends/portfolio_base.py +255 -0
  199. wbportfolio/metric/backends/portfolio_esg.py +66 -0
  200. wbportfolio/metric/tests/__init__.py +0 -0
  201. wbportfolio/metric/tests/conftest.py +4 -0
  202. wbportfolio/metric/tests/test_portfolio_base.py +135 -0
  203. wbportfolio/metric/tests/test_portfolio_esg.py +69 -0
  204. wbportfolio/migrations/0001_initial_squashed.py +13848 -0
  205. wbportfolio/migrations/0002_product_default_sub_account_squashed_0039_alter_assetallocation_company_and_more.py +3836 -0
  206. wbportfolio/migrations/0040_instrument_financial_instrument.py +26 -0
  207. wbportfolio/migrations/0041_remove_listresearch_research_ptr_and_more.py +129 -0
  208. wbportfolio/migrations/0042_instrumentlist_instrumentlistthroughmodel_and_more.py +71 -0
  209. wbportfolio/migrations/0043_alter_instrumentlistthroughmodel_options_and_more.py +238 -0
  210. wbportfolio/migrations/0044_alter_instrumentlist_identifier.py +35 -0
  211. wbportfolio/migrations/0045_alter_instrument_financial_instrument.py +26 -0
  212. wbportfolio/migrations/0046_add_product_default_account.py +166 -0
  213. wbportfolio/migrations/0047_remove_product_default_sub_account.py +14 -0
  214. wbportfolio/migrations/0048_alter_trade_status.py +29 -0
  215. wbportfolio/migrations/0049_trade_claimed_shares.py +25 -0
  216. wbportfolio/migrations/0050_fees_fee_date_fees_wbportfolio_transac_1f7a29_idx.py +44 -0
  217. wbportfolio/migrations/0051_delete_macroreview.py +11 -0
  218. wbportfolio/migrations/0052_remove_cash_instrument_ptr_and_more.py +888 -0
  219. wbportfolio/migrations/0053_remove_product_group.py +132 -0
  220. wbportfolio/migrations/0054_portfolioinstrumentpreferredclassificationthroughmodel_and_more.py +270 -0
  221. wbportfolio/migrations/0055_remove_product__custom_management_rebates_and_more.py +139 -0
  222. wbportfolio/migrations/0056_remove_companyportfoliodata_assets_under_management_currency_and_more.py +56 -0
  223. wbportfolio/migrations/0057_alter_portfolio_preferred_instrument_classifications_and_more.py +36 -0
  224. wbportfolio/migrations/0058_pmsinstrument.py +23 -0
  225. wbportfolio/migrations/0059_fees_unique_fees.py +51 -0
  226. wbportfolio/migrations/0060_alter_portfolioportfoliothroughmodel_type.py +21 -0
  227. wbportfolio/migrations/0061_portfolio_bank_accounts_product_bank_account_and_more.py +175 -0
  228. wbportfolio/migrations/0062_alter_dailyportfoliocashflow_options.py +20 -0
  229. wbportfolio/migrations/0063_accountreconciliation_accountreconciliationline_and_more.py +133 -0
  230. wbportfolio/migrations/0064_alter_portfolio_managers_portfolio_is_tracked_and_more.py +40 -0
  231. wbportfolio/migrations/0065_alter_portfolio_managers_claim_as_shares_and_more.py +73 -0
  232. wbportfolio/migrations/0066_assetposition_initial_shares_at_custodian_and_more.py +108 -0
  233. wbportfolio/migrations/0067_assetposition_unique_asset_position.py +77 -0
  234. wbportfolio/migrations/0068_trade_internal_trade_trade_marked_as_internal_and_more.py +59 -0
  235. wbportfolio/migrations/0069_remove_portfolio_is_invested_and_more.py +56 -0
  236. wbportfolio/migrations/0070_remove_assetposition_unique_asset_position_and_more.py +82 -0
  237. wbportfolio/migrations/0071_alter_trade_options_alter_trade_order.py +22 -0
  238. wbportfolio/migrations/__init__.py +0 -0
  239. wbportfolio/models/__init__.py +26 -0
  240. wbportfolio/models/adjustments.py +246 -0
  241. wbportfolio/models/asset.py +869 -0
  242. wbportfolio/models/custodians.py +101 -0
  243. wbportfolio/models/indexes.py +33 -0
  244. wbportfolio/models/llm/wbcrm/analyze_relationship.py +58 -0
  245. wbportfolio/models/mixins/__init__.py +0 -0
  246. wbportfolio/models/mixins/instruments.py +127 -0
  247. wbportfolio/models/mixins/liquidity_stress_test.py +1307 -0
  248. wbportfolio/models/portfolio.py +1039 -0
  249. wbportfolio/models/portfolio_cash_flow.py +167 -0
  250. wbportfolio/models/portfolio_cash_targets.py +46 -0
  251. wbportfolio/models/portfolio_relationship.py +135 -0
  252. wbportfolio/models/portfolio_swing_pricings.py +51 -0
  253. wbportfolio/models/product_groups.py +230 -0
  254. wbportfolio/models/products.py +569 -0
  255. wbportfolio/models/reconciliations/__init__.py +2 -0
  256. wbportfolio/models/reconciliations/account_reconciliation_lines.py +192 -0
  257. wbportfolio/models/reconciliations/account_reconciliations.py +102 -0
  258. wbportfolio/models/reconciliations/reconciliations.py +25 -0
  259. wbportfolio/models/registers.py +132 -0
  260. wbportfolio/models/roles.py +208 -0
  261. wbportfolio/models/synchronization/__init__.py +3 -0
  262. wbportfolio/models/synchronization/portfolio_synchronization.py +292 -0
  263. wbportfolio/models/synchronization/price_computation.py +200 -0
  264. wbportfolio/models/synchronization/synchronization.py +188 -0
  265. wbportfolio/models/transactions/__init__.py +7 -0
  266. wbportfolio/models/transactions/claim.py +634 -0
  267. wbportfolio/models/transactions/dividends.py +31 -0
  268. wbportfolio/models/transactions/expiry.py +7 -0
  269. wbportfolio/models/transactions/fees.py +153 -0
  270. wbportfolio/models/transactions/trade_proposals.py +502 -0
  271. wbportfolio/models/transactions/trades.py +704 -0
  272. wbportfolio/models/transactions/transactions.py +211 -0
  273. wbportfolio/models/utils.py +12 -0
  274. wbportfolio/permissions.py +13 -0
  275. wbportfolio/pms/__init__.py +0 -0
  276. wbportfolio/pms/statistics/__init__.py +0 -0
  277. wbportfolio/pms/trading/__init__.py +1 -0
  278. wbportfolio/pms/trading/handler.py +164 -0
  279. wbportfolio/pms/typing.py +194 -0
  280. wbportfolio/preferences.py +6 -0
  281. wbportfolio/reports/__init__.py +0 -0
  282. wbportfolio/reports/monthly_position_report.py +74 -0
  283. wbportfolio/risk_management/__init__.py +0 -0
  284. wbportfolio/risk_management/backends/__init__.py +11 -0
  285. wbportfolio/risk_management/backends/accounts.py +166 -0
  286. wbportfolio/risk_management/backends/controversy_portfolio.py +63 -0
  287. wbportfolio/risk_management/backends/exposure_portfolio.py +203 -0
  288. wbportfolio/risk_management/backends/instrument_list_portfolio.py +89 -0
  289. wbportfolio/risk_management/backends/liquidity_risk.py +86 -0
  290. wbportfolio/risk_management/backends/liquidity_stress_instrument.py +86 -0
  291. wbportfolio/risk_management/backends/mixins.py +220 -0
  292. wbportfolio/risk_management/backends/product_integrity.py +111 -0
  293. wbportfolio/risk_management/backends/stop_loss_instrument.py +24 -0
  294. wbportfolio/risk_management/backends/stop_loss_portfolio.py +36 -0
  295. wbportfolio/risk_management/backends/ucits_portfolio.py +63 -0
  296. wbportfolio/risk_management/tests/__init__.py +0 -0
  297. wbportfolio/risk_management/tests/conftest.py +15 -0
  298. wbportfolio/risk_management/tests/test_accounts.py +98 -0
  299. wbportfolio/risk_management/tests/test_controversy_portfolio.py +33 -0
  300. wbportfolio/risk_management/tests/test_exposure_portfolio.py +94 -0
  301. wbportfolio/risk_management/tests/test_instrument_list_portfolio.py +60 -0
  302. wbportfolio/risk_management/tests/test_liquidity_risk.py +47 -0
  303. wbportfolio/risk_management/tests/test_product_integrity.py +55 -0
  304. wbportfolio/risk_management/tests/test_stop_loss_instrument.py +110 -0
  305. wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +119 -0
  306. wbportfolio/risk_management/tests/test_ucits_portfolio.py +39 -0
  307. wbportfolio/serializers/__init__.py +42 -0
  308. wbportfolio/serializers/adjustments.py +24 -0
  309. wbportfolio/serializers/assets.py +166 -0
  310. wbportfolio/serializers/custodians.py +26 -0
  311. wbportfolio/serializers/portfolio_cash_flow.py +48 -0
  312. wbportfolio/serializers/portfolio_cash_targets.py +20 -0
  313. wbportfolio/serializers/portfolio_relationship.py +53 -0
  314. wbportfolio/serializers/portfolio_swing_pricing.py +20 -0
  315. wbportfolio/serializers/portfolios.py +143 -0
  316. wbportfolio/serializers/positions.py +76 -0
  317. wbportfolio/serializers/product_group.py +88 -0
  318. wbportfolio/serializers/products.py +331 -0
  319. wbportfolio/serializers/reconciliations.py +173 -0
  320. wbportfolio/serializers/registers.py +72 -0
  321. wbportfolio/serializers/roles.py +60 -0
  322. wbportfolio/serializers/signals.py +157 -0
  323. wbportfolio/serializers/synchronization.py +18 -0
  324. wbportfolio/serializers/transactions/__init__.py +24 -0
  325. wbportfolio/serializers/transactions/claim.py +310 -0
  326. wbportfolio/serializers/transactions/dividends.py +18 -0
  327. wbportfolio/serializers/transactions/expiry.py +18 -0
  328. wbportfolio/serializers/transactions/fees.py +32 -0
  329. wbportfolio/serializers/transactions/trades.py +315 -0
  330. wbportfolio/serializers/transactions/transactions.py +84 -0
  331. wbportfolio/static/wbportfolio/css/macro_review.css +17 -0
  332. wbportfolio/static/wbportfolio/markdown/documentation/account_holding_reconciliation.md +16 -0
  333. wbportfolio/static/wbportfolio/markdown/documentation/aggregate_asset_position_liquidity.md +25 -0
  334. wbportfolio/static/wbportfolio/markdown/documentation/company.md +78 -0
  335. wbportfolio/static/wbportfolio/markdown/documentation/earnings_instrument.md +14 -0
  336. wbportfolio/static/wbportfolio/markdown/documentation/financial_analysis_instrument_ratios.md +94 -0
  337. wbportfolio/static/wbportfolio/markdown/documentation/financial_statistics.md +44 -0
  338. wbportfolio/static/wbportfolio/markdown/documentation/person.md +70 -0
  339. wbportfolio/tasks.py +125 -0
  340. wbportfolio/templates/portfolio/email/customer_report.html +6 -0
  341. wbportfolio/templates/portfolio/email/customer_trade_notification.html +26 -0
  342. wbportfolio/templates/portfolio/email/email_base_template.html +420 -0
  343. wbportfolio/templates/portfolio/email/rebalancing_report.html +34 -0
  344. wbportfolio/templates/portfolio/macro/macro_review.html +88 -0
  345. wbportfolio/tests/__init__.py +0 -0
  346. wbportfolio/tests/conftest.py +164 -0
  347. wbportfolio/tests/models/__init__.py +0 -0
  348. wbportfolio/tests/models/test_account_reconciliation.py +191 -0
  349. wbportfolio/tests/models/test_assets.py +193 -0
  350. wbportfolio/tests/models/test_custodians.py +12 -0
  351. wbportfolio/tests/models/test_customer_trades.py +113 -0
  352. wbportfolio/tests/models/test_dividends.py +7 -0
  353. wbportfolio/tests/models/test_imports.py +192 -0
  354. wbportfolio/tests/models/test_instrument_mixins.py +48 -0
  355. wbportfolio/tests/models/test_merge.py +133 -0
  356. wbportfolio/tests/models/test_portfolio_cash_flow.py +112 -0
  357. wbportfolio/tests/models/test_portfolio_cash_targets.py +27 -0
  358. wbportfolio/tests/models/test_portfolio_swing_pricings.py +42 -0
  359. wbportfolio/tests/models/test_portfolios.py +676 -0
  360. wbportfolio/tests/models/test_product_groups.py +80 -0
  361. wbportfolio/tests/models/test_products.py +187 -0
  362. wbportfolio/tests/models/test_roles.py +82 -0
  363. wbportfolio/tests/models/test_splits.py +233 -0
  364. wbportfolio/tests/models/test_synchronization.py +617 -0
  365. wbportfolio/tests/models/transactions/__init__.py +0 -0
  366. wbportfolio/tests/models/transactions/test_claim.py +129 -0
  367. wbportfolio/tests/models/transactions/test_fees.py +65 -0
  368. wbportfolio/tests/models/transactions/test_trades.py +204 -0
  369. wbportfolio/tests/models/utils.py +13 -0
  370. wbportfolio/tests/serializers/__init__.py +0 -0
  371. wbportfolio/tests/serializers/test_claims.py +21 -0
  372. wbportfolio/tests/signals.py +151 -0
  373. wbportfolio/tests/tests.py +31 -0
  374. wbportfolio/tests/viewsets/__init__.py +0 -0
  375. wbportfolio/tests/viewsets/test_assets.py +67 -0
  376. wbportfolio/tests/viewsets/test_performances.py +72 -0
  377. wbportfolio/tests/viewsets/test_products.py +92 -0
  378. wbportfolio/tests/viewsets/transactions/__init__.py +0 -0
  379. wbportfolio/tests/viewsets/transactions/test_claims.py +146 -0
  380. wbportfolio/urls.py +247 -0
  381. wbportfolio/utils.py +30 -0
  382. wbportfolio/viewsets/__init__.py +57 -0
  383. wbportfolio/viewsets/adjustments.py +46 -0
  384. wbportfolio/viewsets/assets.py +562 -0
  385. wbportfolio/viewsets/assets_and_net_new_money_progression.py +117 -0
  386. wbportfolio/viewsets/charts/__init__.py +1 -0
  387. wbportfolio/viewsets/charts/assets.py +247 -0
  388. wbportfolio/viewsets/configs/__init__.py +6 -0
  389. wbportfolio/viewsets/configs/buttons/__init__.py +23 -0
  390. wbportfolio/viewsets/configs/buttons/adjustments.py +13 -0
  391. wbportfolio/viewsets/configs/buttons/assets.py +145 -0
  392. wbportfolio/viewsets/configs/buttons/claims.py +83 -0
  393. wbportfolio/viewsets/configs/buttons/custodians.py +76 -0
  394. wbportfolio/viewsets/configs/buttons/fees.py +14 -0
  395. wbportfolio/viewsets/configs/buttons/mixins.py +88 -0
  396. wbportfolio/viewsets/configs/buttons/portfolios.py +115 -0
  397. wbportfolio/viewsets/configs/buttons/products.py +41 -0
  398. wbportfolio/viewsets/configs/buttons/reconciliations.py +65 -0
  399. wbportfolio/viewsets/configs/buttons/registers.py +11 -0
  400. wbportfolio/viewsets/configs/buttons/signals.py +68 -0
  401. wbportfolio/viewsets/configs/buttons/trade_proposals.py +25 -0
  402. wbportfolio/viewsets/configs/buttons/trades.py +144 -0
  403. wbportfolio/viewsets/configs/display/__init__.py +61 -0
  404. wbportfolio/viewsets/configs/display/adjustments.py +81 -0
  405. wbportfolio/viewsets/configs/display/assets.py +265 -0
  406. wbportfolio/viewsets/configs/display/claim.py +299 -0
  407. wbportfolio/viewsets/configs/display/custodians.py +24 -0
  408. wbportfolio/viewsets/configs/display/esg.py +88 -0
  409. wbportfolio/viewsets/configs/display/fees.py +133 -0
  410. wbportfolio/viewsets/configs/display/portfolio_cash_flow.py +103 -0
  411. wbportfolio/viewsets/configs/display/portfolio_relationship.py +38 -0
  412. wbportfolio/viewsets/configs/display/portfolios.py +125 -0
  413. wbportfolio/viewsets/configs/display/positions.py +75 -0
  414. wbportfolio/viewsets/configs/display/product_groups.py +54 -0
  415. wbportfolio/viewsets/configs/display/product_performance.py +241 -0
  416. wbportfolio/viewsets/configs/display/products.py +249 -0
  417. wbportfolio/viewsets/configs/display/reconciliations.py +151 -0
  418. wbportfolio/viewsets/configs/display/registers.py +71 -0
  419. wbportfolio/viewsets/configs/display/roles.py +49 -0
  420. wbportfolio/viewsets/configs/display/trade_proposals.py +97 -0
  421. wbportfolio/viewsets/configs/display/trades.py +359 -0
  422. wbportfolio/viewsets/configs/display/transactions.py +55 -0
  423. wbportfolio/viewsets/configs/endpoints/__init__.py +75 -0
  424. wbportfolio/viewsets/configs/endpoints/adjustments.py +17 -0
  425. wbportfolio/viewsets/configs/endpoints/assets.py +115 -0
  426. wbportfolio/viewsets/configs/endpoints/claim.py +106 -0
  427. wbportfolio/viewsets/configs/endpoints/custodians.py +6 -0
  428. wbportfolio/viewsets/configs/endpoints/esg.py +14 -0
  429. wbportfolio/viewsets/configs/endpoints/fees.py +26 -0
  430. wbportfolio/viewsets/configs/endpoints/portfolio_relationship.py +23 -0
  431. wbportfolio/viewsets/configs/endpoints/portfolios.py +43 -0
  432. wbportfolio/viewsets/configs/endpoints/positions.py +18 -0
  433. wbportfolio/viewsets/configs/endpoints/product_groups.py +11 -0
  434. wbportfolio/viewsets/configs/endpoints/product_performance.py +29 -0
  435. wbportfolio/viewsets/configs/endpoints/products.py +37 -0
  436. wbportfolio/viewsets/configs/endpoints/reconciliations.py +31 -0
  437. wbportfolio/viewsets/configs/endpoints/roles.py +9 -0
  438. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +17 -0
  439. wbportfolio/viewsets/configs/endpoints/trades.py +82 -0
  440. wbportfolio/viewsets/configs/endpoints/transactions.py +17 -0
  441. wbportfolio/viewsets/configs/menu/__init__.py +30 -0
  442. wbportfolio/viewsets/configs/menu/adjustments.py +8 -0
  443. wbportfolio/viewsets/configs/menu/assets.py +8 -0
  444. wbportfolio/viewsets/configs/menu/claim.py +41 -0
  445. wbportfolio/viewsets/configs/menu/custodians.py +11 -0
  446. wbportfolio/viewsets/configs/menu/fees.py +13 -0
  447. wbportfolio/viewsets/configs/menu/instrument_prices.py +10 -0
  448. wbportfolio/viewsets/configs/menu/portfolio_cash_flow.py +8 -0
  449. wbportfolio/viewsets/configs/menu/portfolios.py +15 -0
  450. wbportfolio/viewsets/configs/menu/positions.py +14 -0
  451. wbportfolio/viewsets/configs/menu/product_groups.py +10 -0
  452. wbportfolio/viewsets/configs/menu/product_performance.py +25 -0
  453. wbportfolio/viewsets/configs/menu/products.py +15 -0
  454. wbportfolio/viewsets/configs/menu/reconciliations.py +7 -0
  455. wbportfolio/viewsets/configs/menu/registers.py +10 -0
  456. wbportfolio/viewsets/configs/menu/roles.py +16 -0
  457. wbportfolio/viewsets/configs/menu/trades.py +18 -0
  458. wbportfolio/viewsets/configs/menu/transactions.py +8 -0
  459. wbportfolio/viewsets/configs/previews/__init__.py +1 -0
  460. wbportfolio/viewsets/configs/previews/portfolios.py +21 -0
  461. wbportfolio/viewsets/configs/titles/__init__.py +65 -0
  462. wbportfolio/viewsets/configs/titles/adjustments.py +19 -0
  463. wbportfolio/viewsets/configs/titles/assets.py +57 -0
  464. wbportfolio/viewsets/configs/titles/assets_and_net_new_money_progression.py +6 -0
  465. wbportfolio/viewsets/configs/titles/claim.py +81 -0
  466. wbportfolio/viewsets/configs/titles/custodians.py +12 -0
  467. wbportfolio/viewsets/configs/titles/esg.py +10 -0
  468. wbportfolio/viewsets/configs/titles/fees.py +25 -0
  469. wbportfolio/viewsets/configs/titles/instrument_prices.py +20 -0
  470. wbportfolio/viewsets/configs/titles/portfolios.py +32 -0
  471. wbportfolio/viewsets/configs/titles/positions.py +11 -0
  472. wbportfolio/viewsets/configs/titles/product_groups.py +12 -0
  473. wbportfolio/viewsets/configs/titles/product_performance.py +16 -0
  474. wbportfolio/viewsets/configs/titles/products.py +6 -0
  475. wbportfolio/viewsets/configs/titles/registers.py +12 -0
  476. wbportfolio/viewsets/configs/titles/roles.py +23 -0
  477. wbportfolio/viewsets/configs/titles/trades.py +51 -0
  478. wbportfolio/viewsets/configs/titles/transactions.py +8 -0
  479. wbportfolio/viewsets/custodians.py +66 -0
  480. wbportfolio/viewsets/esg.py +165 -0
  481. wbportfolio/viewsets/mixins.py +48 -0
  482. wbportfolio/viewsets/portfolio_cash_flow.py +31 -0
  483. wbportfolio/viewsets/portfolio_cash_targets.py +8 -0
  484. wbportfolio/viewsets/portfolio_relationship.py +46 -0
  485. wbportfolio/viewsets/portfolio_swing_pricing.py +8 -0
  486. wbportfolio/viewsets/portfolios.py +154 -0
  487. wbportfolio/viewsets/positions.py +292 -0
  488. wbportfolio/viewsets/product_groups.py +84 -0
  489. wbportfolio/viewsets/product_performance.py +646 -0
  490. wbportfolio/viewsets/products.py +529 -0
  491. wbportfolio/viewsets/reconciliations.py +160 -0
  492. wbportfolio/viewsets/registers.py +75 -0
  493. wbportfolio/viewsets/roles.py +44 -0
  494. wbportfolio/viewsets/signals.py +42 -0
  495. wbportfolio/viewsets/synchronization.py +25 -0
  496. wbportfolio/viewsets/transactions/__init__.py +40 -0
  497. wbportfolio/viewsets/transactions/claim.py +933 -0
  498. wbportfolio/viewsets/transactions/fees.py +190 -0
  499. wbportfolio/viewsets/transactions/mixins.py +19 -0
  500. wbportfolio/viewsets/transactions/trade_proposals.py +93 -0
  501. wbportfolio/viewsets/transactions/trades.py +395 -0
  502. wbportfolio/viewsets/transactions/transactions.py +123 -0
  503. wbportfolio-1.43.1.dist-info/METADATA +22 -0
  504. wbportfolio-1.43.1.dist-info/RECORD +506 -0
  505. wbportfolio-1.43.1.dist-info/WHEEL +5 -0
  506. wbportfolio-1.43.1.dist-info/licenses/LICENSE +4 -0
@@ -0,0 +1,248 @@
1
+ from datetime import timedelta
2
+
3
+ from django.db.models import Count, OuterRef, Subquery
4
+ from wbcore import filters as wb_filters
5
+ from wbcrm.models.accounts import Account
6
+ from wbfdm.models import Instrument
7
+ from wbportfolio.models import Product, Trade
8
+ from wbportfolio.models.transactions.claim import Claim
9
+
10
+ from .transactions import TransactionFilterSet
11
+
12
+
13
+ class TradeFilter(TransactionFilterSet):
14
+ transaction_date = wb_filters.DateRangeFilter(
15
+ method=wb_filters.DateRangeFilter.base_date_range_filter_method,
16
+ label="Date Range",
17
+ )
18
+ underlying_instrument = wb_filters.ModelChoiceFilter(
19
+ label="Instrument",
20
+ queryset=Instrument.objects.all(),
21
+ endpoint=Instrument.get_representation_endpoint(),
22
+ value_key=Instrument.get_representation_value_key(),
23
+ label_key=Instrument.get_representation_label_key(),
24
+ filter_params={"is_investable": True},
25
+ )
26
+ only_internal_trade = wb_filters.BooleanFilter(label="Only Internal Trade", method="boolean_only_internal_trade")
27
+
28
+ completely_claimed = wb_filters.BooleanFilter(label="Completely Claimed", method="boolean_completely_claimed")
29
+ completely_claimed_if_approved = wb_filters.BooleanFilter(
30
+ label="Completely Claimed if approved", method="boolean_completely_claimed_if_approved"
31
+ )
32
+
33
+ claimed_shares__gte = wb_filters.NumberFilter(
34
+ label="Claimed Shares", lookup_expr="gte", field_name="claimed_shares"
35
+ )
36
+ claimed_shares__lte = wb_filters.NumberFilter(
37
+ label="Claimed Shares", field_name="claimed_shares", lookup_expr="lte"
38
+ )
39
+ claims = wb_filters.ModelChoiceFilter(
40
+ label="Customer Accounts",
41
+ queryset=Account.objects.all(),
42
+ endpoint=Account.get_representation_endpoint(),
43
+ value_key=Account.get_representation_value_key(),
44
+ label_key=Account.get_representation_label_key(),
45
+ method="filter_customer",
46
+ )
47
+ is_customer_trade = wb_filters.BooleanFilter(label="Is Customer Trade?", method="boolean_is_customer_trade")
48
+ # register = wb_filters.ModelChoiceFilter(
49
+ # label="Register",
50
+ # queryset=Register.objects.all(),
51
+ # endpoint=Register.get_representation_endpoint(),
52
+ # value_key=Register.get_representation_value_key(),
53
+ # label_key = Register.get_representation_label_key(),
54
+ # )
55
+ total_value_usd__gte = total_value_usd__lte = transaction_underlying_type = None
56
+ marked_for_deletion = wb_filters.BooleanFilter(
57
+ label="Marked For Deletion", default=False, field_name="marked_for_deletion", lookup_expr="exact"
58
+ )
59
+ pivot_date = wb_filters.DateFilter(hidden=True, method="filter_pivot_date")
60
+
61
+ def filter_pivot_date(self, queryset, name, value):
62
+ if value:
63
+ return queryset.filter(
64
+ transaction_date__gte=value - timedelta(days=Trade.TRADE_WINDOW_INTERVAL),
65
+ transaction_date__lte=value + timedelta(days=Trade.TRADE_WINDOW_INTERVAL),
66
+ )
67
+ return queryset
68
+
69
+ def filter_customer(self, queryset, name, value):
70
+ if value:
71
+ accounts = value.get_descendants(include_self=True)
72
+ queryset = queryset.annotate(
73
+ filtered_claims=Subquery(
74
+ Claim.objects.filter(trade=OuterRef("pk"), account__in=accounts)
75
+ .values("account")
76
+ .annotate(c=Count("account"))
77
+ .values("c")[:1]
78
+ )
79
+ )
80
+ return queryset.filter(filtered_claims__gt=0)
81
+ return queryset
82
+
83
+ def boolean_is_customer_trade(self, queryset, name, value):
84
+ if value is True:
85
+ return queryset.filter(transaction_subtype__in=[Trade.Type.SUBSCRIPTION, Trade.Type.REDEMPTION])
86
+ elif value is False:
87
+ return queryset.exclude(transaction_subtype__in=[Trade.Type.SUBSCRIPTION, Trade.Type.REDEMPTION])
88
+ else:
89
+ return queryset
90
+
91
+ def boolean_completely_claimed(self, queryset, name, value):
92
+ if value is True:
93
+ return queryset.filter(completely_claimed=True)
94
+ elif value is False:
95
+ return queryset.filter(completely_claimed=False)
96
+ else:
97
+ return queryset
98
+
99
+ def boolean_completely_claimed_if_approved(self, queryset, name, value):
100
+ if value is True:
101
+ return queryset.filter(completely_claimed_if_approved=True)
102
+ elif value is False:
103
+ return queryset.filter(completely_claimed_if_approved=False)
104
+ else:
105
+ return queryset
106
+
107
+ def boolean_only_internal_trade(self, queryset, name, value):
108
+ if value:
109
+ return queryset.exclude(transaction_subtype__in=[Trade.Type.SUBSCRIPTION, Trade.Type.REDEMPTION]).filter(
110
+ underlying_instrument__instrument_type__key="product"
111
+ )
112
+ return queryset
113
+
114
+ class Meta:
115
+ model = Trade
116
+ fields = {
117
+ "shares": ["gte", "lte", "exact"],
118
+ "bank": ["exact", "icontains"],
119
+ "price": ["gte", "lte", "exact"],
120
+ "currency": ["exact"],
121
+ "currency_fx_rate": ["gte", "exact", "lte"],
122
+ "total_value": ["gte", "exact", "lte"],
123
+ "total_value_fx_portfolio": ["gte", "exact", "lte"],
124
+ "comment": ["icontains"],
125
+ "portfolio": ["exact"],
126
+ "register": ["exact"],
127
+ # 'total_value_gross_fx_portfolio': ["gte", "exact", "lte"],
128
+ "transaction_subtype": ["exact"],
129
+ "pending": ["exact"],
130
+ }
131
+
132
+
133
+ class TradePortfolioFilter(TradeFilter):
134
+ portfolio = None
135
+
136
+ class Meta:
137
+ model = Trade
138
+ fields = {
139
+ "shares": ["gte", "lte", "exact"],
140
+ "bank": ["exact", "icontains"],
141
+ "price": ["gte", "lte", "exact"],
142
+ "currency": ["exact"],
143
+ "currency_fx_rate": ["gte", "exact", "lte"],
144
+ "total_value": ["gte", "exact", "lte"],
145
+ "total_value_fx_portfolio": ["gte", "exact", "lte"],
146
+ "comment": ["icontains"],
147
+ "register": ["exact"],
148
+ "transaction_subtype": ["exact"],
149
+ }
150
+
151
+
152
+ class SubscriptionRedemptionFilterSet(TradeFilter):
153
+ is_customer_trade = None
154
+ underlying_instrument = wb_filters.ModelChoiceFilter(
155
+ label="Product",
156
+ queryset=Product.objects.all(),
157
+ endpoint=Product.get_representation_endpoint(),
158
+ value_key=Product.get_representation_value_key(),
159
+ label_key=Product.get_representation_label_key(),
160
+ )
161
+
162
+ opposite_shares = wb_filters.NumberFilter(
163
+ field_name="shares",
164
+ label="Opposite Shares",
165
+ method="filter_opposite_shares",
166
+ lookup_icon="±",
167
+ lookup_label="Opposite",
168
+ )
169
+
170
+ def filter_opposite_shares(self, queryset, name, value):
171
+ return queryset.filter(shares=value * -1)
172
+
173
+ opposite_approximate_shares = wb_filters.NumberFilter(
174
+ field_name="shares",
175
+ label="Opposite Approximite Shares",
176
+ method="filter_opposite_approximate_shares",
177
+ lookup_icon="≈±",
178
+ lookup_label="Opposite Approximate (+- 10%)",
179
+ )
180
+
181
+ def filter_opposite_approximate_shares(self, queryset, name, value):
182
+ value = float(value)
183
+ value_lower_bound = -0.9 * value
184
+ value_higher_bound = -1.1 * value
185
+
186
+ if value > 0:
187
+ return queryset.filter(shares__lte=value_lower_bound, shares__gte=value_higher_bound)
188
+ return queryset.filter(shares__lte=value_higher_bound, shares__gte=value_lower_bound)
189
+
190
+ class Meta:
191
+ model = Trade
192
+ fields = {
193
+ "shares": ["gte", "lte", "exact"],
194
+ "bank": ["exact", "icontains"],
195
+ "price": ["gte", "lte", "exact"],
196
+ "currency": ["exact"],
197
+ "total_value": ["gte", "exact", "lte"],
198
+ "comment": ["icontains"],
199
+ "portfolio": ["exact"],
200
+ "register": ["exact"],
201
+ # 'total_value_gross_fx_portfolio': ["gte", "exact", "lte"],
202
+ "transaction_subtype": ["exact"],
203
+ "marked_for_deletion": ["exact"],
204
+ "custodian": ["exact"],
205
+ "pending": ["exact"],
206
+ }
207
+
208
+
209
+ class TradeInstrumentFilterSet(TradeFilter):
210
+ is_customer_trade = underlying_instrument = completely_claimed = completely_claimed_if_approved = (
211
+ claimed_shares__gte
212
+ ) = claimed_shares__lte = claims = total_value_usd__gte = None
213
+
214
+ class Meta:
215
+ model = Trade
216
+ fields = {
217
+ "shares": ["gte", "lte", "exact"],
218
+ "bank": ["exact", "icontains"],
219
+ "price": ["gte", "lte", "exact"],
220
+ "currency_fx_rate": ["gte", "exact", "lte"],
221
+ "total_value": ["gte", "exact", "lte"],
222
+ "total_value_fx_portfolio": ["gte", "exact", "lte"],
223
+ "total_value_gross": ["gte", "exact", "lte"],
224
+ "total_value_gross_fx_portfolio": ["gte", "exact", "lte"],
225
+ "comment": ["icontains"],
226
+ "portfolio": ["exact"],
227
+ "register": ["exact"],
228
+ "transaction_subtype": ["exact"],
229
+ }
230
+
231
+
232
+ class SubscriptionRedemptionPortfolioFilterSet(TradeFilter):
233
+ is_customer_trade = None
234
+
235
+ class Meta:
236
+ model = Trade
237
+ fields = {
238
+ "shares": ["gte", "lte", "exact"],
239
+ "bank": ["exact", "icontains"],
240
+ "price": ["gte", "lte", "exact"],
241
+ "currency": ["exact"],
242
+ "total_value": ["gte", "exact", "lte"],
243
+ "comment": ["icontains"],
244
+ "register": ["exact"],
245
+ # 'total_value_gross_fx_portfolio': ["gte", "exact", "lte"],
246
+ "transaction_subtype": ["exact"],
247
+ "custodian": ["exact"],
248
+ }
@@ -0,0 +1,98 @@
1
+ from datetime import date, timedelta
2
+
3
+ from psycopg.types.range import DateRange
4
+ from wbcore import filters as wb_filters
5
+ from wbportfolio.models import Fees, Portfolio, Trade, Transaction
6
+
7
+
8
+ def get_transaction_gte_default(field, request, view):
9
+ filter_date = date.today() - timedelta(days=90)
10
+ qs = Transaction.objects.none()
11
+ if "instrument_id" in view.kwargs:
12
+ qs = Transaction.objects.filter(underlying_instrument__id=view.kwargs["instrument_id"])
13
+ elif "portfolio_id" in view.kwargs:
14
+ qs = Transaction.objects.filter(portfolio__id=view.kwargs["portfolio_id"])
15
+ if qs.exists():
16
+ filter_date = qs.earliest("transaction_date").transaction_date
17
+ return filter_date
18
+
19
+
20
+ def get_transaction_underlying_type_choices(*args):
21
+ models = [Fees, Trade]
22
+ choices = []
23
+ for model in models:
24
+ for choice in model.Type.choices:
25
+ choices.append(choice)
26
+ return choices
27
+
28
+
29
+ def get_transaction_lte_default(field, request, view):
30
+ filter_date = date.today() + timedelta(days=7)
31
+ qs = Transaction.objects.none()
32
+ if "instrument_id" in view.kwargs:
33
+ qs = Transaction.objects.filter(underlying_instrument__id=view.kwargs["instrument_id"])
34
+ elif "portfolio_id" in view.kwargs:
35
+ qs = Transaction.objects.filter(portfolio__id=view.kwargs["portfolio_id"])
36
+ if qs.exists():
37
+ filter_date = qs.latest("transaction_date").transaction_date
38
+ return filter_date
39
+
40
+
41
+ def get_transaction_default_date_range(*args, **kwargs):
42
+ return DateRange(get_transaction_gte_default(*args, **kwargs), get_transaction_lte_default(*args, **kwargs))
43
+
44
+
45
+ class TransactionFilterSet(wb_filters.FilterSet):
46
+ transaction_date = wb_filters.DateRangeFilter(
47
+ method=wb_filters.DateRangeFilter.base_date_range_filter_method,
48
+ label="Date Range",
49
+ default=get_transaction_default_date_range,
50
+ )
51
+
52
+ portfolio = wb_filters.ModelChoiceFilter(
53
+ label="Portfolio",
54
+ queryset=Portfolio.objects.all(),
55
+ endpoint=Portfolio.get_representation_endpoint(),
56
+ value_key=Portfolio.get_representation_value_key(),
57
+ label_key=Portfolio.get_representation_label_key(),
58
+ )
59
+ total_value_usd__gte = wb_filters.NumberFilter(
60
+ lookup_expr="gte", field_name="total_value_usd", label="Total Value ($)"
61
+ )
62
+ total_value_usd__lte = wb_filters.NumberFilter(
63
+ lookup_expr="lte",
64
+ label="Total Value ($)",
65
+ field_name="total_value_usd",
66
+ )
67
+
68
+ transaction_underlying_type = wb_filters.ChoiceFilter(
69
+ label="Underlying Type",
70
+ choices=get_transaction_underlying_type_choices(),
71
+ method="filter_transaction_underlying_type",
72
+ )
73
+
74
+ def filter_transaction_underlying_type(self, queryset, name, value):
75
+ if value:
76
+ queryset = queryset.filter(transaction_underlying_type=value)
77
+ return queryset
78
+
79
+ class Meta:
80
+ model = Transaction
81
+ fields = {
82
+ "transaction_type": ["exact"],
83
+ "underlying_instrument": ["exact"],
84
+ "currency": ["exact"],
85
+ "total_value": ["gte", "exact", "lte"],
86
+ "total_value_fx_portfolio": ["gte", "exact", "lte"],
87
+ }
88
+
89
+
90
+ class TransactionPortfolioFilterSet(TransactionFilterSet):
91
+ portfolio = transaction_date__gte = transaction_date__lte = None
92
+ transaction_date = wb_filters.DateFilter(
93
+ label="Transaction Date",
94
+ lookup_expr="exact",
95
+ field_name="transaction_date",
96
+ default=get_transaction_lte_default,
97
+ required=True,
98
+ )
@@ -0,0 +1 @@
1
+ [{"model": "wbreport.colorgradient", "pk": 1, "fields": {"title": "Default Color Palette", "colors": "[\"#ffb166\", \"#d1c554\", \"#8cd66b\", \"#5fd785\", \"#05d6a1\", \"#00c1ab\", \"#01abaa\", \"#38c1d7\", \"#70d6ff\", \"#00b1ff\", \"#0585ff\", \"#2a5df2\", \"#5724d9\", \"#823edf\", \"#a359e5\", \"#e13da9\", \"#ef476f\"]"}}, {"model": "wbreport.reportasset", "pk": 1, "fields": {"key": "font-default", "description": "", "text": "", "asset": "report/assets/Roboto-Light.ttf"}}, {"model": "wbreport.reportasset", "pk": 2, "fields": {"key": "font-bd", "description": "", "text": "", "asset": "report/assets/Roboto-Medium.ttf"}}, {"model": "wbreport.reportasset", "pk": 3, "fields": {"key": "font-it", "description": "", "text": "", "asset": "report/assets/Roboto-LightItalic.ttf"}}, {"model": "wbreport.reportcategory", "pk": 1, "fields": {"title": "Test", "order": 0}}, {"model": "wbreport.reportclass", "pk": 1, "fields": {"title": "Base Product Report Class", "class_path": "wbreport.defaults.factsheets.base"}}, {"model": "wbreport.reportclass", "pk": 2, "fields": {"title": "Multindex Report Class", "class_path": "wbreport.defaults.factsheets.multitheme"}}, {"model": "wbreport.reportclass", "pk": 3, "fields": {"title": "Menu Report Class", "class_path": "wbreport.defaults.factsheets.menu"}}, {"model": "wbreport.report", "pk": 1, "fields": {"content_type": 67, "object_id": 566, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#70D6FF", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Base Index Factsheet", "namespace": "base-index-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 2, "fields": {"content_type": 67, "object_id": 567, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#05D6A1", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Base Product Factsheet", "namespace": "base-product-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 3, "fields": {"content_type": 67, "object_id": 629, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#FFB166", "is_private": false, "mailing_list": null, "report_class": 2, "title": "Multi Indexes Factsheet", "namespace": "multi-indexes", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 4, "fields": {"content_type": 67, "object_id": 613, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#EF476F", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Share class Factsheet", "namespace": "share-class-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 5, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": null, "is_active": true, "file_disabled": false, "base_color": "#FFF000", "is_private": false, "mailing_list": null, "report_class": 3, "title": "Factsheet", "namespace": "factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.reportversion", "pk": 1, "fields": {"uuid": "9ebd8023-7ebb-4389-a484-410c7c4aad2a", "lookup": "august-2021", "title": "August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:15:28.539Z", "update_date": "2021-11-09T13:00:00.665Z", "comment": "", "is_primary": true, "disabled": false, "report": 5}}, {"model": "wbreport.reportversion", "pk": 2, "fields": {"uuid": "8a675145-249e-4183-8f19-5cb023fce8c7", "lookup": "factsheet-august-2021", "title": "Share class - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:18:20.567Z", "update_date": "2021-11-09T13:00:01.418Z", "comment": "", "is_primary": true, "disabled": false, "report": 4}}, {"model": "wbreport.reportversion", "pk": 4, "fields": {"uuid": "8e186d05-5298-4ab3-bc0b-6a81398e622a", "lookup": "factsheet-mutli-index-august-2021", "title": "Mutli Index - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:05.102Z", "update_date": "2021-11-09T13:00:01.962Z", "comment": "", "is_primary": true, "disabled": false, "report": 3}}, {"model": "wbreport.reportversion", "pk": 6, "fields": {"uuid": "825c101e-54ec-4197-ad8a-678bcd6431dd", "lookup": "factsheet-base-product-august-2021", "title": "Base product - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:41.464Z", "update_date": "2021-11-09T13:00:02.271Z", "comment": "", "is_primary": true, "disabled": false, "report": 2}}, {"model": "wbreport.reportversion", "pk": 7, "fields": {"uuid": "5e1014f0-f211-4b81-b272-4d5284d7ef8c", "lookup": "factsheet-base-index-august-2021", "title": "Base Index - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:54.422Z", "update_date": "2021-11-09T13:00:02.616Z", "comment": "", "is_primary": true, "disabled": false, "report": 1}}]
Binary file
File without changes
@@ -0,0 +1,2 @@
1
+ from .ubs import *
2
+ from .wbfdm import *
@@ -0,0 +1,40 @@
1
+ from datetime import datetime
2
+ from io import BytesIO
3
+ from typing import Optional
4
+
5
+ from django.db import models
6
+ from pandas.tseries.offsets import BDay
7
+ from wbcore.contrib.io.backends import AbstractDataBackend, register
8
+ from wbfdm.import_export.backends.refinitiv.utils import Controller
9
+ from wbportfolio.import_export.backends.utils import (
10
+ get_timedelta_import_instrument_price,
11
+ )
12
+
13
+ from ..wbfdm.mixin import DataBackendMixin
14
+
15
+ DEFAULT_MAPPING = {"AX": "factor"}
16
+
17
+
18
+ @register("Adjustment", provider_key="refinitiv")
19
+ class DataBackend(DataBackendMixin, AbstractDataBackend):
20
+ CHUNK_SIZE = 50
21
+
22
+ def __init__(self, import_credential: Optional[models.Model] = None, **kwargs):
23
+ self.controller = Controller(import_credential.username, import_credential.password)
24
+
25
+ def get_files(
26
+ self,
27
+ execution_time: datetime,
28
+ obj_external_ids: list[str] = None,
29
+ **kwargs,
30
+ ) -> BytesIO:
31
+ execution_date = execution_time.date()
32
+ start = kwargs.get("start", (execution_date - BDay(get_timedelta_import_instrument_price())).date())
33
+ fields = list(DEFAULT_MAPPING.keys())
34
+ if obj_external_ids:
35
+ df = self.controller.get_data(obj_external_ids, fields, start, execution_date)
36
+ if not df.empty:
37
+ content_file = BytesIO()
38
+ df.to_json(content_file, orient="records")
39
+ file_name = f"adjustment_chunk-{start:%Y-%m-%d}-{execution_date:%Y-%m-%d}_{datetime.timestamp(execution_time)}.json"
40
+ yield file_name, content_file
@@ -0,0 +1,3 @@
1
+ from .asset_position import *
2
+ from .fees import *
3
+ from .instrument_price import *
@@ -0,0 +1,45 @@
1
+ import json
2
+ from datetime import date, datetime
3
+ from io import BytesIO
4
+ from typing import Optional
5
+
6
+ from django.db import models
7
+ from pandas.tseries.offsets import BDay
8
+ from wbcore.contrib.io.backends import AbstractDataBackend, register
9
+
10
+ from ..utils import process_request
11
+ from .mixin import DataBackendMixin
12
+
13
+
14
+ @register("Asset Position", provider_key="ubs", save_data_in_import_source=True, passive_only=True)
15
+ class DataBackend(DataBackendMixin, AbstractDataBackend):
16
+ def __init__(
17
+ self, import_credential: Optional[models.Model] = None, ubs_bank: Optional[models.Model] = None, **kwargs
18
+ ):
19
+ if not ubs_bank:
20
+ raise ValueError("The ubs company objects needs to be passed to this backend")
21
+ self.ubs_bank = ubs_bank
22
+ if not import_credential or not import_credential.authentication_token:
23
+ raise ValueError("UBS backend needs a valid import credential object")
24
+ self.authentication_token = import_credential.authentication_token
25
+
26
+ def get_files(
27
+ self,
28
+ execution_time: datetime,
29
+ start: date = None,
30
+ obj_external_ids: list[str] = None,
31
+ **kwargs,
32
+ ) -> BytesIO:
33
+ execution_date = (execution_time - BDay(1)).date()
34
+
35
+ endpoint = "https://neo.ubs.com/api/ged-amc/external/report/v1/valuation/{0}/{1}"
36
+ if obj_external_ids:
37
+ for external_id in obj_external_ids:
38
+ res_json = process_request(
39
+ self.authentication_token, endpoint.format(external_id, execution_date.strftime("%Y-%m-%d"))
40
+ )
41
+ if res_json:
42
+ content_file = BytesIO()
43
+ content_file.write(json.dumps(res_json).encode())
44
+ file_name = f"ubs_positions_{external_id}_{execution_date:%Y-%m-%d}_{datetime.timestamp(execution_time)}.json"
45
+ yield file_name, content_file
@@ -0,0 +1,63 @@
1
+ import json
2
+ from datetime import datetime
3
+ from io import BytesIO
4
+ from typing import Optional
5
+
6
+ from django.db import models
7
+ from dynamic_preferences.registries import global_preferences_registry
8
+ from wbcore.contrib.io.backends import AbstractDataBackend, register
9
+
10
+ from ..utils import process_request
11
+ from .mixin import DataBackendMixin
12
+
13
+
14
+ @register("Fees", provider_key="ubs", save_data_in_import_source=True, passive_only=True)
15
+ class DataBackend(DataBackendMixin, AbstractDataBackend):
16
+ def __init__(
17
+ self, import_credential: Optional[models.Model] = None, ubs_bank: Optional[models.Model] = None, **kwargs
18
+ ):
19
+ if not ubs_bank:
20
+ raise ValueError("The ubs company objects needs to be passed to this backend")
21
+ self.ubs_bank = ubs_bank
22
+ if not import_credential or not import_credential.authentication_token:
23
+ raise ValueError("UBS backend needs a valid import credential object")
24
+ self.authentication_token = import_credential.authentication_token
25
+
26
+ def get_files(
27
+ self,
28
+ execution_time: datetime,
29
+ obj_external_ids: list[str] = None,
30
+ **kwargs,
31
+ ) -> BytesIO:
32
+ execution_date = execution_time.date()
33
+
34
+ mngt_fees_endpoint = "https://neo.ubs.com/api/ged-amc/external/fee/v1/management/{0}"
35
+ perf_fees_endpoint = "https://neo.ubs.com/api/ged-amc/external/fee/v1/performance/{0}"
36
+ if obj_external_ids:
37
+ for external_id in obj_external_ids:
38
+ start = kwargs.get("start", None)
39
+ if not start:
40
+ start = global_preferences_registry.manager()["wbfdm__default_start_date_historical_import"]
41
+ mngt_res = process_request(
42
+ self.authentication_token,
43
+ mngt_fees_endpoint.format(external_id),
44
+ {"fromDate": start.strftime("%Y-%m-%d"), "toDate": execution_date.strftime("%Y-%m-%d")},
45
+ )
46
+ perf_res = process_request(
47
+ self.authentication_token,
48
+ perf_fees_endpoint.format(external_id),
49
+ {"fromDate": start.strftime("%Y-%m-%d"), "toDate": execution_date.strftime("%Y-%m-%d")},
50
+ )
51
+
52
+ if mngt_res or perf_res:
53
+ res_json = {
54
+ "performance_fees": perf_res.get("fees", []),
55
+ "management_fees": mngt_res.get("fees", []),
56
+ "isin": external_id,
57
+ }
58
+
59
+ if res_json:
60
+ content_file = BytesIO()
61
+ content_file.write(json.dumps(res_json).encode())
62
+ file_name = f"ubs_fees_{external_id}_{start:%Y-%m-%d}_{execution_date:%Y-%m-%d}_{datetime.timestamp(execution_time)}.json"
63
+ yield file_name, content_file
@@ -0,0 +1,44 @@
1
+ import json
2
+ from datetime import datetime
3
+ from io import BytesIO
4
+ from typing import Optional
5
+
6
+ from django.db import models
7
+ from pandas.tseries.offsets import BDay
8
+ from wbcore.contrib.io.backends import AbstractDataBackend, register
9
+
10
+ from ..utils import process_request
11
+ from .mixin import DataBackendMixin
12
+
13
+
14
+ @register("Instrument Prices", provider_key="ubs", save_data_in_import_source=True, passive_only=True)
15
+ class DataBackend(DataBackendMixin, AbstractDataBackend):
16
+ def __init__(
17
+ self, import_credential: Optional[models.Model] = None, ubs_bank: Optional[models.Model] = None, **kwargs
18
+ ):
19
+ if not ubs_bank:
20
+ raise ValueError("The ubs company objects needs to be passed to this backend")
21
+ self.ubs_bank = ubs_bank
22
+ if not import_credential or not import_credential.authentication_token:
23
+ raise ValueError("UBS backend needs a valid import credential object")
24
+ self.authentication_token = import_credential.authentication_token
25
+
26
+ def get_files(
27
+ self,
28
+ execution_time: datetime,
29
+ obj_external_ids: list[str] = None,
30
+ **kwargs,
31
+ ) -> BytesIO:
32
+ execution_date = (execution_time - BDay(1)).date()
33
+ endpoint = "https://neo.ubs.com/api/ged-amc/external/report/v1/valuation/{0}/{1}"
34
+ if obj_external_ids:
35
+ for external_id in obj_external_ids:
36
+ res_json = process_request(
37
+ self.authentication_token, endpoint.format(external_id, execution_date.strftime("%Y-%m-%d"))
38
+ )
39
+ res_json.pop("constituents", None)
40
+ if res_json:
41
+ content_file = BytesIO()
42
+ content_file.write(json.dumps(res_json).encode())
43
+ file_name = f"ubs_instrument_price_{external_id}_{execution_date:%Y-%m-%d}_{int(datetime.timestamp(execution_time))}.json"
44
+ yield file_name, content_file
@@ -0,0 +1,15 @@
1
+ from datetime import date
2
+
3
+ from django.db import models
4
+ from wbportfolio.models.products import Product
5
+
6
+
7
+ class DataBackendMixin:
8
+ def is_object_valid(self, obj: models.Model) -> bool:
9
+ return super().is_object_valid(obj) and obj.is_active_at_date(date.today()) and obj.isin
10
+
11
+ def get_default_queryset(self):
12
+ return Product.objects.filter(bank=self.ubs_bank, isin__isnull=False)
13
+
14
+ def get_provider_id(self, obj: models.Model) -> str:
15
+ return obj.isin
@@ -0,0 +1,58 @@
1
+ from contextlib import suppress
2
+
3
+ import pandas as pd
4
+ import requests
5
+ from django.db.models import Q
6
+ from dynamic_preferences.registries import global_preferences_registry
7
+ from wbfdm.models import Instrument
8
+
9
+
10
+ def get_timedelta_import_instrument_price():
11
+ return global_preferences_registry.manager()["wbportfolio__timedelta_import_instrument_price"]
12
+
13
+
14
+ def process_request(authentication_token: str, endpoint: str | None = None, kwargs={}) -> pd.DataFrame:
15
+ headers = {"Authorization": authentication_token}
16
+ r = requests.get(endpoint, params=kwargs, headers=headers)
17
+ if r.status_code == requests.codes.ok:
18
+ with suppress(
19
+ requests.exceptions.JSONDecodeError
20
+ ): # we catch any json decode error because the UBS api doesn't seem to respect HTTP status code rule (i.e. returns 200 even though the http content is malformed)
21
+ r_json = r.json()
22
+ if r_json.get("status", "") == "SUCCESS":
23
+ return r_json
24
+ raise ValueError(f"Issue while processing request: {r.content}")
25
+
26
+
27
+ def filter_active_instruments(_date, queryset=None):
28
+ if not queryset:
29
+ queryset = Instrument.objects
30
+ queryset = queryset.filter(Q(delisted_date__isnull=True) | Q(delisted_date__gte=_date))
31
+ queryset = queryset.filter(
32
+ Q(refinitiv_mnemonic_code__isnull=False) | Q(refinitiv_identifier_code__isnull=False) | Q(isin__isnull=False)
33
+ )
34
+ return queryset.distinct()
35
+
36
+
37
+ def chunked_queryset(queryset, chunk_size):
38
+ """Slice a queryset into chunks."""
39
+
40
+ start_pk = 0
41
+ queryset = queryset.order_by("pk")
42
+
43
+ while True:
44
+ # No entry left
45
+ if not queryset.filter(pk__gt=start_pk).exists():
46
+ break
47
+
48
+ try:
49
+ # Fetch chunk_size entries if possible
50
+ end_pk = queryset.filter(pk__gt=start_pk).values_list("pk", flat=True)[chunk_size - 1]
51
+
52
+ # Fetch rest entries if less than chunk_size left
53
+ except IndexError:
54
+ end_pk = queryset.values_list("pk", flat=True).last()
55
+
56
+ yield queryset.filter(pk__gt=start_pk).filter(pk__lte=end_pk)
57
+
58
+ start_pk = end_pk
@@ -0,0 +1,2 @@
1
+ from .adjustment import *
2
+ from .dividend import *