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,6 +1,6 @@
1
1
  from wbcore import filters
2
- from wbcore.contrib.directory.filters import CompanyFilter as CF
3
- from wbcore.contrib.directory.filters import PersonFilter as PF
2
+ from wbcore.contrib.directory.filters import CompanyFilter as BaseCompanyFilter
3
+ from wbcore.contrib.directory.filters import PersonFilter as BasePersonFilter
4
4
  from wbcore.contrib.directory.models import Company, Person
5
5
 
6
6
 
@@ -99,29 +99,29 @@ class EntryPortfolioFilter(filters.FilterSet):
99
99
  return queryset
100
100
 
101
101
 
102
- class CompanyFilter(CF, EntryPortfolioFilter):
102
+ class CompanyFilter(BaseCompanyFilter, EntryPortfolioFilter):
103
103
  @classmethod
104
104
  def get_filter_class_for_remote_filter(cls):
105
105
  """
106
106
  Define which filterset class sender to user for remote filter registration
107
107
  """
108
- return CF
108
+ return BaseCompanyFilter
109
109
 
110
- class Meta(CF.Meta):
110
+ class Meta(BaseCompanyFilter.Meta):
111
111
  fields = {
112
- **CF.Meta.fields,
112
+ **BaseCompanyFilter.Meta.fields,
113
113
  }
114
114
 
115
115
 
116
- class PersonFilter(PF, EntryPortfolioFilter):
116
+ class PersonFilter(BasePersonFilter, EntryPortfolioFilter):
117
117
  @classmethod
118
118
  def get_filter_class_for_remote_filter(cls):
119
119
  """
120
120
  Define which filterset class sender to user for remote filter registration
121
121
  """
122
- return PF
122
+ return BasePersonFilter
123
123
 
124
- class Meta(PF.Meta):
124
+ class Meta(BasePersonFilter.Meta):
125
125
  fields = {
126
- **PF.Meta.fields,
126
+ **BasePersonFilter.Meta.fields,
127
127
  }
@@ -3,6 +3,7 @@ from datetime import date
3
3
  from decimal import Decimal
4
4
 
5
5
  from django.conf import settings
6
+ from django.core.cache import cache
6
7
  from django.db import models
7
8
  from django.db.models.signals import post_save
8
9
  from django.dispatch import receiver
@@ -16,7 +17,12 @@ from wbportfolio.models import Claim, Product
16
17
 
17
18
 
18
19
  def get_total_assets_under_management(val_date: date) -> Decimal:
19
- return sum([product.get_total_aum_usd(val_date) for product in Product.active_objects.all()])
20
+ cache_key = f"total_assets_under_management:{val_date.isoformat()}"
21
+ return cache.get_or_set(
22
+ cache_key,
23
+ lambda: sum([product.get_total_aum_usd(val_date) for product in Product.active_objects.all()]),
24
+ 60 * 60 * 24,
25
+ )
20
26
 
21
27
 
22
28
  def get_lost_client_customer_status():
@@ -44,9 +50,8 @@ class Updater:
44
50
  self.val_date = val_date
45
51
  self.total_assets_under_management = get_total_assets_under_management(val_date)
46
52
 
47
- def update_company_data(self, company):
53
+ def update_company_data(self, company_portfolio_data) -> tuple[str, str]:
48
54
  # save company portfolio data
