wbportfolio 1.43.2__py2.py3-none-any.whl → 1.44.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wbportfolio might be problematic. Click here for more details.

@@ -4,7 +4,10 @@ from decimal import Decimal
4
4
  from itertools import chain
5
5
  from typing import Any, Dict, List, Optional
6
6
 
7
+ from django.core.exceptions import ObjectDoesNotExist
7
8
  from django.db import models
9
+ from contextlib import suppress
10
+
8
11
  from wbcore.contrib.authentication.authentication import User
9
12
  from wbcore.contrib.currency.import_export.handlers import CurrencyImportHandler
10
13
  from wbcore.contrib.io.exceptions import DeserializationError
@@ -145,23 +148,23 @@ class AssetPositionImportHandler(ImportExportHandler):
145
148
  for val_date in sorted(dates):
146
149
  trigger_portfolio_change_as_task.delay(portfolio.id, val_date, recompute_weighting=True)
147
150
 
148
- portfolios = set([(obj.portfolio, obj.date) for obj in chain(created_objs, modified_objs)])
149
-
150
- for portfolio, val_date in portfolios:
151
- custodian = portfolio.dependency_through.filter(type="CUSTODIAN")
152
- if custodian.exists():
153
- differences = portfolio.check_related_portfolio_at_date(
154
- val_date, custodian.first().dependency_portfolio
155
- )
156
- if differences.exists():
157
- for user in User.objects.filter(
158
- profile_id__in=PortfolioRole.portfolio_managers().values_list("person", flat=True)
159
- ):
160
- send_notification(
161
- code="wbportfolio.portfolio.check_custodian_portfolio",
162
- title="There is a discrepency between two portfolios",
163
- body=f"There has been a discrepency between two portfolios: {portfolio} and {custodian.first().dependency_portfolio}.",
164
- user=user,
165
- reverse_name="wbportfolio:portfolio-modelcompositionpandas-list",
166
- reverse_args=[portfolio.id],
167
- )
151
+ # check if portfolio as custodian
152
+ latest_date = max(dates)
153
+ with suppress(ObjectDoesNotExist):
154
+ if (custodian_rel := portfolio.dependency_through.get(type="CUSTODIAN")) and portfolio.assets.latest(
155
+ "date"
156
+ ).date >= latest_date:
157
+ custodian_portfolio = custodian_rel.dependency_portfolio
158
+ differences = portfolio.check_related_portfolio_at_date(latest_date, custodian_portfolio)
159
+ if differences.exists():
160
+ for user in User.objects.filter(
161
+ profile_id__in=PortfolioRole.portfolio_managers().values_list("person", flat=True)
162
+ ):
163
+ send_notification(
164
+ code="wbportfolio.portfolio.check_custodian_portfolio",
165
+ title="There is a discrepency between two portfolios",
166
+ body=f"There has been a discrepency between two portfolios: {portfolio} and {custodian_portfolio}.",
167
+ user=user,
168
+ reverse_name="wbportfolio:portfolio-modelcompositionpandas-list",
169
+ reverse_args=[portfolio.id],
170
+ )
@@ -51,7 +51,7 @@ class RuleBackend(backend.AbstractRuleBackend):
51
51
  return RuleBackendSerializer
52
52
 
53
53
  def _process_dto(self, product: Product, **kwargs) -> Generator[backend.IncidentResult, None, None]:
54
- for lag_threshold in self.thresholds:
54
+ for lag_threshold in reversed(self.thresholds):
55
55
  numerical_range = lag_threshold.numerical_range
56
56
 
57
57
  last_asset_position_date = (
@@ -77,35 +77,33 @@ class RuleBackend(backend.AbstractRuleBackend):
77
77
  )
78
78
  - 1
79
79
  )
80
-
80
+ breached_data_type = []
81
81
  if (
82
82
  self.DataTypeChoices.ASSET_POSITION.value in self.data_type
83
83
  and asset_position_lag >= numerical_range[0]
84
84
  and asset_position_lag < numerical_range[1]
85
85
  ):
