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,161 +0,0 @@
1
- from datetime import date
2
- from decimal import Decimal
3
-
4
- from django.core.exceptions import ValidationError
5
-
6
- from wbportfolio.pms.typing import Portfolio, Trade, TradeBatch
7
-
8
-
9
- class TradingService:
10
- """
11
- This class represents the trading service. It can be instantiated either with the target portfolio and the effective portfolio or given a direct list of trade
12
- In any case, it will compute all three states
13
- """
14
-
15
- def __init__(
16
- self,
17
- trade_date: date,
18
- effective_portfolio: Portfolio | None = None,
19
- target_portfolio: Portfolio | None = None,
20
- trades_batch: TradeBatch | None = None,
21
- total_value: Decimal = None,
22
- ):
23
- self.total_value = total_value
24
- self.trade_date = trade_date
25
- if target_portfolio is None:
26
- target_portfolio = Portfolio(positions=())
27
- if effective_portfolio is None:
28
- effective_portfolio = Portfolio(positions=())
29
- # If effective portfoolio and trades batch is provided, we ensure the trade batch contains at least one trade for every position
30
- trades_batch = self.build_trade_batch(effective_portfolio, target_portfolio, trades_batch=trades_batch)
31
- # if no trade but a effective portfolio is provided, we get the trade batch only from the effective portofolio (and the target portfolio if provided, but optional. Without it, the trade delta weight will be 0 )
32
- # Finally, we compute the target portfolio
33
- if trades_batch and not target_portfolio:
34
- target_portfolio = trades_batch.convert_to_portfolio()
35
-
36
- self.trades_batch = trades_batch
37
- self.effective_portfolio = effective_portfolio
38
- self.target_portfolio = target_portfolio
39
-
40
- @property
41
- def errors(self) -> list[str]:
42
- """
43
- Returned the list of errors stored during the validation process. Can only be called after is_valid
44
- """
45
- if not hasattr(self, "_errors"):
46
- msg = "You must call `.is_valid()` before accessing `.errors`."
47
- raise AssertionError(msg)
48
- return self._errors
49
-
50
- @property
51
- def validated_trades(self) -> list[Trade]:
52
- """
53
- Returned the list of validated trade stored during the validation process. Can only be called after is_valid
54
- """
55
- if not hasattr(self, "_validated_trades"):
56
- msg = "You must call `.is_valid()` before accessing `.validated_trades`."
57
- raise AssertionError(msg)
58
- return self._validated_trades
59
-
60
- def run_validation(self, validated_trades: list[Trade]):
61
- """
62
- Test the given value against all the validators on the field,
63
- and either raise a `ValidationError` or simply return.
64
- """
65
- # TradeBatch(validated_trades).validate()
66
- if self.effective_portfolio:
67
- for trade in validated_trades:
68
- if (
69
- trade.effective_weight
70
- and trade.underlying_instrument not in self.effective_portfolio.positions_map
71
- ):
72
- raise ValidationError("All effective position needs to be matched with a validated trade")
73
-
74
- def build_trade_batch(
75
- self,
76
- effective_portfolio: Portfolio,
77
- target_portfolio: Portfolio,
78
- trades_batch: TradeBatch | None = None,
79
- ) -> TradeBatch:
80
- """
81
- Given combination of effective portfolio and either a trades batch or a target portfolio, ensure all theres variables are set
82
-
83
- Args:
84
- effective_portfolio: The effective portfolio
85
- target_portfolio: The optional target portfolio
86
- trades_batch: The optional trades batch
87
-
88
- Returns: The normalized trades batch
89
- """
90
- instruments = list(effective_portfolio.positions_map.keys())
91
- instruments.extend(list(target_portfolio.positions_map.keys()))
92
- if trades_batch:
93
- instruments.extend(list(trades_batch.trades_map.keys()))
94
- _trades: list[Trade] = []
95
- for instrument in set(instruments):
96
- effective_weight = target_weight = 0
97
- effective_shares = 0
98
- instrument_type = currency = None
99
- if effective_pos := effective_portfolio.positions_map.get(instrument, None):
100
- effective_weight = effective_pos.weighting
101
- effective_shares = effective_pos.shares
102
- instrument_type, currency = effective_pos.instrument_type, effective_pos.currency
103
- if target_pos := target_portfolio.positions_map.get(instrument, None):
104
- target_weight = target_pos.weighting
105
- instrument_type, currency = target_pos.instrument_type, target_pos.currency
106
- if trades_batch and (trade := trades_batch.trades_map.get(instrument, None)):
107
- effective_weight, target_weight = trade.effective_weight, trade.target_weight
108
- effective_shares = trade.effective_shares
109
- instrument_type, currency = trade.instrument_type, trade.currency
110
-
111
- _trades.append(
112
- Trade(
113
- underlying_instrument=instrument,
114
- effective_weight=effective_weight,
115
- target_weight=target_weight,
116
- effective_shares=effective_shares,
117
- date=self.trade_date,
118
- instrument_type=instrument_type,
119
- currency=currency,
120
- )
121
- )
122
- return TradeBatch(tuple(_trades))
123
-
124
- def is_valid(self, ignore_error: bool = False) -> bool:
125
- """
126
- Validate the trade batch against a set of default rules. Populate the validated_trades and errors property.
127
- Ignore error by default
128
- Args:
129
- ignore_error: If true, will raise the error. False by default
130
-
131
- Returns: True if the trades batch is valid
132
- """
133
- if not hasattr(self, "_validated_trades"):
134
- self._validated_trades = []
135
- self._errors = []
136
- # Run validation for every trade. If a trade is not valid, we simply exclude it from the validated trades list
137
- for _, trade in self.trades_batch.trades_map.items():
138
- try:
139
- trade.validate()
140
- self._validated_trades.append(trade)
141
- except ValidationError as exc:
142
- self._errors.append(exc.message)
143
- try:
144
- # Check the overall validity of the trade batch. If this fail, we consider all trade invalids
145
- self.run_validation(self._validated_trades)
146
- except ValidationError as exc:
147
- self._validated_trades = []
148
- self._errors.append(exc.message)
149
-
150
- if self._errors and not ignore_error:
151
- raise ValidationError(self.errors)
152
-
153
- return not bool(self._errors)
154
-
155
- def normalize(self):
156
- """
157
- Normalize the instantiate trades batch so that the target weight is 100%
158
- """
159
- self.trades_batch = TradeBatch(
160
- [trade.normalize_target(self.trades_batch.total_target_weight) for trade in self.trades_batch.trades]
161
- )
@@ -1,18 +0,0 @@
1
- from wbportfolio.models import Expiry
2
-
3
- from .transactions import (
4
- TransactionModelSerializer,
5
- TransactionRepresentationSerializer,
6
- )
7
-
8
-
9
- class ExpiryRepresentationSerializer(TransactionRepresentationSerializer):
10
- class Meta:
11
- model = Expiry
12
- fields = TransactionRepresentationSerializer.Meta.fields
13
-
14
-
15
- class ExpiryModelSerializer(TransactionModelSerializer):
16
- class Meta:
17
- model = Expiry
18
- fields = TransactionModelSerializer.Meta.fields
@@ -1,76 +0,0 @@
1
- from django.contrib.messages import warning
2
- from django.core.exceptions import ValidationError
3
- from rest_framework.reverse import reverse
4
- from wbcore import serializers as wb_serializers
5
- from wbcore.serializers import DefaultFromView
6
-
7
- from wbportfolio.models import Portfolio, RebalancingModel, TradeProposal
8
-
9
- from .. import PortfolioRepresentationSerializer, RebalancingModelRepresentationSerializer
10
-
11
-
12
- class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
13
- rebalancing_model = wb_serializers.PrimaryKeyRelatedField(queryset=RebalancingModel.objects.all(), required=False)
14
- _rebalancing_model = RebalancingModelRepresentationSerializer(source="rebalancing_model")
15
- target_portfolio = wb_serializers.PrimaryKeyRelatedField(
16
- queryset=Portfolio.objects.all(), write_only=True, required=False, default=DefaultFromView("portfolio")
17
- )
18
- _target_portfolio = PortfolioRepresentationSerializer(source="target_portfolio")
19
-
20
- trade_date = wb_serializers.DateField(
21
- read_only=lambda view: not view.new_mode, default=DefaultFromView("default_trade_date")
22
- )
23
-
24
- def create(self, validated_data):
25
- target_portfolio = validated_data.pop("target_portfolio", None)
26
- rebalancing_model = validated_data.get("rebalancing_model", None)
27
- if request := self.context.get("request"):
28
- validated_data["creator"] = request.user.profile
29
- obj = super().create(validated_data)
30
-
31
- target_portfolio_dto = None
32
- if target_portfolio and not rebalancing_model and (last_effective_date := obj.last_effective_date):
33
- target_portfolio_dto = target_portfolio._build_dto(last_effective_date)
34
- try:
35
- obj.reset_trades(target_portfolio=target_portfolio_dto)
36
- except ValidationError as e:
37
- if request := self.context.get("request"):
38
- warning(request, str(e), extra_tags="auto_close=0")
39
- return obj
40
-
41
- @wb_serializers.register_only_instance_resource()
42
- def additional_resources(self, instance, request, user, **kwargs):
43
- res = {}
44
- if instance.status == TradeProposal.Status.APPROVED:
45
- res["replay"] = reverse("wbportfolio:tradeproposal-replay", args=[instance.id], request=request)
46
- if instance.status == TradeProposal.Status.DRAFT:
47
- res["reset"] = reverse("wbportfolio:tradeproposal-reset", args=[instance.id], request=request)
48
- res["normalize"] = reverse("wbportfolio:tradeproposal-normalize", args=[instance.id], request=request)
49
- res["deleteall"] = reverse("wbportfolio:tradeproposal-deleteall", args=[instance.id], request=request)
50
- res["trades"] = reverse(
51
- "wbportfolio:tradeproposal-trade-list",
52
- args=[instance.id],
53
- request=request,
54
- )
55
- return res
56
-
57
- class Meta:
58
- model = TradeProposal
59
- only_fsm_transition_on_instance = True
60
- fields = (
61
- "id",
62
- "trade_date",
63
- "comment",
64
- "status",
65
- "portfolio",
66
- "_rebalancing_model",
67
- "rebalancing_model",
68
- "target_portfolio",
69
- "_target_portfolio",
70
- "_additional_resources",
71
- )
72
-
73
-
74
- class ReadOnlyTradeProposalModelSerializer(TradeProposalModelSerializer):
75
- class Meta(TradeProposalModelSerializer.Meta):
76
- read_only_fields = TradeProposalModelSerializer.Meta.fields
@@ -1,85 +0,0 @@
1
- from wbcore import serializers as wb_serializers
2
- from wbcore.contrib.currency.serializers import CurrencyRepresentationSerializer
3
- from wbfdm.serializers import InvestableUniverseRepresentationSerializer
4
-
5
- from wbportfolio.models import Transaction
6
- from wbportfolio.serializers.portfolios import PortfolioRepresentationSerializer
7
-
8
-
9
- class TransactionRepresentationSerializer(wb_serializers.RepresentationSerializer):
10
- _detail = wb_serializers.HyperlinkField(reverse_name="wbportfolio:transaction-detail")
11
-
12
- class Meta:
13
- model = Transaction
14
- fields = ("id", "transaction_date", "total_value", "_detail")
15
-
16
-
17
- class TransactionModelSerializer(wb_serializers.ModelSerializer):
18
- external_id = wb_serializers.CharField(required=False, read_only=True)
19
- value_date = wb_serializers.DateField(required=False, read_only=True)
20
- total_value_usd = wb_serializers.FloatField(default=0, read_only=True, label="Total Value ($)")
21
- total_value_gross_usd = wb_serializers.FloatField(default=0, read_only=True, label="Total Value Gross ($)")
22
- transaction_underlying_type = wb_serializers.CharField(read_only=True)
23
- transaction_url_type = wb_serializers.SerializerMethodField()
24
- _portfolio = PortfolioRepresentationSerializer(source="portfolio")
25
- _underlying_instrument = InvestableUniverseRepresentationSerializer(source="underlying_instrument")
26
- _currency = CurrencyRepresentationSerializer(source="currency")
27
-
28
- total_value = wb_serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
29
- total_value_fx_portfolio = wb_serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
30
- total_value_gross = wb_serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
31
- total_value_gross_fx_portfolio = wb_serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
32
-
33
- def get_transaction_url_type(self, obj):
34
- return obj.transaction_type.lower()
35
-
36
- # def get_transaction_underlying_type(self, obj):
37
- # try:
38
- # casted_transaction = obj.get_casted_transaction()
39
- # return casted_transaction.Type[casted_transaction.transaction_subtype].label
40
- # except Exception as e:
41
- # return ""
42
- class Meta:
43
- model = Transaction
44
- decorators = {
45
- "total_value": wb_serializers.decorator(
46
- decorator_type="text", position="left", value="{{_currency.symbol}}"
47
- ),
48
- "total_value_gross": wb_serializers.decorator(
49
- decorator_type="text", position="left", value="{{_currency.symbol}}"
50
- ),
51
- "total_value_usd": wb_serializers.decorator(decorator_type="text", position="left", value="{{$}}"),
52
- "total_value_gross_usd": wb_serializers.decorator(decorator_type="text", position="left", value="{{$}}"),
53
- "total_value_fx_portfolio": wb_serializers.decorator(
54
- position="left", value="{{_portfolio.currency_symbol}}"
55
- ),
56
- "total_value_gross_fx_portfolio": wb_serializers.decorator(
57
- position="left", value="{{_portfolio.currency_symbol}}"
58
- ),
59
- # "total_value_fx_portfolio": wb_serializers.decorator(decorator_type="text", position="left", value="{{_portfolio.currency_symbol}}}"),
60
- # "total_value_gross_fx_portfolio": wb_serializers.decorator(decorator_type="text", position="left", value="{{_portfolio.currency_symbol}}"),
61
- }
62
- fields = (
63
- "id",
64
- "transaction_type",
65
- "transaction_url_type",
66
- "transaction_underlying_type",
67
- "portfolio",
68
- "_portfolio",
69
- "underlying_instrument",
70
- "_underlying_instrument",
71
- "transaction_date",
72
- "book_date",
73
- "value_date",
74
- "currency",
75
- "_currency",
76
- "currency_fx_rate",
77
- "total_value",
78
- "total_value_fx_portfolio",
79
- "total_value_gross",
80
- "total_value_gross_fx_portfolio",
81
- "external_id",
82
- "comment",
83
- "total_value_usd",
84
- "total_value_gross_usd",
85
- )