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,40 @@
1
+ import re
2
+ from io import BytesIO
3
+
4
+ import pandas as pd
5
+ from wbfdm.models import Instrument
6
+ from wbportfolio.models import Trade
7
+
8
+ FIELD_MAP = {
9
+ "Trade Date": "book_date",
10
+ "Volume": "total_value",
11
+ "Price": "price",
12
+ "Qty": "shares",
13
+ "Custodian": "bank",
14
+ }
15
+
16
+
17
+ def parse(import_source):
18
+ # for extra safety we ensure that passed file satisfy some regex.
19
+ filename_regex = import_source.source.import_parameters.get(
20
+ "customer_trade_filename_regex",
21
+ ".*([A-Z]{2}[A-Z0-9]{9}[0-9]{1})_TransactionList_([0-9]{4}-[0-9]{2}-[0-9]{2})_[0-9]{2}_[0-9]{2}.*.xlsx",
22
+ )
23
+ if match := re.match(filename_regex, import_source.file.name):
24
+ isin = match.group(1)
25
+ max_date = match.group(2)
26
+ df = pd.read_excel(BytesIO(import_source.file.read()), engine="openpyxl")
27
+ product = Instrument.objects.get(isin=isin)
28
+ if not df.empty:
29
+ df = df.rename(columns=FIELD_MAP)
30
+ df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
31
+ df["transaction_subtype"] = df.shares.apply(
32
+ lambda x: Trade.Type.REDEMPTION if x < 0 else Trade.Type.SUBSCRIPTION
33
+ )
34
+ df["underlying_instrument"] = product.id
35
+ df["book_date"] = pd.to_datetime(df["book_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
36
+ df["portfolio"] = product.primary_portfolio.id
37
+ df = df.dropna()
38
+ data = df.to_dict("records")
39
+ return {"data": data, "history": {"underlying_instrument": product.id, "book_date": max_date}}
40
+ return {"data": {}}
@@ -0,0 +1,34 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ FIELD_MAP = {
5
+ "#AssetIdentifier": "identifier",
6
+ "AssetCurrency": "currency__key",
7
+ "AssetValor": "valoren",
8
+ "ISIN": "isin",
9
+ "SymbolTK": "ticker",
10
+ "OperatingMic": "exchange",
11
+ "AssetType": "instrument_type",
12
+ "AssetText": "name",
13
+ }
14
+
15
+ ASSET_TYPE_MAP = {
16
+ "EQUADR": "equity",
17
+ "EQUBEA": "equity",
18
+ "EQUREG": "equity",
19
+ "FNDETF": "etf",
20
+ "OTHOTH": np.nan,
21
+ "STROTH": "etf",
22
+ "STRPRT": "etf",
23
+ }
24
+
25
+
26
+ def parse(import_source):
27
+ df = pd.read_csv(import_source.file, encoding="utf-16", delimiter=";")
28
+ df = df.rename(columns=FIELD_MAP)
29
+ df = df.replace([np.inf, -np.inf, np.nan], None)
30
+ df["instrument_type"] = df["instrument_type"].apply(lambda x: ASSET_TYPE_MAP[x])
31
+ df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
32
+ df["exchange"] = df["exchange"].apply(lambda x: {"operating_mic_code": x})
33
+ df = df.dropna(subset="instrument_type")
34
+ return {"data": df.to_dict("records")}
@@ -0,0 +1,86 @@
1
+ import re
2
+ from datetime import datetime
3
+
4
+ import pandas as pd
5
+ from wbportfolio.models import FeeProductPercentage, Fees
6
+
7
+ from .utils import get_portfolio_id, get_product
8
+
9
+ FIELD_MAP = {
10
+ "Transaction_Date": "transaction_date",
11
+ "Trade_Date": "book_date",
12
+ "Value_Date": "value_date",
13
+ "Quantity": "total_value",
14
+ "Currency": "currency__key",
15
+ "Portfolio_Identifier": "linked_product",
16
+ "Booking_Comment": "comment",
17
+ }
18
+
19
+
20
+ def parse(import_source):
21
+ df = pd.read_csv(import_source.file, encoding="utf-16", delimiter=";")
22
+ df = df.loc[(df["Order_Type"] == "xfermon_client") & (df["Bookkind"] == "forex_impl_macc"), :]
23
+ date_range_regex = import_source.source.import_parameters.get(
24
+ "date_range_regex", r"\((\b\d{2}\.\d{2}\.\d{4}\b) to (\b\d{2}\.\d{2}\.\d{4}\b)\)"
25
+ )
26
+ data = []
27
+ if not df.empty:
28
+ df = df.rename(columns=FIELD_MAP)
29
+ df.linked_product = df.linked_product.apply(lambda x: get_product(x))
30
+ df["transaction_date"] = pd.to_datetime(df["transaction_date"], dayfirst=True)
31
+ df["book_date"] = pd.to_datetime(df["book_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
32
+ df["value_date"] = pd.to_datetime(df["value_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
33
+ df["portfolio"] = df.linked_product.apply(lambda x: get_portfolio_id(x))
34
+ df["base_management_fees"] = df.apply(
35
+ lambda row: float(
36
+ row["linked_product"].get_fees_percent(row["transaction_date"], FeeProductPercentage.Type.MANAGEMENT)
37
+ ),
38
+ axis=1,
39
+ )
40
+ df["base_bank_fees"] = df.apply(
41
+ lambda row: float(
42
+ row["linked_product"].get_fees_percent(row["transaction_date"], FeeProductPercentage.Type.BANK)
43
+ ),
44
+ axis=1,
45
+ )
46
+ df.total_value = -df.total_value
47
+
48
+ df_management = df.copy()
49
+ df_management["transaction_subtype"] = Fees.Type.MANAGEMENT
50
+ df_management["total_value"] = (
51
+ df_management["total_value"]
52
+ * df_management["base_management_fees"]
53
+ / (df_management["base_management_fees"] + df_management["base_bank_fees"])
54
+ )
55
+
56
+ df_bank = df.copy()
57
+ df_bank["transaction_subtype"] = Fees.Type.ISSUER
58
+ df_bank["total_value"] = (
59
+ df_bank["total_value"]
60
+ * df_bank["base_bank_fees"]
61
+ / (df_bank["base_management_fees"] + df_bank["base_bank_fees"])
62
+ )
63
+
64
+ df = pd.concat([df_management, df_bank], axis=0)
65
+ df = df.drop(columns=df.columns.difference(["transaction_subtype", *FIELD_MAP.values()]))
66
+
67
+ df["linked_product"] = df.linked_product.apply(lambda x: x.id)
68
+ df = df.dropna(subset=["linked_product"])
69
+
70
+ for row in df.to_dict("records"):
71
+ # The fee are sometime aggregated. The aggregate date range information is given in the comment. We therefore try to extract it and duplicate the averaged fees
72
+ if match := re.search(date_range_regex, row["comment"]):
73
+ from_date = datetime.strptime(match.group(1), "%d.%m.%Y").date()
74
+ to_date = datetime.strptime(match.group(2), "%d.%m.%Y").date()
75
+ else:
76
+ from_date = (row["transaction_date"] - pd.tseries.offsets.BDay(1)).date()
77
+ to_date = row["transaction_date"]
78
+
79
+ dates = pd.date_range(from_date, to_date, freq="B", inclusive="right")
80
+ for ts in pd.date_range(from_date, to_date, freq="B", inclusive="right"):
81
+ row_copy = row.copy()
82
+ row_copy["transaction_date"] = ts.strftime("%Y-%m-%d")
83
+ row_copy["total_value"] = row["total_value"] / len(dates)
84
+ data.append(row_copy)
85
+
86
+ return {"data": data}
@@ -0,0 +1,35 @@
1
+ import pandas as pd
2
+ from wbportfolio.models import Fees
3
+
4
+ from .utils import get_perf_fee_isin, get_portfolio_id, get_product
5
+
6
+ FIELD_MAP = {
7
+ "LastPriceDate": "transaction_date",
8
+ "EndOfDay": "value_date",
9
+ "AccrValueInstrCcy": "total_value",
10
+ "FxRate": "currency_fx_rate",
11
+ "TradingCurrency": "currency__key",
12
+ "#ClientName": "linked_product",
13
+ "InstrumentName": "comment",
14
+ }
15
+
16
+ PERF_FEES_INSTRUMENT_ISIN = "CH0040602242"
17
+
18
+
19
+ def parse(import_source):
20
+ perf_fee_isin = get_perf_fee_isin(import_source.source)
21
+ df = pd.read_csv(import_source.file, encoding="utf-16", delimiter=";")
22
+ df = df.loc[df["ISIN"] == perf_fee_isin, :]
23
+ data = []
24
+ if not df.empty:
25
+ df = df.rename(columns=FIELD_MAP)
26
+ df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
27
+ df.total_value = -df.total_value
28
+ df["transaction_subtype"] = Fees.Type.MANAGEMENT
29
+ df["transaction_date"] = pd.to_datetime(df["transaction_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
30
+ df["value_date"] = pd.to_datetime(df["value_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
31
+ df = df.dropna(subset=["linked_product"])
32
+ df["portfolio"] = df.linked_product.apply(lambda x: get_portfolio_id(x))
33
+ df.linked_product = df.linked_product.apply(lambda x: get_product(x).id)
34
+ data = df.to_dict("records")
35
+ return {"data": data}
@@ -0,0 +1,38 @@
1
+ import pandas as pd
2
+ from wbportfolio.models import Trade
3
+
4
+ from .utils import get_portfolio_id
5
+
6
+ FIELD_MAP = {
7
+ "Transaction_Date": "transaction_date",
8
+ "Trade_Date": "book_date",
9
+ "Value_Date": "value_date",
10
+ "Price": "price",
11
+ "Quantity": "shares",
12
+ "Currency": "currency__key",
13
+ "Isin": "underlying_instrument__isin",
14
+ "Asset_Text": "underlying_instrument__name",
15
+ "Portfolio_Identifier": "portfolio",
16
+ "Booking_comment": "comment",
17
+ }
18
+
19
+
20
+ def parse(import_source):
21
+ df = pd.read_csv(import_source.file, encoding="utf-16", delimiter=";")
22
+ df = df.loc[(df["Order_Type"] == "stex_buy_secondary") & (df["Bookkind"] == "trade_asset"), :]
23
+ data = []
24
+ if not df.empty:
25
+ df = df.rename(columns=FIELD_MAP)
26
+ df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
27
+ df.shares = -df.shares
28
+ df["transaction_date"] = pd.to_datetime(df["transaction_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
29
+ df["book_date"] = pd.to_datetime(df["book_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
30
+ df["value_date"] = pd.to_datetime(df["value_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
31
+ df["transaction_subtype"] = df.shares.apply(lambda x: Trade.Type.SELL if x < 0 else Trade.Type.BUY)
32
+ df["portfolio"] = df["portfolio"].apply(lambda x: get_portfolio_id(x))
33
+ df = df.dropna(subset=["underlying_instrument__isin"])
34
+
35
+ df["underlying_instrument__currency__key"] = df["currency__key"]
36
+ df["bank"] = "N/A"
37
+ data = df.to_dict("records")
38
+ return {"data": data}
@@ -0,0 +1,17 @@
1
+ from wbportfolio.models import Product
2
+
3
+
4
+ def get_product(identifier):
5
+ return Product.objects.filter(identifier=identifier).first()
6
+
7
+
8
+ def get_portfolio_id(identifier):
9
+ if (product := get_product(identifier)) and (portfolio := product.primary_portfolio):
10
+ return portfolio.id
11
+
12
+
13
+ def get_perf_fee_isin(source):
14
+ default_perf_fee_isin = "CH0040602242"
15
+ if source:
16
+ return source.import_parameters.get("performance_fees_instrument_isin", default_perf_fee_isin)
17
+ return default_perf_fee_isin
@@ -0,0 +1,29 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ FIELD_MAP = {"Date": "date", "Quantity": "outstanding_shares", "NAV": "net_value"}
5
+
6
+
7
+ def parse(import_source):
8
+ base_filename = import_source.source.import_parameters.get("valuation_base_filename", "StrategicCertificate")
9
+ data = []
10
+ if base_filename in import_source.file.name:
11
+ df = pd.read_excel(import_source.file, engine="openpyxl", sheet_name=0)
12
+ xx, yy = np.where(df == "Date")
13
+ product_ticker = list(filter(lambda x: "Unnamed" not in x, df.columns))
14
+ df = df.iloc[xx[0] :, yy[0] :]
15
+
16
+ df = df.rename(columns=df.iloc[0]).drop(df.index[0]).dropna(how="all")
17
+ df = df.rename(columns=FIELD_MAP)
18
+ df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
19
+ df["date"] = pd.to_datetime(df["date"]).dt.strftime("%Y-%m-%d")
20
+ df["outstanding_shares"] = -df["outstanding_shares"]
21
+
22
+ if len(product_ticker) == 0:
23
+ for k, v in import_source.source.import_parameter.items():
24
+ df[k] = v
25
+ else:
26
+ df["instrument__ticker"] = product_ticker[0]
27
+ df = df.replace([np.inf, -np.inf, np.nan], None)
28
+ data = df.to_dict("records")
29
+ return {"data": data}
File without changes
@@ -0,0 +1,68 @@
1
+ from import_export import fields
2
+ from import_export.widgets import DecimalWidget, ForeignKeyWidget
3
+ from wbcore.contrib.currency.models import Currency
4
+ from wbcore.contrib.io.resources import ViewResource
5
+ from wbfdm.models import Exchange
6
+ from wbportfolio.models import AssetPosition
7
+
8
+
9
+ class AssetPositionResource(ViewResource):
10
+ """
11
+ The resource to download AssetPositions
12
+ """
13
+
14
+ exchange = fields.Field(
15
+ column_name="exchange",
16
+ attribute="exchange",
17
+ widget=ForeignKeyWidget(Exchange, field="name"),
18
+ )
19
+
20
+ price = fields.Field(column_name="price", attribute="price", widget=DecimalWidget())
21
+ shares = fields.Field(column_name="shares", attribute="shares", widget=DecimalWidget())
22
+ total_value = fields.Field(column_name="total_value", attribute="total_value", widget=DecimalWidget())
23
+ currency_fx_rate = fields.Field(
24
+ column_name="currency_fx_rate", attribute="currency_fx_rate", widget=DecimalWidget()
25
+ )
26
+ total_value_fx_portfolio = fields.Field(
27
+ column_name="total_value_fx_portfolio", attribute="total_value_fx_portfolio", widget=DecimalWidget()
28
+ )
29
+ total_value_fx_usd = fields.Field(
30
+ column_name="total_value_fx_portfolio", attribute="total_value_fx_portfolio", widget=DecimalWidget()
31
+ )
32
+
33
+ currency = fields.Field(
34
+ column_name="currency",
35
+ attribute="currency",
36
+ widget=ForeignKeyWidget(Currency, field="key"),
37
+ )
38
+
39
+ class Meta:
40
+ import_id_fields = ("id",)
41
+ fields = (
42
+ "date",
43
+ "exchange",
44
+ "price",
45
+ "shares",
46
+ "total_value",
47
+ "currency",
48
+ "currency_fx_rate",
49
+ "total_value_fx_portfolio",
50
+ "total_value_fx_usd",
51
+ "weighting",
52
+ )
53
+ export_order = (
54
+ "date",
55
+ "underlying_instrument_name",
56
+ "underlying_instrument_ticker",
57
+ "underlying_instrument_isin",
58
+ "exchange",
59
+ "price",
60
+ "shares",
61
+ "total_value",
62
+ "currency",
63
+ "currency_fx_rate",
64
+ "total_value_fx_portfolio",
65
+ "total_value_fx_usd",
66
+ "weighting",
67
+ )
68
+ model = AssetPosition
@@ -0,0 +1,41 @@
1
+ import rstr
2
+ from faker import Faker
3
+ from import_export import fields
4
+ from import_export.widgets import ForeignKeyWidget
5
+ from wbcore.contrib.io.resources import FilterModelResource
6
+ from wbfdm.models import Instrument
7
+ from wbportfolio.models import Trade
8
+
9
+ fake = Faker()
10
+
11
+
12
+ class TradeProposalTradeResource(FilterModelResource):
13
+ """
14
+ Trade Resource class to use to import trade from the trade proposal
15
+ """
16
+
17
+ DUMMY_FIELD_MAP = {
18
+ "underlying_instrument": lambda: rstr.xeger("([A-Z]{2}[A-Z0-9]{9}[0-9]{1})"),
19
+ "weighting": 1.0,
20
+ "shares": 1000.2536,
21
+ "comment": lambda: fake.sentence(),
22
+ "order": 1,
23
+ }
24
+ underlying_instrument = fields.Field(
25
+ column_name="underlying_instrument",
26
+ attribute="underlying_instrument",
27
+ widget=ForeignKeyWidget(Instrument, field="isin"),
28
+ )
29
+
30
+ class Meta:
31
+ import_id_fields = ("id",)
32
+ fields = (
33
+ "id",
34
+ "underlying_instrument",
35
+ "weighting",
36
+ "shares",
37
+ "comment",
38
+ "order",
39
+ )
40
+ export_order = fields
41
+ model = Trade
@@ -0,0 +1,42 @@
1
+ import os
2
+
3
+ import dateparser
4
+ from xlrd.xldate import xldate_as_datetime
5
+
6
+
7
+ def get_fields_name(model):
8
+ return [field.name for field in model._meta.get_fields()]
9
+
10
+
11
+ def get_file_extension(file):
12
+ name, extension = os.path.splitext(file.name)
13
+ return extension.lower()
14
+
15
+
16
+ def convert_string_to_number(string):
17
+ if string == "nan" or not string:
18
+ return 0.0
19
+ if type(string) in (int, float):
20
+ return float(string)
21
+ try:
22
+ return float(string.replace(" ", "").replace(",", ""))
23
+ except ValueError:
24
+ return 0.0
25
+
26
+
27
+ def parse_date(date, formats=[]):
28
+ if isinstance(date, int) or isinstance(date, float):
29
+ return xldate_as_datetime(int(date), 0).date()
30
+ if isinstance(date, str):
31
+ if format:
32
+ return dateparser.parse(date, date_formats=formats).date()
33
+ else:
34
+ return dateparser.parse(date).date()
35
+
36
+
37
+ def extract_exchange_ticker(bbg_ticker):
38
+ try:
39
+ ticker, exchange = bbg_ticker.split(" ")[0:2]
40
+ except Exception:
41
+ ticker, exchange = bbg_ticker, None
42
+ return ticker, exchange
@@ -0,0 +1,119 @@
1
+ -- CTE for prices that fetch prices per product converted to USD
2
+ WITH prices AS (
3
+ SELECT
4
+ price.date AS date,
5
+ price.instrument_id AS instrument_id,
6
+ price.net_value AS net_value,
7
+ price.net_value * (1 / fx.value) AS net_value_usd
8
+ FROM wbfdm_instrumentprice AS price
9
+ JOIN currency_currencyfxrates AS fx ON price.currency_fx_rate_to_usd_id = fx.id
10
+ WHERE price.calculated = FALSE
11
+ ),
12
+ -- CTE for claims, that fetches approved claims
13
+ claims AS (
14
+ SELECT
15
+ claim.shares,
16
+ claim.date,
17
+ claim.product_id,
18
+ account.tree_id AS tree_id,
19
+ trade.marked_as_internal AS marked_as_internal
20
+ FROM wbportfolio_claim AS claim
21
+ JOIN wbcrm_account AS account
22
+ ON claim.account_id = account.id
23
+ JOIN wbportfolio_trade AS trade
24
+ ON claim.trade_id = trade.transaction_ptr_id
25
+ WHERE claim.status = 'APPROVED'
26
+ ),
27
+ -- CTE for claims, that fetches them with their shares and price at transaction date
28
+ claims_with_nav AS (
29
+ SELECT
30
+ claims.date,
31
+ claims.shares,
32
+ claims.product_id,
33
+ nav.net_value_usd,
34
+ accounts.tree_id AS tree_id,
35
+ trade.marked_as_internal as marked_as_internal
36
+ FROM wbportfolio_claim AS claims
37
+ JOIN wbportfolio_trade AS trade
38
+ ON claims.trade_id = trade.transaction_ptr_id
39
+ LEFT JOIN wbcrm_account AS accounts
40
+ ON claims.account_id = accounts.id
41
+ LEFT JOIN LATERAL (
42
+ SELECT
43
+ price.net_value * (1 / cc.value) AS net_value_usd
44
+ FROM wbfdm_instrumentprice AS price
45
+ LEFT JOIN currency_currencyfxrates AS cc
46
+ ON cc.id = price.currency_fx_rate_to_usd_id
47
+ WHERE
48
+ price.instrument_id = claims.product_id
49
+ AND price.date <= claims.date
50
+ AND claims.status = 'APPROVED'
51
+ AND claims.account_id IS NOT NULL
52
+ AND price.calculated = FALSE
53
+ ORDER BY price.date DESC
54
+ LIMIT 1
55
+ ) AS nav ON TRUE
56
+ -- CTE to generate a series with year and month already extracted
57
+ ), series AS (
58
+ SELECT
59
+ (DATE_TRUNC('month', dts) + INTERVAL '1 month - 1 day')::DATE AS val_date,
60
+ EXTRACT(YEAR FROM dts) AS year,
61
+ EXTRACT(MONTH FROM dts) AS month
62
+ FROM GENERATE_SERIES('{{ from_date | identifier }}'::DATE, '{{ to_date | identifier }}'::DATE, '1 month'::INTERVAL) AS dts
63
+ )
64
+
65
+ SELECT
66
+ series.year AS year,
67
+ series.month AS month,
68
+ SUM(NAV.net_value_usd * HOLDINGS.sum_shares) AS total_assets,
69
+ SUM(COALESCE(NNM.nnm_usd, 0)) AS net_new_money
70
+
71
+ FROM series
72
+ -- Cartesian product with products, to get each item of the series
73
+ -- for each product
74
+ CROSS JOIN wbportfolio_product AS product
75
+
76
+ -- If there is not product_id it means we filter for all products
77
+ -- This means we have to filter out all products that are not
78
+ -- invested to avoid double accounting
79
+
80
+ -- Foreach product/date combination, get the last NAV date available
81
+ LEFT JOIN LATERAL (
82
+ SELECT prices.net_value_usd
83
+ FROM prices
84
+ WHERE
85
+ prices.date <= series.val_date
86
+ AND prices.instrument_id = product.instrument_ptr_id
87
+ ORDER BY prices.date DESC
88
+ LIMIT 1
89
+ ) AS NAV ON TRUE
90
+ -- For each product/date combination, get the number of shares
91
+ LEFT JOIN LATERAL (
92
+ SELECT SUM(claims.shares) AS sum_shares
93
+ FROM claims
94
+ WHERE claims.date <= series.val_date
95
+ AND claims.product_id = product.instrument_ptr_id
96
+ {% if account_tree_id %}AND claims.tree_id={{ account_tree_id }}{% endif %}
97
+ {% if not product_id %}AND claims.marked_as_internal=False{% endif %}
98
+ ) AS HOLDINGS ON TRUE
99
+ -- For each product/date combination, get the NNM
100
+ LEFT JOIN LATERAL (
101
+ -- Get the number of shares that were created in the month of date (from the series)
102
+ SELECT
103
+ SUM(claims.shares * claims.net_value_usd) AS nnm_usd,
104
+ SUM(claims.shares) as shares
105
+ FROM claims_with_nav as claims
106
+ WHERE
107
+ EXTRACT(YEAR FROM claims.date) = series.year
108
+ AND EXTRACT(MONTH FROM claims.date) = series.month
109
+ AND claims.product_id = product.instrument_ptr_id
110
+ {% if account_tree_id %}AND claims.tree_id={{ account_tree_id }}{% endif %}
111
+ {% if not product_id %}AND claims.marked_as_internal=False{% endif %}
112
+ ) AS NNM ON TRUE
113
+
114
+ -- if we pass in the product_id, we only display the data for this particular product
115
+ {% if product_id %}
116
+ WHERE product.instrument_ptr_id = {{ product_id }}
117
+ {% endif %}
118
+
119
+ GROUP BY series.year, series.month