86
- yield backend.IncidentResult(
87
- breached_object=product,
88
- breached_object_repr=str(product),
89
- breached_value=str(asset_position_lag),
90
- report_details={
91
- "Last Datapoint": f"{last_asset_position_date:%d.%m.%Y}",
92
- "Data Type": "Asset Position",
93
- },
94
- severity=lag_threshold.severity,
95
- )
86
+ breached_data_type.append("Asset Position")
96
87
  if (
97
88
  self.DataTypeChoices.INSTRUMENT_PRICE.value in self.data_type
98
89
  and instrument_price_lag >= numerical_range[0]
99
90
  and instrument_price_lag < numerical_range[1]
100
91
  ):
92
+ breached_data_type.append("Valuation")
93
+
94
+ if len(breached_data_type) > 0:
95
+ max_lag = max([asset_position_lag, instrument_price_lag])
101
96
  yield backend.IncidentResult(
102
97
  breached_object=product,
103
98
  breached_object_repr=str(product),
104
- breached_value=str(instrument_price_lag),
99
+ breached_value=str(max_lag),
105
100
  report_details={
106
- "Lag": instrument_price_lag,
107
- "Last Datapoint": f"{last_instrument_price_date:%d.%m.%Y}",
108
- "Data Type": "Valuation",
101
+ "Lag": max_lag,
102
+ "Last Valuation Datapoint": f"{last_instrument_price_date:%d.%m.%Y}",
103
+ "Last Asset Position Datapoint": f"{last_asset_position_date:%d.%m.%Y}",
104
+ "Valuation Lag": instrument_price_lag,
105
+ "Asset Position Lag": asset_position_lag,
109
106
  },
110
107
  severity=lag_threshold.severity,
111
108
  )
109
+ break
@@ -33,23 +33,16 @@ class TestProductRuleModel:
33
33
  asset_position_factory.create(date=weekday - BDay(2), portfolio=product.portfolio, is_estimated=False)
34
34
  instrument_price_factory.create(date=weekday - BDay(1), instrument=product, calculated=False)
35
35
 
36
- res = list(product_backend.check_rule())
37
- low_severity = list(filter(lambda x: x.severity.name == "LOW", res))[0]
38
- high_severity = list(filter(lambda x: x.severity.name == "HIGH", res))[0]
39
-
40
- assert high_severity.report_details["Data Type"] == "Asset Position"
41
- assert low_severity.report_details["Data Type"] == "Valuation"
42
- assert high_severity.report_details["Last Datapoint"] == f"{(weekday - BDay(2)).date():%d.%m.%Y}"
43
- assert low_severity.report_details["Last Datapoint"] == f"{(weekday - BDay(1)).date():%d.%m.%Y}"
44
-
45
- instrument_price_factory.create(date=weekday, instrument=product, calculated=False)
46
- asset_position_factory.create(date=weekday - BDay(1), portfolio=product.portfolio, is_estimated=False)
47
-
48
36
  res = list(product_backend.check_rule())
49
37
  assert len(res) == 1
50
- assert res[0].report_details["Data Type"] == "Asset Position"
51
- assert res[0].report_details["Last Datapoint"] == f"{(weekday - BDay(1)).date():%d.%m.%Y}"
38
+ incident = res[0]
39
+ assert incident.severity.name == "HIGH"
40
+ assert incident.breached_value == str(2)
41
+ assert incident.report_details["Lag"] == 2
42
+ assert incident.report_details["Last Asset Position Datapoint"] == f"{(weekday - BDay(2)).date():%d.%m.%Y}"
43
+ assert incident.report_details["Last Valuation Datapoint"] == f"{(weekday - BDay(1)).date():%d.%m.%Y}"
52
44
 
45
+ instrument_price_factory.create(date=weekday, instrument=product, calculated=False)
53
46
  asset_position_factory.create(date=weekday, portfolio=product.portfolio, is_estimated=False)
54
47
  res = list(product_backend.check_rule())
55
48
  assert len(res) == 0
@@ -14,12 +14,12 @@ class ESGMetricAggregationPortfolioPandasDisplayConfig(DisplayViewConfig):
14
14
  label="Portfolio Data",
