wbportfolio 1.52.0__py2.py3-none-any.whl → 1.59.4__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 (273) hide show
  1. wbportfolio/admin/__init__.py +3 -1
  2. wbportfolio/admin/indexes.py +1 -1
  3. wbportfolio/admin/orders/__init__.py +2 -0
  4. wbportfolio/admin/orders/order_proposals.py +16 -0
  5. wbportfolio/admin/orders/orders.py +32 -0
  6. wbportfolio/admin/portfolio.py +11 -5
  7. wbportfolio/admin/product_groups.py +1 -1
  8. wbportfolio/admin/products.py +2 -1
  9. wbportfolio/admin/{transactions/rebalancing.py → rebalancing.py} +1 -1
  10. wbportfolio/admin/transactions/__init__.py +0 -2
  11. wbportfolio/admin/transactions/dividends.py +40 -4
  12. wbportfolio/admin/transactions/fees.py +24 -14
  13. wbportfolio/admin/transactions/trades.py +34 -27
  14. wbportfolio/analysis/claims.py +5 -6
  15. wbportfolio/api_clients/ubs.py +162 -0
  16. wbportfolio/constants.py +1 -0
  17. wbportfolio/contrib/company_portfolio/configs/display.py +22 -10
  18. wbportfolio/contrib/company_portfolio/configs/previews.py +3 -3
  19. wbportfolio/contrib/company_portfolio/filters.py +10 -10
  20. wbportfolio/contrib/company_portfolio/models.py +69 -39
  21. wbportfolio/contrib/company_portfolio/scripts.py +7 -2
  22. wbportfolio/contrib/company_portfolio/serializers.py +32 -22
  23. wbportfolio/contrib/company_portfolio/tasks.py +12 -1
  24. wbportfolio/contrib/company_portfolio/tests/conftest.py +2 -2
  25. wbportfolio/defaults/fees/default.py +7 -15
  26. wbportfolio/factories/__init__.py +2 -2
  27. wbportfolio/factories/assets.py +1 -1
  28. wbportfolio/factories/dividends.py +8 -3
  29. wbportfolio/factories/fees.py +8 -4
  30. wbportfolio/factories/orders/__init__.py +2 -0
  31. wbportfolio/factories/orders/order_proposals.py +21 -0
  32. wbportfolio/factories/orders/orders.py +34 -0
  33. wbportfolio/factories/portfolios.py +2 -1
  34. wbportfolio/factories/product_groups.py +3 -3
  35. wbportfolio/factories/products.py +3 -3
  36. wbportfolio/factories/rebalancing.py +1 -1
  37. wbportfolio/factories/trades.py +12 -16
  38. wbportfolio/filters/assets.py +18 -4
  39. wbportfolio/filters/orders/__init__.py +2 -0
  40. wbportfolio/filters/orders/order_proposals.py +55 -0
  41. wbportfolio/filters/orders/orders.py +11 -0
  42. wbportfolio/filters/portfolios.py +38 -1
  43. wbportfolio/filters/positions.py +0 -1
  44. wbportfolio/filters/transactions/__init__.py +1 -2
  45. wbportfolio/filters/transactions/fees.py +5 -12
  46. wbportfolio/filters/transactions/trades.py +16 -8
  47. wbportfolio/filters/transactions/utils.py +42 -0
  48. wbportfolio/import_export/backends/ubs/__init__.py +1 -0
  49. wbportfolio/import_export/backends/ubs/asset_position.py +6 -7
  50. wbportfolio/import_export/backends/ubs/fees.py +10 -20
  51. wbportfolio/import_export/backends/ubs/instrument_price.py +6 -6
  52. wbportfolio/import_export/backends/ubs/trade.py +48 -0
  53. wbportfolio/import_export/backends/utils.py +0 -17
  54. wbportfolio/import_export/handlers/asset_position.py +22 -10
  55. wbportfolio/import_export/handlers/dividend.py +8 -8
  56. wbportfolio/import_export/handlers/fees.py +13 -23
  57. wbportfolio/import_export/handlers/orders.py +71 -0
  58. wbportfolio/import_export/handlers/trade.py +53 -77
  59. wbportfolio/import_export/parsers/default_mapping.py +1 -1
  60. wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +2 -2
  61. wbportfolio/import_export/parsers/jpmorgan/fees.py +4 -4
  62. wbportfolio/import_export/parsers/jpmorgan/strategy.py +59 -85
  63. wbportfolio/import_export/parsers/jpmorgan/valuation.py +2 -2
  64. wbportfolio/import_export/parsers/leonteq/customer_trade.py +5 -5
  65. wbportfolio/import_export/parsers/leonteq/fees.py +11 -7
  66. wbportfolio/import_export/parsers/leonteq/trade.py +2 -6
  67. wbportfolio/import_export/parsers/natixis/d1_fees.py +2 -2
  68. wbportfolio/import_export/parsers/natixis/dividend.py +4 -9
  69. wbportfolio/import_export/parsers/natixis/equity.py +22 -4
  70. wbportfolio/import_export/parsers/natixis/fees.py +7 -9
  71. wbportfolio/import_export/parsers/natixis/utils.py +13 -19
  72. wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +1 -1
  73. wbportfolio/import_export/parsers/sg_lux/equity.py +10 -10
  74. wbportfolio/import_export/parsers/sg_lux/fees.py +2 -2
  75. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +2 -2
  76. wbportfolio/import_export/parsers/sg_lux/sylk.py +12 -11
  77. wbportfolio/import_export/parsers/sg_lux/utils.py +2 -2
  78. wbportfolio/import_export/parsers/sg_lux/valuation.py +4 -2
  79. wbportfolio/import_export/parsers/societe_generale/strategy.py +5 -5
  80. wbportfolio/import_export/parsers/tellco/customer_trade.py +2 -1
  81. wbportfolio/import_export/parsers/tellco/valuation.py +4 -3
  82. wbportfolio/import_export/parsers/ubs/api/fees.py +2 -2
  83. wbportfolio/import_export/parsers/ubs/api/trade.py +39 -0
  84. wbportfolio/import_export/parsers/ubs/customer_trade.py +7 -5
  85. wbportfolio/import_export/parsers/ubs/equity.py +3 -2
  86. wbportfolio/import_export/parsers/ubs/valuation.py +2 -1
  87. wbportfolio/import_export/parsers/vontobel/customer_trade.py +2 -3
  88. wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +0 -1
  89. wbportfolio/import_export/parsers/vontobel/management_fees.py +12 -20
  90. wbportfolio/import_export/parsers/vontobel/performance_fees.py +5 -8
  91. wbportfolio/import_export/parsers/vontobel/valuation_api.py +4 -1
  92. wbportfolio/import_export/resources/trades.py +3 -3
  93. wbportfolio/import_export/utils.py +3 -1
  94. wbportfolio/jinja2/wbportfolio/sql/aum_nnm.sql +2 -2
  95. wbportfolio/metric/backends/base.py +2 -2
  96. wbportfolio/migrations/0059_fees_unique_fees.py +1 -1
  97. wbportfolio/migrations/0077_remove_transaction_currency_and_more.py +622 -0
  98. wbportfolio/migrations/0078_trade_drift_factor.py +26 -0
  99. wbportfolio/migrations/0079_alter_trade_drift_factor.py +19 -0
  100. wbportfolio/migrations/0080_alter_trade_drift_factor_alter_trade_weighting.py +19 -0
  101. wbportfolio/migrations/0081_alter_trade_drift_factor.py +19 -0
  102. wbportfolio/migrations/0082_remove_tradeproposal_creator_and_more.py +93 -0
  103. wbportfolio/migrations/0083_order_alter_trade_options_and_more.py +181 -0
  104. wbportfolio/migrations/0084_orderproposal_min_order_value.py +25 -0
  105. wbportfolio/migrations/0085_order_desired_target_weight.py +26 -0
  106. wbportfolio/migrations/0086_orderproposal_total_cash_weight.py +19 -0
  107. wbportfolio/migrations/0087_product_order_routing_custodian_adapter.py +94 -0
  108. wbportfolio/migrations/0088_orderproposal_total_effective_portfolio_contribution.py +19 -0
  109. wbportfolio/migrations/0089_orderproposal_min_weighting.py +71 -0
  110. wbportfolio/migrations/0090_dividendtransaction_price_fx_portfolio_and_more.py +44 -0
  111. wbportfolio/migrations/0091_remove_order_execution_confirmed_and_more.py +32 -0
  112. wbportfolio/migrations/0092_order_quantization_error_alter_orderproposal_status.py +49 -0
  113. wbportfolio/migrations/0093_remove_portfolioportfoliothroughmodel_unique_primary_and_more.py +35 -0
  114. wbportfolio/models/__init__.py +2 -0
  115. wbportfolio/models/adjustments.py +1 -1
  116. wbportfolio/models/asset.py +28 -170
  117. wbportfolio/models/builder.py +323 -0
  118. wbportfolio/models/custodians.py +3 -3
  119. wbportfolio/models/exceptions.py +1 -1
  120. wbportfolio/models/graphs/portfolio.py +1 -1
  121. wbportfolio/models/graphs/utils.py +11 -11
  122. wbportfolio/models/mixins/instruments.py +7 -0
  123. wbportfolio/models/mixins/liquidity_stress_test.py +4 -4
  124. wbportfolio/models/orders/__init__.py +2 -0
  125. wbportfolio/models/orders/order_proposals.py +1414 -0
  126. wbportfolio/models/orders/orders.py +410 -0
  127. wbportfolio/models/portfolio.py +311 -289
  128. wbportfolio/models/portfolio_relationship.py +6 -0
  129. wbportfolio/models/products.py +12 -0
  130. wbportfolio/models/{transactions/rebalancing.py → rebalancing.py} +40 -27
  131. wbportfolio/models/roles.py +4 -10
  132. wbportfolio/models/transactions/__init__.py +0 -4
  133. wbportfolio/models/transactions/claim.py +7 -6
  134. wbportfolio/models/transactions/dividends.py +42 -5
  135. wbportfolio/models/transactions/fees.py +55 -22
  136. wbportfolio/models/transactions/trades.py +121 -442
  137. wbportfolio/models/transactions/transactions.py +78 -158
  138. wbportfolio/models/utils.py +100 -1
  139. wbportfolio/order_routing/__init__.py +35 -0
  140. wbportfolio/order_routing/adapters/__init__.py +65 -0
  141. wbportfolio/order_routing/adapters/ubs.py +195 -0
  142. wbportfolio/order_routing/router.py +33 -0
  143. wbportfolio/order_routing/tests/__init__.py +0 -0
  144. wbportfolio/order_routing/tests/test_router.py +110 -0
  145. wbportfolio/permissions.py +7 -0
  146. wbportfolio/pms/analytics/portfolio.py +17 -9
  147. wbportfolio/pms/analytics/utils.py +9 -0
  148. wbportfolio/pms/trading/__init__.py +0 -1
  149. wbportfolio/pms/trading/optimizer.py +61 -0
  150. wbportfolio/pms/typing.py +198 -63
  151. wbportfolio/rebalancing/base.py +12 -1
  152. wbportfolio/rebalancing/decorators.py +1 -1
  153. wbportfolio/rebalancing/models/composite.py +4 -8
  154. wbportfolio/rebalancing/models/equally_weighted.py +13 -11
  155. wbportfolio/rebalancing/models/market_capitalization_weighted.py +21 -14
  156. wbportfolio/rebalancing/models/model_portfolio.py +14 -18
  157. wbportfolio/risk_management/backends/__init__.py +1 -0
  158. wbportfolio/risk_management/backends/controversy_portfolio.py +2 -2
  159. wbportfolio/risk_management/backends/esg_aggregation_portfolio.py +64 -0
  160. wbportfolio/risk_management/backends/exposure_portfolio.py +4 -4
  161. wbportfolio/risk_management/backends/instrument_list_portfolio.py +3 -3
  162. wbportfolio/risk_management/tests/test_esg_aggregation_portfolio.py +49 -0
  163. wbportfolio/risk_management/tests/test_exposure_portfolio.py +1 -1
  164. wbportfolio/risk_management/tests/test_stop_loss_instrument.py +2 -2
  165. wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +1 -1
  166. wbportfolio/serializers/__init__.py +1 -0
  167. wbportfolio/serializers/orders/__init__.py +2 -0
  168. wbportfolio/serializers/orders/order_proposals.py +115 -0
  169. wbportfolio/serializers/orders/orders.py +283 -0
  170. wbportfolio/serializers/portfolios.py +7 -7
  171. wbportfolio/serializers/positions.py +2 -2
  172. wbportfolio/serializers/rebalancing.py +1 -1
  173. wbportfolio/serializers/signals.py +9 -12
  174. wbportfolio/serializers/transactions/__init__.py +1 -10
  175. wbportfolio/serializers/transactions/claim.py +2 -2
  176. wbportfolio/serializers/transactions/dividends.py +37 -9
  177. wbportfolio/serializers/transactions/fees.py +39 -10
  178. wbportfolio/serializers/transactions/trades.py +55 -157
  179. wbportfolio/tasks.py +43 -5
  180. wbportfolio/tests/analysis/__init__.py +0 -0
  181. wbportfolio/tests/analysis/test_claims.py +85 -0
  182. wbportfolio/tests/conftest.py +12 -12
  183. wbportfolio/tests/models/orders/__init__.py +0 -0
  184. wbportfolio/tests/models/orders/test_order_proposals.py +1046 -0
  185. wbportfolio/tests/models/test_assets.py +7 -3
  186. wbportfolio/tests/models/test_imports.py +9 -13
  187. wbportfolio/tests/models/test_portfolios.py +102 -95
  188. wbportfolio/tests/models/test_products.py +11 -0
  189. wbportfolio/tests/models/test_splits.py +1 -6
  190. wbportfolio/tests/models/test_utils.py +140 -0
  191. wbportfolio/tests/models/transactions/test_fees.py +7 -13
  192. wbportfolio/tests/models/transactions/test_rebalancing.py +5 -5
  193. wbportfolio/tests/models/transactions/test_trades.py +0 -20
  194. wbportfolio/tests/pms/test_analytics.py +22 -3
  195. wbportfolio/tests/rebalancing/test_models.py +51 -57
  196. wbportfolio/tests/signals.py +10 -20
  197. wbportfolio/tests/tests.py +3 -1
  198. wbportfolio/tests/viewsets/test_products.py +1 -0
  199. wbportfolio/urls.py +10 -13
  200. wbportfolio/viewsets/__init__.py +9 -4
  201. wbportfolio/viewsets/assets.py +3 -204
  202. wbportfolio/viewsets/charts/__init__.py +6 -1
  203. wbportfolio/viewsets/charts/assets.py +344 -154
  204. wbportfolio/viewsets/configs/buttons/__init__.py +2 -2
  205. wbportfolio/viewsets/configs/buttons/assets.py +1 -1
  206. wbportfolio/viewsets/configs/buttons/mixins.py +4 -4
  207. wbportfolio/viewsets/configs/buttons/portfolios.py +45 -1
  208. wbportfolio/viewsets/configs/buttons/products.py +32 -2
  209. wbportfolio/viewsets/configs/display/__init__.py +2 -5
  210. wbportfolio/viewsets/configs/display/assets.py +6 -19
  211. wbportfolio/viewsets/configs/display/fees.py +3 -3
  212. wbportfolio/viewsets/configs/display/portfolios.py +5 -5
  213. wbportfolio/viewsets/configs/display/products.py +1 -1
  214. wbportfolio/viewsets/configs/display/rebalancing.py +2 -2
  215. wbportfolio/viewsets/configs/display/reconciliations.py +4 -4
  216. wbportfolio/viewsets/configs/display/trades.py +1 -189
  217. wbportfolio/viewsets/configs/endpoints/__init__.py +3 -7
  218. wbportfolio/viewsets/configs/endpoints/fees.py +2 -2
  219. wbportfolio/viewsets/configs/endpoints/trades.py +0 -41
  220. wbportfolio/viewsets/configs/menu/__init__.py +1 -1
  221. wbportfolio/viewsets/configs/menu/orders.py +11 -0
  222. wbportfolio/viewsets/configs/titles/__init__.py +2 -3
  223. wbportfolio/viewsets/configs/titles/fees.py +4 -8
  224. wbportfolio/viewsets/esg.py +3 -5
  225. wbportfolio/viewsets/mixins.py +5 -1
  226. wbportfolio/viewsets/orders/__init__.py +6 -0
  227. wbportfolio/viewsets/orders/configs/__init__.py +4 -0
  228. wbportfolio/viewsets/orders/configs/buttons/__init__.py +2 -0
  229. wbportfolio/viewsets/orders/configs/buttons/order_proposals.py +188 -0
  230. wbportfolio/viewsets/orders/configs/buttons/orders.py +113 -0
  231. wbportfolio/viewsets/orders/configs/displays/__init__.py +2 -0
  232. wbportfolio/viewsets/orders/configs/displays/order_proposals.py +157 -0
  233. wbportfolio/viewsets/orders/configs/displays/orders.py +232 -0
  234. wbportfolio/viewsets/orders/configs/endpoints/__init__.py +2 -0
  235. wbportfolio/viewsets/orders/configs/endpoints/order_proposals.py +21 -0
  236. wbportfolio/viewsets/orders/configs/endpoints/orders.py +28 -0
  237. wbportfolio/viewsets/orders/configs/titles/__init__.py +0 -0
  238. wbportfolio/viewsets/orders/configs/titles/orders.py +0 -0
  239. wbportfolio/viewsets/orders/order_proposals.py +252 -0
  240. wbportfolio/viewsets/orders/orders.py +277 -0
  241. wbportfolio/viewsets/portfolios.py +36 -12
  242. wbportfolio/viewsets/positions.py +3 -2
  243. wbportfolio/viewsets/products.py +6 -6
  244. wbportfolio/viewsets/{transactions/rebalancing.py → rebalancing.py} +2 -2
  245. wbportfolio/viewsets/transactions/__init__.py +3 -14
  246. wbportfolio/viewsets/transactions/fees.py +22 -22
  247. wbportfolio/viewsets/transactions/trades.py +1 -180
  248. {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/METADATA +3 -1
  249. {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/RECORD +252 -203
  250. {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/WHEEL +1 -1
  251. wbportfolio/admin/transactions/transactions.py +0 -38
  252. wbportfolio/factories/transactions.py +0 -22
  253. wbportfolio/fdm/tasks.py +0 -13
  254. wbportfolio/filters/transactions/transactions.py +0 -99
  255. wbportfolio/models/transactions/expiry.py +0 -7
  256. wbportfolio/models/transactions/trade_proposals.py +0 -704
  257. wbportfolio/pms/trading/handler.py +0 -161
  258. wbportfolio/serializers/transactions/expiry.py +0 -18
  259. wbportfolio/serializers/transactions/trade_proposals.py +0 -76
  260. wbportfolio/serializers/transactions/transactions.py +0 -85
  261. wbportfolio/tests/models/transactions/test_trade_proposals.py +0 -410
  262. wbportfolio/viewsets/configs/buttons/trade_proposals.py +0 -66
  263. wbportfolio/viewsets/configs/display/trade_proposals.py +0 -100
  264. wbportfolio/viewsets/configs/display/transactions.py +0 -55
  265. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +0 -18
  266. wbportfolio/viewsets/configs/endpoints/transactions.py +0 -14
  267. wbportfolio/viewsets/configs/menu/transactions.py +0 -9
  268. wbportfolio/viewsets/configs/titles/transactions.py +0 -9
  269. wbportfolio/viewsets/signals.py +0 -43
  270. wbportfolio/viewsets/transactions/trade_proposals.py +0 -139
  271. wbportfolio/viewsets/transactions/transactions.py +0 -122
  272. /wbportfolio/{fdm → api_clients}/__init__.py +0 -0
  273. {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +0,0 @@
1
- from wbcore.metadata.configs.titles import TitleViewConfig
2
-
3
- from wbportfolio.models import Portfolio
4
-
5
-
6
- class TransactionPortfolioTitleConfig(TitleViewConfig):
7
- def get_list_title(self):
8
- portfolio = Portfolio.objects.get(id=self.view.kwargs["portfolio_id"])
9
- return f"Transactions for Product {str(portfolio)}"
@@ -1,43 +0,0 @@
1
- from django.apps import apps
2
- from django.contrib.contenttypes.models import ContentType
3
- from django.db.models import Q
4
- from django.dispatch import receiver
5
- from rest_framework.reverse import reverse
6
- from wbcore.contrib.icons import WBIcon
7
- from wbcore.metadata.configs import buttons as bt
8
- from wbcore.signals.instance_buttons import add_instance_button
9
-
10
- from wbportfolio.viewsets.products import ProductModelViewSet
11
-
12
-
13
- @receiver(add_instance_button, sender=ProductModelViewSet)
14
- def add_report_buttons(sender, many, *args, request=None, **kwargs):
15
- report_buttons = []
16
- try:
17
- if instrument_id := kwargs.get("pk", None):
18
- content_type_product = ContentType.objects.get(app_label="wbportfolio", model="product")
19
- content_type_instrument = ContentType.objects.get(app_label="wbfdm", model="instrument")
20
- Report = apps.get_model("wbreport", "Report")
21
- for report in Report.objects.filter(object_id=instrument_id, is_active=True).filter(
22
- Q(content_type=content_type_product) | Q(content_type=content_type_instrument)
23
- ):
24
- if primary_version := report.primary_version:
25
- report_buttons.append(
26
- bt.HyperlinkButton(
27
- label=f"{report.title} Report",
28
- endpoint=reverse(
29
- "public_report:report_version", args=[primary_version.lookup], request=request
30
- ),
31
- ),
32
- )
33
- if request.user.profile.is_internal or request.user.is_superuser:
34
- report_buttons.append(
35
- bt.HyperlinkButton(
36
- label=f"{report.title} Widget",
37
- endpoint=reverse("wbreport:report-detail", args=[report.id], request=request),
38
- ),
39
- )
40
- except LookupError:
41
- pass
42
- if report_buttons:
43
- return bt.DropDownButton(label="Reports", buttons=tuple(report_buttons), icon=WBIcon.NOTEBOOK.icon)
@@ -1,139 +0,0 @@
1
- from contextlib import suppress
2
- from datetime import date
3
-
4
- from django.contrib.messages import info, warning
5
- from django.shortcuts import get_object_or_404
6
- from django.utils.functional import cached_property
7
- from pandas._libs.tslibs.offsets import BDay
8
- from rest_framework import status
9
- from rest_framework.decorators import action
10
- from rest_framework.response import Response
11
- from wbcompliance.viewsets.risk_management.mixins import RiskCheckViewSetMixin
12
- from wbcore import serializers as wb_serializers
13
- from wbcore import viewsets
14
- from wbcore.metadata.configs.display.instance_display import (
15
- Display,
16
- create_simple_display,
17
- )
18
- from wbcore.permissions.permissions import InternalUserPermissionMixin
19
- from wbcore.utils.views import CloneMixin
20
-
21
- from wbportfolio.models import AssetPosition, TradeProposal
22
- from wbportfolio.models.transactions.trade_proposals import (
23
- replay_as_task,
24
- )
25
- from wbportfolio.serializers import (
26
- ReadOnlyTradeProposalModelSerializer,
27
- TradeProposalModelSerializer,
28
- TradeProposalRepresentationSerializer,
29
- )
30
-
31
- from ..configs import (
32
- TradeProposalButtonConfig,
33
- TradeProposalDisplayConfig,
34
- TradeProposalEndpointConfig,
35
- TradeProposalPortfolioEndpointConfig,
36
- )
37
- from ..mixins import UserPortfolioRequestPermissionMixin
38
-
39
-
40
- class TradeProposalRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
41
- IDENTIFIER = "wbportfolio:trade"
42
- queryset = TradeProposal.objects.all()
43
- serializer_class = TradeProposalRepresentationSerializer
44
-
45
-
46
- class TradeProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserPermissionMixin, viewsets.ModelViewSet):
47
- ordering_fields = ("trade_date",)
48
- ordering = ("-trade_date",)
49
- search_fields = ("comment",)
50
- filterset_fields = {"trade_date": ["exact", "gte", "lte"], "status": ["exact"]}
51
-
52
- queryset = TradeProposal.objects.select_related("rebalancing_model", "portfolio")
53
- serializer_class = TradeProposalModelSerializer
54
- display_config_class = TradeProposalDisplayConfig
55
- button_config_class = TradeProposalButtonConfig
56
- endpoint_config_class = TradeProposalEndpointConfig
57
-
58
- def get_serializer_class(self):
59
- if self.new_mode or (
60
- "pk" in self.kwargs and (obj := self.get_object()) and obj.status == TradeProposal.Status.DRAFT
61
- ):
62
- return TradeProposalModelSerializer
63
- return ReadOnlyTradeProposalModelSerializer
64
-
65
- # 2 methods to parametrize the clone button functionality
66
- def get_clone_button_serializer_class(self, instance):
67
- class CloneSerializer(wb_serializers.Serializer):
68
- clone_date = wb_serializers.DateField(
69
- default=(instance.trade_date + BDay(1)).date(), label="Trade Date"
70
- ) # we need to change the field name from the trade proposa fields, otherwise fontend conflicts
71
- clone_comment = wb_serializers.TextField(label="Comment")
72
-
73
- return CloneSerializer
74
-
75
- def get_clone_button_instance_display(self) -> Display:
76
- return create_simple_display(
77
- [
78
- ["clone_comment"],
79
- ["clone_date"],
80
- ]
81
- )
82
-
83
- def add_messages(self, request, instance=None, **kwargs):
84
- if instance and instance.status == TradeProposal.Status.SUBMIT:
85
- if not instance.portfolio.is_manageable:
86
- info(request, "This trade proposal cannot be approved the portfolio is considered unmanaged.")
87
- if instance.has_non_successful_checks:
88
- warning(
89
- request,
90
- "This trade proposal cannot be approved because there is unsuccessful pre-trade checks. Please rectify accordingly and resubmit a valid trade proposal",
91
- )
92
-
93
- @classmethod
94
- def _get_risk_checks_button_title(cls) -> str:
95
- return "Pre-Trade Checks"
96
-
97
- @action(detail=True, methods=["PATCH"])
98
- def reset(self, request, pk=None):
99
- trade_proposal = get_object_or_404(TradeProposal, pk=pk)
100
- if trade_proposal.status == TradeProposal.Status.DRAFT:
101
- trade_proposal.reset_trades()
102
- return Response({"send": True})
103
- return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
104
-
105
- @action(detail=True, methods=["PATCH"])
106
- def normalize(self, request, pk=None):
107
- trade_proposal = get_object_or_404(TradeProposal, pk=pk)
108
- if trade_proposal.status == TradeProposal.Status.DRAFT:
109
- trade_proposal.normalize_trades()
110
- return Response({"send": True})
111
- return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
112
-
113
- @action(detail=True, methods=["PATCH"])
114
- def replay(self, request, pk=None):
115
- trade_proposal = get_object_or_404(TradeProposal, pk=pk)
116
- if trade_proposal.portfolio.is_manageable:
117
- replay_as_task.delay(trade_proposal.id, user_id=self.request.user.id)
118
- return Response({"send": True})
119
- return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
120
-
121
- @action(detail=True, methods=["PATCH"])
122
- def deleteall(self, request, pk=None):
123
- trade_proposal = get_object_or_404(TradeProposal, pk=pk)
124
- if trade_proposal.status == TradeProposal.Status.DRAFT:
125
- trade_proposal.trades.all().delete()
126
- return Response({"send": True})
127
- return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
128
-
129
-
130
- class TradeProposalPortfolioModelViewSet(UserPortfolioRequestPermissionMixin, TradeProposalModelViewSet):
131
- endpoint_config_class = TradeProposalPortfolioEndpointConfig
132
-
133
- @cached_property
134
- def default_trade_date(self) -> date | None:
135
- with suppress(AssetPosition.DoesNotExist):
136
- return (self.portfolio.assets.latest("date").date + BDay(1)).date()
137
-
138
- def get_queryset(self):
139
- return TradeProposal.objects.filter(portfolio=self.kwargs["portfolio_id"])
@@ -1,122 +0,0 @@
1
- from decimal import Decimal
2
-
3
- from django.db.models import (
4
- Case,
5
- DecimalField,
6
- ExpressionWrapper,
7
- F,
8
- OuterRef,
9
- Subquery,
10
- Sum,
11
- When,
12
- )
13
- from wbcore import viewsets
14
- from wbcore.contrib.currency.models import CurrencyFXRates
15
- from wbcore.permissions.permissions import InternalUserPermissionMixin
16
- from wbcore.utils.strings import format_number
17
-
18
- from wbportfolio.filters import TransactionFilterSet, TransactionPortfolioFilterSet
19
- from wbportfolio.models import Fees, Trade, Transaction
20
- from wbportfolio.serializers import (
21
- TransactionModelSerializer,
22
- TransactionRepresentationSerializer,
23
- )
24
-
25
- from ..configs import (
26
- TransactionDisplayConfig,
27
- TransactionEndpointConfig,
28
- TransactionPortfolioDisplayConfig,
29
- TransactionPortfolioEndpointConfig,
30
- TransactionPortfolioTitleConfig,
31
- )
32
- from ..mixins import UserPortfolioRequestPermissionMixin
33
-
34
-
35
- class TransactionRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
36
- IDENTIFIER = "wbportfolio:transaction"
37
- filterset_class = TransactionFilterSet
38
- search_fields = ("portfolio__name", "underlying_instrument__name", "comment")
39
- ordering_fields = (
40
- "transaction_date",
41
- "currency__key",
42
- "underlying_instrument__name",
43
- "portfolio__name",
44
- "transaction_type",
45
- "book_date",
46
- "value_date",
47
- "currency_fx_rate",
48
- "total_value",
49
- "total_value_fx_portfolio",
50
- "total_value_gross",
51
- "total_value_gross_fx_portfolio",
52
- )
53
- ordering = ["-transaction_date"]
54
-
55
- serializer_class = TransactionRepresentationSerializer
56
- queryset = Transaction.objects.all()
57
-
58
-
59
- class TransactionModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPermissionMixin, viewsets.ModelViewSet):
60
- IDENTIFIER = "wbportfolio:transaction"
61
-
62
- serializer_class = TransactionModelSerializer
63
-
64
- filterset_class = TransactionFilterSet
65
- ordering_fields = TransactionRepresentationViewSet.ordering_fields
66
- search_fields = TransactionRepresentationViewSet.search_fields
67
- ordering = TransactionRepresentationViewSet.ordering
68
-
69
- queryset = Transaction.objects.all()
70
- display_config_class = TransactionDisplayConfig
71
- endpoint_config_class = TransactionEndpointConfig
72
-
73
- def get_queryset(self):
74
- return (
75
- super()
76
- .get_queryset()
77
- .annotate(
78
- fx_rate=CurrencyFXRates.get_fx_rates_subquery(
79
- "transaction_date", currency="currency", lookup_expr="exact"
80
- ),
81
- total_value_usd=ExpressionWrapper(F("total_value"), output_field=DecimalField()),
82
- total_value_gross_usd=ExpressionWrapper(F("total_value_gross"), output_field=DecimalField()),
83
- transaction_underlying_type_trade=Subquery(
84
- Trade.objects.filter(id=OuterRef("pk")).values("transaction_subtype")[:1]
85
- ),
86
- transaction_underlying_type_fees=Subquery(
87
- Fees.objects.filter(id=OuterRef("pk")).values("transaction_subtype")[:1]
88
- ),
89
- transaction_underlying_type=Case(
90
- When(
91
- transaction_underlying_type_trade__isnull=False,
92
- then=F("transaction_underlying_type_trade"),
93
- ),
94
- When(transaction_underlying_type_fees__isnull=False, then=F("transaction_underlying_type_fees")),
95
- ),
96
- )
97
- .select_related("underlying_instrument")
98
- .select_related("portfolio")
99
- .select_related("currency")
100
- )
101
-
102
- def get_aggregates(self, queryset, paginated_queryset):
103
- return {
104
- "total_value_usd": {
105
- "Σ": format_number(queryset.aggregate(s=Sum("total_value_usd"))["s"] or Decimal(0)),
106
- }
107
- }
108
-
109
-
110
- class TransactionPortfolioModelViewSet(TransactionModelViewSet):
111
- IDENTIFIER = "wbportfolio:transaction"
112
-
113
- filterset_class = TransactionPortfolioFilterSet
114
- display_config_class = TransactionPortfolioDisplayConfig
115
- endpoint_config_class = TransactionPortfolioEndpointConfig
116
- title_config_class = TransactionPortfolioTitleConfig
117
-
118
- def get_queryset(self):
119
- qs = super().get_queryset().filter(portfolio=self.portfolio)
120
- if self.is_portfolio_manager:
121
- return qs
122
- return qs.exclude(transaction_type=Transaction.Type.FEES)
File without changes