49
- company_portfolio_data = CompanyPortfolioData.objects.get_or_create(company=company)[0]
50
55
  if (
51
56
  invested_assets_under_management_usd := company_portfolio_data.get_assets_under_management_usd(
52
57
  self.val_date
@@ -55,19 +60,11 @@ class Updater:
55
60
  company_portfolio_data.invested_assets_under_management_usd = invested_assets_under_management_usd
56
61
  if (potential := company_portfolio_data.get_potential(self.val_date)) is not None:
57
62
  company_portfolio_data.potential = potential
58
- company_portfolio_data.save()
59
63
 
60
64
  # update the company object itself
61
- if (tier := company_portfolio_data.get_tiering(self.total_assets_under_management)) is not None:
62
- company.tier = tier
63
- company.customer_status = company_portfolio_data.get_customer_status()
64
- company.save()
65
-
66
- # def update_all_companies(self, val_date: date):
67
- # for company in tqdm(qs, total=qs.count()):
68
- # with suppress(CompanyPortfolioData.DoesNotExist):
69
- # company_portfolio = CompanyPortfolioData.objects.get(company=company)
70
- # company_portfolio.update_data(date.today())
65
+ tier = company_portfolio_data.get_tiering(self.total_assets_under_management)
66
+ customer_status = company_portfolio_data.get_customer_status()
67
+ return customer_status, tier
71
68
 
72
69
 
73
70
  class CompanyPortfolioData(models.Model):
@@ -104,14 +101,6 @@ class CompanyPortfolioData(models.Model):
104
101
  verbose_name="AUM",
105
102
  help_text="The Assets under Management (AUM) that is managed by this company or this person's primary employer.",
106
103
  )
107
- invested_assets_under_management_usd = models.DecimalField(
108
- max_digits=17,
109
- decimal_places=2,
110
- null=True,
111
- blank=True,
112
- help_text="The invested Assets under Management (AUM).",
113
- verbose_name="Invested AUM ($)",
114
- )
115
104
 
116
105
  investment_discretion = models.CharField(
117
106
  max_length=21,
@@ -120,10 +109,6 @@ class CompanyPortfolioData(models.Model):
120
109
  help_text="What discretion this company or this person's primary employer has to invest its assets.",
121
110
  verbose_name="Investment Discretion",
122
111
  )
123
-
124
- potential = models.DecimalField(
125
- decimal_places=2, max_digits=19, null=True, blank=True, help_text=potential_help_text
126
- )
127
112
  potential_currency = models.ForeignKey(
128
113
  to="currency.Currency",
129
114
  related_name="wbportfolio_potential_currencies",
@@ -132,6 +117,25 @@ class CompanyPortfolioData(models.Model):
132
117
  on_delete=models.PROTECT,
133
118
  )
134
119
 
120
+ # Dynamic fields
121
+ invested_assets_under_management_usd = models.DecimalField(
122
+ max_digits=17,
123
+ decimal_places=2,
124
+ null=True,
125
+ blank=True,
126
+ help_text="The invested Assets under Management (AUM).",
127
+ verbose_name="Invested AUM ($)",
128
+ )
129
+
130
+ potential = models.DecimalField(
131
+ decimal_places=2, max_digits=19, null=True, blank=True, help_text=potential_help_text
132
+ )
133
+
134
+ def update(self):
135
+ with suppress(CurrencyFXRates.DoesNotExist):
136
+ val_date = CurrencyFXRates.objects.latest("date").date
137
+ self.company.customer_status, self.company.tier = Updater(val_date).update_company_data(self)
138
+
135
139
  def get_assets_under_management_usd(self, val_date: date) -> Decimal:
136
140
  return Claim.objects.filter(status=Claim.Status.APPROVED).filter_for_customer(
137
141
  self.company
@@ -140,16 +144,17 @@ class CompanyPortfolioData(models.Model):
140
144
  )["invested_aum_usd"] or Decimal(0)
141
145
 
142
146
  def _get_default_potential(self, val_date: date) -> Decimal:
143
- with suppress(CurrencyFXRates.DoesNotExist):
144
- fx = CurrencyFXRates.objects.get(currency=self.assets_under_management_currency, date=val_date).value
147
+ if self.assets_under_management:
148
+ with suppress(CurrencyFXRates.DoesNotExist):
149
+ fx = CurrencyFXRates.objects.get(currency=self.assets_under_management_currency, date=val_date).value
145
150
 
146
- aum_usd = self.assets_under_management / fx
147
- aum_potential = Decimal(0)
148
- for asset_allocation in self.company.asset_allocations.all():
149
- aum_potential += aum_usd * asset_allocation.percent * asset_allocation.max_investment
150
- invested_aum = self.invested_assets_under_management_usd or Decimal(0.0)
151
+ aum_usd = self.assets_under_management / fx
152
+ aum_potential = Decimal(0)
153
+ for asset_allocation in self.company.asset_allocations.all():
154
+ aum_potential += aum_usd * asset_allocation.percent * asset_allocation.max_investment
155
+ invested_aum = self.invested_assets_under_management_usd or Decimal(0.0)
151
156
 
152
- return aum_potential - invested_aum
157
+ return aum_potential - invested_aum
153
158
 
154
159
  def get_potential(self, val_date: date) -> Decimal:
155
160
  if module_path := getattr(settings, "PORTFOLIO_COMPANY_DATA_POTENTIAL_METHOD", None):
@@ -225,11 +230,6 @@ class CompanyPortfolioData(models.Model):
225
230
  verbose_name_plural = "Company Portfolio Data"
226
231
 
227
232
 
228
- @receiver(post_save, sender="directory.Company")
229
- def create_company_portfolio_data(sender, instance, created, **kwargs):
230
- CompanyPortfolioData.objects.get_or_create(company=instance)
231
-
232
-
233
233
  class AssetAllocationType(WBModel):
234
234
  name = models.CharField(max_length=255)
235
235
  default_max_investment = models.DecimalField(
@@ -329,3 +329,33 @@ class GeographicFocus(models.Model):
329
329
  @classmethod
330
330
  def get_representation_label_key(cls):
331
331
  return "{{company}}: {{percent}} {{country}}"
332
+
333
+
334
+ @receiver(post_save, sender=AssetAllocation)
335
+ @receiver(post_save, sender=GeographicFocus)
336
+ def post_save_company_data(sender, instance, created, **kwargs):
337
+ company = instance.company
338
+ portfolio_data, created = CompanyPortfolioData.objects.get_or_create(company=company)
339
+ if not created:
340
+ portfolio_data.update()
341
+ portfolio_data.save()
342
+ portfolio_data.company.save()
343
+
344
+
345
+ @receiver(post_save, sender="directory.Company")
346
+ def handle_company_portfolio_data(sender, instance, created, **kwargs):
347
+ # create default asset allocation type (equity 50/50)
348
+ if not instance.asset_allocations.exists():
349
+ equity_asset_type = AssetAllocationType.objects.get_or_create(name="Equity")[0]
350
+ AssetAllocation.objects.create(
351
+ company=instance,
352
+ asset_type=equity_asset_type,
353
+ percent=0.5,
354
+ max_investment=0.5,
355
+ )
356
+ if created:
357
+ portfolio_data, created = CompanyPortfolioData.objects.get_or_create(company=instance)
358
+ if not created:
359
+ portfolio_data.update()
360
+ portfolio_data.save()
361
+ portfolio_data.company.save()
@@ -1,8 +1,11 @@
1
+ import logging
1
2
  import re
2
3
 
3
4
  from wbcore.contrib.currency.models import Currency
4
5
  from wbcore.contrib.directory.models import Company
5
6
 
7
+ logger = logging.getLogger("pms")
8
+
6
9
 
7
10
  def get_currency_and_assets_under_management(
8
11
  aum_string, currency_mapping, default_currency, multiplier_mapping, default_multiplier
@@ -72,5 +75,7 @@ def assign_aum():
72
75
  portfolio_data.assets_under_management_currency = currency
73
76
  try:
74
77
  portfolio_data.save()
75
- except Exception:
76
- pass
78
+ except Exception as e:
79
+ logger.error(
80
+ f"while we try to save the customer portfolio aum data for {company}, we encounter the error: {e}"
81
+ )
@@ -91,7 +91,7 @@ def update_portfolio_data(company_portfolio_data, portfolio_data):
91
91
 
92
92
  if investment_discretion := portfolio_data["investment_discretion"]:
93
93
  company_portfolio_data.investment_discretion = investment_discretion
94
-
94
+ company_portfolio_data.update()
95
95
  company_portfolio_data.save()
96
96
 
97
97
 
@@ -103,22 +103,11 @@ class CompanyPortfolioDataMixin(serializers.ModelSerializer):
103
103
  read_only=True,
104
104
  **_get_assets_under_management_kwargs(field_name="invested_assets_under_management_usd"),
105
105
  )
106
- investment_discretion = serializers.ChoiceField(**_get_investment_discretion_kwargs())
107
- assets_under_management_currency = serializers.PrimaryKeyRelatedField(
108
- required=False,
109
- queryset=Currency.objects.all(),
110
- default=None,
111
- label=CompanyPortfolioData.assets_under_management_currency.field.verbose_name,
112
- )
106
+
113
107
  assets_under_management_currency_repr = serializers.CharField(read_only=True, required=False)
114
- _assets_under_management_currency = CurrencyRepresentationSerializer(source="assets_under_management_currency")
108
+
115
109
  potential = serializers.DecimalField(**_get_potential_kwargs())
116
- potential_currency = serializers.PrimaryKeyRelatedField(
117
- required=False,
118
- queryset=Currency.objects.all(),
119
- label=CompanyPortfolioData.potential_currency.field.verbose_name,
120
- )
121
- _potential_currency = CurrencyRepresentationSerializer(source="potential_currency")
110
+ investment_discretion = serializers.ChoiceField(**_get_investment_discretion_kwargs())
122
111
 
123
112
  # Not sure why read_only_fields does not work...
124
113
  tier = serializers.ChoiceField(
@@ -144,10 +133,6 @@ class CompanyPortfolioDataMixin(serializers.ModelSerializer):
144
133
  "asset_under_management", # TODO: add an s after asset - After removing this field from the base model
145
134
  "assets_under_management_currency_repr",
146
135
  "invested_assets_under_management_usd",
147
- "potential_currency",
148
- "_potential_currency",
149
- "assets_under_management_currency",
150
- "_assets_under_management_currency",
151
136
  "investment_discretion",
152
137
  "potential",
153
138
  "tier",
@@ -156,6 +141,19 @@ class CompanyPortfolioDataMixin(serializers.ModelSerializer):
156
141
 
157
142
  class CompanyModelSerializer(CompanyPortfolioDataMixin, BaseCompanyModelSerializer):
158
143
  SERIALIZER_CLASS_FOR_REMOTE_ADDITIONAL_RESOURCES = BasePersonModelSerializer
144
+ assets_under_management_currency = serializers.PrimaryKeyRelatedField(
145
+ required=False,
146
+ queryset=Currency.objects.all(),
147
+ default=None,
148
+ label=CompanyPortfolioData.assets_under_management_currency.field.verbose_name,
149
+ )
150
+ _assets_under_management_currency = CurrencyRepresentationSerializer(source="assets_under_management_currency")
151
+ potential_currency = serializers.PrimaryKeyRelatedField(
152
+ required=False,
153
+ queryset=Currency.objects.all(),
154
+ label=CompanyPortfolioData.potential_currency.field.verbose_name,
155
+ )
156
+ _potential_currency = CurrencyRepresentationSerializer(source="potential_currency")
159
157
 
160
158
  def update(self, instance, validated_data):
161
159
  validated_data, portfolio_data = get_portfolio_data(validated_data)
@@ -209,6 +207,20 @@ class CompanyModelListSerializer(CompanyPortfolioDataMixin, BaseCompanyModelList
209
207
  class PersonModelSerializer(CompanyPortfolioDataMixin, BasePersonModelSerializer):
210
208
  SERIALIZER_CLASS_FOR_REMOTE_ADDITIONAL_RESOURCES = BasePersonModelSerializer
211
209
 
210
+ assets_under_management_currency = serializers.PrimaryKeyRelatedField(
211
+ required=False,
212
+ queryset=Currency.objects.all(),
213
+ default=None,
214
+ label=CompanyPortfolioData.assets_under_management_currency.field.verbose_name,
215
+ )
216
+ _assets_under_management_currency = CurrencyRepresentationSerializer(source="assets_under_management_currency")
217
+ potential_currency = serializers.PrimaryKeyRelatedField(
218
+ required=False,
219
+ queryset=Currency.objects.all(),
220
+ label=CompanyPortfolioData.potential_currency.field.verbose_name,
221
+ )
222
+ _potential_currency = CurrencyRepresentationSerializer(source="potential_currency")
223
+
212
224
  asset_under_management = serializers.DecimalField(
213
225
  **_get_assets_under_management_kwargs(field_name="assets_under_management"),
214
226
  read_only=True,
@@ -243,11 +255,9 @@ class PersonModelListSerializer(CompanyPortfolioDataMixin, BasePersonModelListSe
243
255
 
244
256
 
245
257
  class AssetAllocationTypeRepresentationSerializer(serializers.RepresentationSerializer):
246
- _detail = serializers.HyperlinkField(reverse_name="company_portfolio:assetallocationtyperepresentation-detail")
247
-
248
258
  class Meta:
249
259
  model = AssetAllocationType
250
- fields = ("id", "name", "_detail")
260
+ fields = ("id", "name")
251
261
 
252
262
 
253
263
  class AssetAllocationTypeModelSerializer(serializers.ModelSerializer):
@@ -19,5 +19,16 @@ def update_all_portfolio_data(val_date: date | None = None):
19
19
  has_account=Exists(Account.objects.filter(owner=OuterRef("pk"))),
20
20
  has_portfolio_data=Exists(CompanyPortfolioData.objects.filter(company=OuterRef("pk"))),
21
21
  )
22
+ company_objs = []
23
+ portfolio_data_objs = []
22
24
  for company in tqdm(qs, total=qs.count()):
23
- updater.update_company_data(company)
25
+ portfolio_data = CompanyPortfolioData.objects.get_or_create(company=company)[0]
26
+ company.customer_status, company.tier = updater.update_company_data(portfolio_data)
27
+ portfolio_data_objs.append(portfolio_data)
28
+ company_objs.append(company)
29
+ if company_objs:
30
+ Company.objects.bulk_update(company_objs, ["customer_status", "tier"])
31
+ if portfolio_data_objs:
32
+ CompanyPortfolioData.objects.bulk_update(
33
+ portfolio_data_objs, ["invested_assets_under_management_usd", "potential"]
34
+ )
@@ -60,7 +60,7 @@ from wbportfolio.factories import (
60
60
  ProductGroupRepresentantFactory,
61
61
  ProductPortfolioRoleFactory,
62
62
  TradeFactory,
63
- TradeProposalFactory,
63
+ OrderProposalFactory,
64
64
  WhiteLabelProductFactory,
65
65
  )
66
66
 
@@ -95,7 +95,7 @@ register(ModelPortfolioFactory)
95
95
  register(ModelPortfolioWithBaseProductFactory, "model_portfolio_with_base_product")
96
96
  register(TradeFactory)
97
97
  register(CustomerTradeFactory)
98
- register(TradeProposalFactory)
98
+ register(OrderProposalFactory)
99
99
  register(DividendTransactionsFactory)
100
100
  register(FeesFactory)
101
101
  register(WhiteLabelProductFactory, "white_label_product")
@@ -1,4 +1,4 @@
1
- from wbfdm.models import Cash, InstrumentPrice
1
+ from wbfdm.models import InstrumentPrice
2
2
 
3
3
  from wbportfolio.models import FeeProductPercentage, Fees, Product
4
4
 
@@ -8,7 +8,6 @@ def fees_calculation(price_id):
8
8
  if price.calculated:
9
9
  raise ValueError("Cannot compute fees on calculated price")
10
10
  currency = price.instrument.currency
11
- underlying_instrument = Cash.objects.filter(currency=currency).first()
12
11
  product = Product.objects.get(id=price.instrument.id)
13
12
  portfolio = product.portfolio
14
13
  previous_price = price.previous_price
@@ -54,40 +53,33 @@ def fees_calculation(price_id):
54
53
  performance_fees_gross = product_gross_performance_fees * multiplicator
55
54
  base_fields = [
56
55
  "total_value",
57
- "total_value_fx_portfolio",
58
56
  "total_value_gross",
59
- "total_value_gross_fx_portfolio",
60
57
  ]
61
58
  yield {
62
59
  "portfolio": portfolio,
63
- "linked_product": product,
64
- "transaction_date": price.date,
60
+ "product": product,
61
+ "fee_date": price.date,
65
62
  "transaction_subtype": Fees.Type.MANAGEMENT,
66
- "underlying_instrument": underlying_instrument,
67
63
  "currency": currency,
68
64
  "calculated": True,
69
65
  **{f: management_fees for f in base_fields},
70
66
  }
71
67
  yield {
72
68
  "portfolio": portfolio,
73
- "linked_product": product,
74
- "transaction_date": price.date,
69
+ "product": product,
70
+ "fee_date": price.date,
75
71
  "transaction_subtype": Fees.Type.ISSUER,
76
- "underlying_instrument": underlying_instrument,
77
72
  "currency": currency,
78
73
  "calculated": True,
79
74
  **{f: bank_fees for f in base_fields},
80
75
  }
81
76
  yield {
82
77
  "portfolio": portfolio,
83
- "linked_product": product,
84
- "transaction_date": price.date,
78
+ "product": product,
79
+ "fee_date": price.date,
85
80
  "transaction_subtype": Fees.Type.PERFORMANCE,
86
- "underlying_instrument": underlying_instrument,
87
81
  "currency": currency,
88
82
  "calculated": True,
89
83
  "total_value": performance_fees_net,
90
- "total_value_fx_portfolio": performance_fees_net,
91
84
  "total_value_gross": performance_fees_gross,
92
- "total_value_gross_fx_portfolio": performance_fees_gross,
93
85
  }
@@ -21,7 +21,7 @@ from .product_groups import ProductGroupFactory, ProductGroupRepresentantFactory
21
21
  from .products import IndexProductFactory, ProductFactory, WhiteLabelProductFactory, ModelPortfolioWithBaseProductFactory
22
22
  from .reconciliations import AccountReconciliationFactory, AccountReconciliationLineFactory
23
23
  from .roles import ManagerPortfolioRoleFactory, ProductPortfolioRoleFactory
24
- from .trades import CustomerTradeFactory, TradeFactory, TradeProposalFactory
25
- from .transactions import TransactionFactory
24
+ from .trades import CustomerTradeFactory, TradeFactory
25
+ from .orders import OrderProposalFactory, OrderFactory
26
26
  from .indexes import IndexFactory
27
27
  from .rebalancing import (RebalancingModelFactory, RebalancerFactory)
@@ -37,7 +37,7 @@ class AssetPositionFactory(factory.django.DjangoModelFactory):
37
37
  initial_price = factory.Faker("pydecimal", min_value=100, max_value=120, right_digits=4)
38
38
  underlying_quote_price = factory.LazyAttribute(
39
39
  lambda o: InstrumentPriceFactory.create(
40
- instrument=o.underlying_instrument or o.underlying_quote,
40
+ instrument=o.underlying_quote or o.underlying_instrument,
41
41
  calculated=False,
42
42
  date=o.date,
43
43
  net_value=o.initial_price,
@@ -4,13 +4,18 @@ import factory
4
4
 
5
5
  from wbportfolio.models import DividendTransaction
6
6
 
7
- from .transactions import TransactionFactory
8
7
 
9
-
10
- class DividendTransactionsFactory(TransactionFactory):
8
+ class DividendTransactionsFactory(factory.django.DjangoModelFactory):
11
9
  class Meta:
12
10
  model = DividendTransaction
13
11
 
12
+ currency_fx_rate = 1.0
13
+ portfolio = factory.SubFactory("wbportfolio.factories.PortfolioFactory")
14
+ underlying_instrument = factory.SubFactory("wbfdm.factories.InstrumentFactory")
15
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
16
+ value_date = factory.Faker("date_object")
17
+ ex_date = factory.Faker("date_object")
18
+ record_date = factory.LazyAttribute(lambda o: o.ex_date)
14
19
  retrocession = 1.0
15
20
  shares = factory.LazyAttribute(lambda o: random.randint(10, 10000))
16
21
  price = factory.LazyAttribute(lambda o: random.randint(10, 10000))
@@ -1,16 +1,20 @@
1
+ import random
2
+
1
3
  import factory
2
4
  from faker import Faker
3
5
 
4
6
  from wbportfolio.models import Fees
5
7
 
6
- from .transactions import TransactionFactory
7
-
8
8
  faker = Faker()
9
9
 
10
10
 
11
- class FeesFactory(TransactionFactory):
11
+ class FeesFactory(factory.django.DjangoModelFactory):
12
12
  class Meta:
13
13
  model = Fees
14
14
 
15
+ currency_fx_rate = 1.0
16
+ fee_date = factory.Faker("date_object")
17
+ total_value = factory.LazyAttribute(lambda o: random.randint(1, 1000))
15
18
  transaction_subtype = factory.Faker("random_element", elements=[x[0] for x in Fees.Type.choices])
16
- linked_product = factory.SubFactory("wbportfolio.factories.ProductFactory")
19
+ product = factory.SubFactory("wbportfolio.factories.products.ProductFactory")
20
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
@@ -0,0 +1,2 @@
1
+ from .order_proposals import OrderProposalFactory
2
+ from .orders import OrderFactory
@@ -0,0 +1,21 @@
1
+ import factory
2
+ from faker import Faker
3
+ from pandas._libs.tslibs.offsets import BDay
4
+ from wbcore.contrib.authentication.factories import UserFactory
5
+ from wbcore.contrib.currency.factories import CurrencyFactory
6
+
7
+ from wbportfolio.factories import PortfolioFactory
8
+ from wbportfolio.models import OrderProposal
9
+
10
+ fake = Faker()
11
+
12
+
13
+ class OrderProposalFactory(factory.django.DjangoModelFactory):
14
+ class Meta:
15
+ model = OrderProposal
16
+
17
+ trade_date = factory.LazyAttribute(lambda o: (fake.date_object() + BDay(1)).date())
18
+ comment = factory.Faker("paragraph")
19
+ portfolio = factory.LazyAttribute(lambda o: PortfolioFactory.create(currency=CurrencyFactory.create(key="USD")))
20
+ creator = factory.LazyAttribute(lambda o: UserFactory.create().profile)
21
+ approver = factory.LazyAttribute(lambda o: UserFactory.create().profile)
@@ -0,0 +1,34 @@
1
+ from decimal import Decimal
2
+
3
+ import factory
4
+ from faker import Faker
5
+ from wbfdm.factories import InstrumentPriceFactory
6
+
7
+ from wbportfolio.models import Order
8
+
9
+ fake = Faker()
10
+
11
+
12
+ class OrderFactory(factory.django.DjangoModelFactory):
13
+ class Meta:
14
+ model = Order
15
+
16
+ order_proposal = factory.SubFactory("wbportfolio.factories.OrderProposalFactory")
17
+ currency_fx_rate = Decimal(1.0)
18
+ fees = Decimal(0.0)
19
+ underlying_instrument = factory.SubFactory("wbfdm.factories.InstrumentFactory")
20
+ shares = factory.Faker("pydecimal", min_value=10, max_value=1000, right_digits=4)
21
+
22
+ @factory.post_generation
23
+ def create_price(self, create, extracted, **kwargs):
24
+ if create:
25
+ if self.price:
26
+ p = InstrumentPriceFactory.create(
27
+ instrument=self.underlying_instrument, date=self.value_date, calculated=False, net_value=self.price
28
+ )
29
+ else:
30
+ p = InstrumentPriceFactory.create(
31
+ instrument=self.underlying_instrument, date=self.value_date, calculated=False
32
+ )
33
+ self.price = p.net_value
34
+ self.save()
@@ -15,10 +15,11 @@ class PortfolioFactory(factory.django.DjangoModelFactory):
15
15
  model = Portfolio
16
16
 
17
17
  name = factory.Sequence(lambda n: f"Portfolio {n}")
18
- currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
18
+ currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyUSDFactory")
19
19
  is_manageable = True
20
20
  is_tracked = True
21
21
  is_lookthrough = False
22
+ only_weighting = True
22
23
  invested_timespan = DateRange(date.min, date.max)
23
24
 
24
25
  @factory.post_generation
@@ -21,10 +21,10 @@ class ProductGroupFactory(InstrumentFactory):
21
21
  paying_agent = factory.SubFactory("wbcore.contrib.directory.factories.entries.CompanyFactory")
22
22
 
23
23
  @factory.post_generation
24
- def create_initial_portfolio(product_group, *args, **kwargs):
25
- if product_group.id and not product_group.portfolios.exists():
24
+ def create_initial_portfolio(self, *args, **kwargs):
25
+ if self.id and not self.portfolios.exists():
26
26
  portfolio = PortfolioFactory.create()
27
- InstrumentPortfolioThroughModel.objects.create(instrument=product_group, portfolio=portfolio)
27
+ InstrumentPortfolioThroughModel.objects.create(instrument=self, portfolio=portfolio)
28
28
 
29
29
 
30
30
  class ProductGroupRepresentantFactory(factory.django.DjangoModelFactory):
@@ -40,10 +40,10 @@ class ProductFactory(InstrumentFactory):
40
40
  instrument_type = factory.LazyAttribute(lambda o: InstrumentTypeFactory.create(name="Product", key="product"))
41
41
 
42
42
  @factory.post_generation
43
- def create_initial_portfolio(product, *args, **kwargs):
44
- if product.id and not product.portfolios.exists():
43
+ def create_initial_portfolio(self, *args, **kwargs):
44
+ if self.id and not self.portfolios.exists():
45
45
  portfolio = PortfolioFactory.create()
46
- InstrumentPortfolioThroughModel.objects.create(instrument=product, portfolio=portfolio)
46
+ InstrumentPortfolioThroughModel.objects.create(instrument=self, portfolio=portfolio)
47
47
 
48
48
  # wbportfolio = factory.SubFactory(PortfolioFactory)
49
49
  # portfolio_computed = factory.SubFactory(PortfolioFactory)
@@ -18,6 +18,6 @@ class RebalancerFactory(factory.django.DjangoModelFactory):
18
18
  portfolio = factory.SubFactory("wbportfolio.factories.portfolios.PortfolioFactory")
19
19
  rebalancing_model = factory.SubFactory(RebalancingModelFactory)
20
20
  parameters = dict()
21
- approve_trade_proposal_automatically = False
21
+ apply_order_proposal_automatically = False
22
22
  frequency = "RRULE:FREQ=MONTHLY;"
23
23
  activation_date = None