15
15
  children=[
16
16
  dp.Field(
17
- key="underlying_instrument_repr", label="Name", width=Unit.PIXEL(300), lock_position=True
18
- ),
19
- dp.Field(key="weighting", label="Weight", width=Unit.PIXEL(100), lock_position=True),
20
- dp.Field(
21
- key="total_value_fx_usd", label="Total Asset (USD)", width=Unit.PIXEL(150), lock_position=True
17
+ key="underlying_instrument_repr",
18
+ label="Name",
19
+ width=Unit.PIXEL(300),
22
20
  ),
21
+ dp.Field(key="weighting", label="Weight", width=Unit.PIXEL(100)),
22
+ dp.Field(key="total_value_fx_usd", label="Total Asset (USD)", width=Unit.PIXEL(150)),
23
23
  ],
24
24
  )
25
25
  ]
@@ -28,15 +28,12 @@ class ESGMetricAggregationPortfolioPandasDisplayConfig(DisplayViewConfig):
28
28
  key="esg_data",
29
29
  label=self.view.esg_aggregation.get_esg_code().name,
30
30
  width=Unit.PIXEL(150),
31
- lock_position=True,
32
31
  )
33
32
  ]
34
33
  for extra in self.view.dataloader.extra_esg_data_logs:
35
- esg_data_fields.append(
36
- dp.Field(key=extra.series.name, label=extra.label, width=Unit.PIXEL(150), lock_position=True)
37
- )
34
+ esg_data_fields.append(dp.Field(key=extra.series.name, label=extra.label, width=Unit.PIXEL(150)))
38
35
  fields.append(
39
- dp.Field(key=None, label="ESG Data", children=esg_data_fields, lock_position=True),
36
+ dp.Field(key=None, label="ESG Data", children=esg_data_fields),
40
37
  )
41
38
  if self.view.dataloader.intermediary_logs:
42
39
  intermediary_fields = []
@@ -47,7 +44,6 @@ class ESGMetricAggregationPortfolioPandasDisplayConfig(DisplayViewConfig):
47
44
  key=intermediary.series.name,
48
45
  label=intermediary.label,
49
46
  width=Unit.PIXEL(100),
50
- lock_position=True,
51
47
  )
52
48
  if intermediary.group:
53
49
  intermediary_groups[intermediary.group].append(field)
@@ -58,29 +54,23 @@ class ESGMetricAggregationPortfolioPandasDisplayConfig(DisplayViewConfig):
58
54
  key=intermediary.series.name,
59
55
  label=intermediary.label,
60
56
  width=Unit.PIXEL(150),
61
- lock_position=True,
62
57
  )
63
58
  )
64
59
  for group_name, intermediary_groups_fields in intermediary_groups.items():
65
- intermediary_fields.append(
66
- dp.Field(key=None, label=group_name, children=intermediary_groups_fields, lock_position=True)
67
- )
60
+ intermediary_fields.append(dp.Field(key=None, label=group_name, children=intermediary_groups_fields))
68
61
  fields.append(
69
- dp.Field(key=None, label="Intermediary", children=intermediary_fields, lock_position=True),
62
+ dp.Field(key=None, label="Intermediary", children=intermediary_fields),
70
63
  )
71
64
  fields.append(
72
65
  dp.Field(
73
66
  key=None,
74
67
  label="Metric",
75
68
  children=[
76
- dp.Field(
77
- key="metric", label=self.view.esg_aggregation.value, width=Unit.PIXEL(150), lock_position=True
78
- ),
69
+ dp.Field(key="metric", label=self.view.esg_aggregation.value, width=Unit.PIXEL(150)),
79
70
  dp.Field(
80
71
  key="weights_in_coverage",
81
72
  label="Weight in Coverage",
82
73
  width=Unit.PIXEL(100),
83
- lock_position=True,
84
74
  ),
85
75
  ],
86
76
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.43.2
3
+ Version: 1.44.1
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*
@@ -114,7 +114,7 @@ wbportfolio/import_export/backends/wbfdm/dividend.py,sha256=iAQXnYPXmtG_Jrc8THAJ
114
114
  wbportfolio/import_export/backends/wbfdm/mixin.py,sha256=JNtjgqGLson1nu_Chqb8MWyuiF3Ws8ox2vapxIRBYKE,400
115
115
  wbportfolio/import_export/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
116
  wbportfolio/import_export/handlers/adjustment.py,sha256=6bdTIYFmc8_HFxcdwtnYwglMyCfAD8XrTIrEb2zWY0g,1757
117
- wbportfolio/import_export/handlers/asset_position.py,sha256=wqSuRb4uiTIrZh1v23JLNdn_Ixo0biZAgFhaJU_orC8,8496
117
+ wbportfolio/import_export/handlers/asset_position.py,sha256=fj9ywxvfQfyQi7RZC5NmeKb4hb18atx0P8QHmnrc0Ks,8692
118
118
  wbportfolio/import_export/handlers/dividend.py,sha256=tftdVdAzNpKSSvouOtvJfzWL362HUPIC94F6Noha8CE,3998
119
119
  wbportfolio/import_export/handlers/fees.py,sha256=lZvxPBDHa3cmmQ4KJwBSPWfUXKdj1X8CDfNTpGQwJcI,2823
120
120
  wbportfolio/import_export/handlers/portfolio_cash_flow.py,sha256=WsDAJoBvZ_Lw4X88jYA7JbM3h7nxP-K1qT6z0AIEyy4,2732
@@ -292,7 +292,7 @@ wbportfolio/risk_management/backends/instrument_list_portfolio.py,sha256=TD8xt9H
292
292
  wbportfolio/risk_management/backends/liquidity_risk.py,sha256=KNumGhZVP82tjO_IyqTVZTLGkJFtEOQUEQsVj7ZNK0o,3687
293
293
  wbportfolio/risk_management/backends/liquidity_stress_instrument.py,sha256=PusnD3Xkmsm2E75LDWB2E9JMJikD1bX86XlgWCnr3d4,3611
294
294
  wbportfolio/risk_management/backends/mixins.py,sha256=79azRuBxCSDmLOOwOwRiAvHzsGe79K0lJW9yX6ze8sQ,9452
295
- wbportfolio/risk_management/backends/product_integrity.py,sha256=M6YHCXIeLcovNvcH46s5QCTMWoqaTHZpkaq3LaFVga0,4379
295
+ wbportfolio/risk_management/backends/product_integrity.py,sha256=4U_dodoZfRtuC9WaIsHc1u_9pvixV3ReRpbK4-72HB8,4366
296
296
  wbportfolio/risk_management/backends/stop_loss_instrument.py,sha256=-RhvKJzzy1_Wd5X97ZTN-Ou8nOSc0dfp9mWAmgNS5tE,1127
297
297
  wbportfolio/risk_management/backends/stop_loss_portfolio.py,sha256=nEx5U6ao-qptuPMb75MjwFSxY9d1oLBa8LAaGOIN6jY,1673
298
298
  wbportfolio/risk_management/backends/ucits_portfolio.py,sha256=lnQNIngc9Tsni6irYIqTP_IERS1ErLlA5Te1MYsDeRw,3365
@@ -303,7 +303,7 @@ wbportfolio/risk_management/tests/test_controversy_portfolio.py,sha256=EMOn5VLNP
303
303
  wbportfolio/risk_management/tests/test_exposure_portfolio.py,sha256=PUt7ZkHKSXQ79utA-wmBjP4Onp0r8Aggosk2tgvYgVk,3511
304
304
  wbportfolio/risk_management/tests/test_instrument_list_portfolio.py,sha256=b5xiFc3O5pZ2Rsnrr6qtWPxGOpwPU7g-WiAIOUq2B3o,2762
305
305
  wbportfolio/risk_management/tests/test_liquidity_risk.py,sha256=cPBk0trd-Eo6bypPDNNERVM7dQGstcci2kmn_hd6_ss,1712
306
- wbportfolio/risk_management/tests/test_product_integrity.py,sha256=bww5UA6IPDwHaRNxqhuavUoMDDwZfq9E5Qj4RhpWl-U,2410
306
+ wbportfolio/risk_management/tests/test_product_integrity.py,sha256=5xxXVvrGxdkrA7Tio7lojzva-FKe4zIe5GwqSjKqJsE,1958
307
307
  wbportfolio/risk_management/tests/test_stop_loss_instrument.py,sha256=Crt8dQjPKvmvgdd7_OMGchTHwcV8zP5-wun5P0W_-aw,4196
308
308
  wbportfolio/risk_management/tests/test_stop_loss_portfolio.py,sha256=VCsawHAVfSHvG13bFANZzJRuKfcrxVZUKsbbXEweSpY,4594
309
309
  wbportfolio/risk_management/tests/test_ucits_portfolio.py,sha256=zZLBILyWGyiYs7gsVanXWCd1i699-PujDiunj_vfkHo,1806
@@ -422,7 +422,7 @@ wbportfolio/viewsets/configs/display/adjustments.py,sha256=L91jfYhGT7g1QyK3M1f4F
422
422
  wbportfolio/viewsets/configs/display/assets.py,sha256=3rZJgLZ26fusN2Ik3O4TnXhpoGl8vSQ3BCCp6Xyyok8,10932
423
423
  wbportfolio/viewsets/configs/display/claim.py,sha256=w3CXiU2qEgyVY7NSjfsM6rF9tTm1rEmJXFWAVwTkRCw,12040
424
424
  wbportfolio/viewsets/configs/display/custodians.py,sha256=R-tnktfY48K-8Zz-Fg_M6IXKHv42J1ZeSpom7lvYVS8,801
425
- wbportfolio/viewsets/configs/display/esg.py,sha256=mdjTjYn_ujpPdxON3Q5CserCgrZ1d9liSt_9Ll0fNJQ,3550
425
+ wbportfolio/viewsets/configs/display/esg.py,sha256=W8uetCPN2TjHtU2kvQjKOmkq7uoaYvzX2iLN6Nz78Pk,3111
426
426
  wbportfolio/viewsets/configs/display/fees.py,sha256=_8HDJADh5bA5VMVPkhKObPmNNdPHWR8Oz23PEgd04F0,5834
427
427
  wbportfolio/viewsets/configs/display/portfolio_cash_flow.py,sha256=CS_UGhKZSXnX-0SZ0RXzbG1WMJm6NX3GUljwIa4RWBk,5025
428
428
  wbportfolio/viewsets/configs/display/portfolio_relationship.py,sha256=8DvsYWFCX29qj5wUf1wCtjlPwzKRd_E7JDuo_CopaJ0,1294
@@ -500,7 +500,7 @@ wbportfolio/viewsets/transactions/mixins.py,sha256=i9ICaUXZfryIrbgS-bdCcoBJO-pTn
500
500
  wbportfolio/viewsets/transactions/trade_proposals.py,sha256=5mgAk60cINZHd2HfTu7StFOc882FGakNcIVZpNSwBCs,3528
501
501
  wbportfolio/viewsets/transactions/trades.py,sha256=_rZuPRDjf3MbwZ5xvdCgczCxcIbvI-RvyAf-50Y_Ga4,14744
502
502
  wbportfolio/viewsets/transactions/transactions.py,sha256=VgSBUs1g5etsXjt7zi4wQi82XwOGuHsd_zppnsbMZMs,4517
503
- wbportfolio-1.43.2.dist-info/METADATA,sha256=0-P8ZfUk518ciioDACBj8ooCJD2jK6xOML07WDCCVDg,645
504
- wbportfolio-1.43.2.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
505
- wbportfolio-1.43.2.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
506
- wbportfolio-1.43.2.dist-info/RECORD,,
503
+ wbportfolio-1.44.1.dist-info/METADATA,sha256=C8XJhmd9qFLEqDHA1Gjxx9smfA8wIKu9PXyCSyr6xDg,645
504
+ wbportfolio-1.44.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
505
+ wbportfolio-1.44.1.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
506
+ wbportfolio-1.44.1.dist-info/RECORD,,