wbportfolio 1.56.1__py2.py3-none-any.whl → 1.56.3__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.

@@ -12,3 +12,5 @@ class OrderProposalAdmin(admin.ModelAdmin):
12
12
  list_display = ("portfolio", "rebalancing_model", "trade_date", "status")
13
13
  autocomplete_fields = ["portfolio", "rebalancing_model"]
14
14
  inlines = [OrderTabularInline]
15
+
16
+ raw_id_fields = ["portfolio", "creator", "approver"]
@@ -28,3 +28,5 @@ class OrderTabularInline(admin.TabularInline):
28
28
  "shares",
29
29
  "daily_return",
30
30
  ]
31
+
32
+ raw_id_fields = ["underlying_instrument"]
@@ -338,7 +338,9 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
338
338
  def prepare_orders_for_execution(self) -> list[OrderDTO]:
339
339
  executable_orders = []
340
340
  for order in (
341
- self.get_orders().exclude(underlying_instrument__is_cash=True).select_related("underlying_instrument")
341
+ self.get_orders()
342
+ .exclude(models.Q(underlying_instrument__is_cash=True) | (models.Q(weighting=0) & models.Q(shares=0)))
343
+ .select_related("underlying_instrument")
342
344
  ):
343
345
  instrument = order.underlying_instrument
344
346
  # we support only the instrument type provided by the Order DTO class
@@ -459,7 +461,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
459
461
  positions.append(
460
462
  PositionDTO(
461
463
  underlying_instrument=instrument.id,
462
- instrument_type=instrument.instrument_type.id,
464
+ instrument_type=instrument.security_instrument_type.id,
463
465
  weighting=weighting,
464
466
  daily_return=daily_return if use_effective else Decimal("0"),
465
467
  shares=row["shares"],
@@ -898,11 +900,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
898
900
 
899
901
  Order.objects.bulk_update(orders, ["shares", "weighting", "desired_target_weight"])
900
902
 
901
- # If we estimate cash on this order proposal, we make sure to create the corresponding cash component
902
- estimated_cash_position = self.get_estimated_target_cash()
903
- target_portfolio = self.validated_trading_service.trades_batch.convert_to_portfolio(
904
- estimated_cash_position._build_dto()
905
- )
903
+ target_portfolio = self.convert_to_portfolio()
906
904
  self.evaluate_active_rules(self.trade_date, target_portfolio, asynchronously=True)
907
905
  self.total_cash_weight = Decimal("1") - total_target_weight
908
906
  return orders_validation_warnings
@@ -28,10 +28,10 @@ def execute_orders(order_proposal: "OrderProposal") -> tuple[ExecutionStatus, st
28
28
  orders = order_proposal.prepare_orders_for_execution()
29
29
  as_draft = _should_route_as_draft()
30
30
  adapter = order_proposal.custodian_adapter
31
- order_confirmations, rebalancing_comment = adapter.submit_rebalancing(orders, as_draft=as_draft)
31
+ confirmed_orders, rebalancing_comment = adapter.submit_rebalancing(orders, as_draft=as_draft)
32
32
  leftover_orders = order_proposal.orders.all()
33
33
 
34
- for confirmed in order_confirmations:
34
+ for confirmed in confirmed_orders:
35
35
  with suppress(ObjectDoesNotExist):
36
36
  order = leftover_orders.get(id=confirmed.id)
37
37
  order.execution_confirmed = True
@@ -40,7 +40,7 @@ def execute_orders(order_proposal: "OrderProposal") -> tuple[ExecutionStatus, st
40
40
  leftover_orders = leftover_orders.exclude(id=order.id)
41
41
 
42
42
  # Orders without confirmation
43
- leftover_orders.update(execution_confirmed=False, execution_comment="No confirmation received from the custodian")
43
+ leftover_orders.update(execution_confirmed=False, execution_comment="Execution ignored")
44
44
  return ExecutionStatus.IN_DRAFT if as_draft else ExecutionStatus.PENDING, rebalancing_comment
45
45
 
46
46
 
@@ -10,7 +10,10 @@ from wbportfolio.pms.typing import Order
10
10
  from .. import ExecutionStatus, RoutingException
11
11
  from . import BaseCustodianAdapter
12
12
 
13
- ASSET_CLASS_MAP = {Order.AssetType.EQUITY: "EQUITY"} # API can support BOND, FUTURE, OPTION, and DYNAMIC_STRATEGY
13
+ ASSET_CLASS_MAP = {
14
+ Order.AssetType.EQUITY: "EQUITY",
15
+ Order.AssetType.AMERICAN_DEPOSITORY_RECEIPT: "EQUITY",
16
+ } # API can support BOND, FUTURE, OPTION, and DYNAMIC_STRATEGY
14
17
  ASSET_CLASS_MAP_INV = {
15
18
  v: k for k, v in ASSET_CLASS_MAP.items()
16
19
  } # API can support BOND, FUTURE, OPTION, and DYNAMIC_STRATEGY
wbportfolio/pms/typing.py CHANGED
@@ -101,6 +101,7 @@ class Portfolio:
101
101
  class Order:
102
102
  class AssetType(enum.Enum):
103
103
  EQUITY = "EQUITY"
104
+ AMERICAN_DEPOSITORY_RECEIPT = "AMERICAN_DEPOSITORY_RECEIPT"
104
105
 
105
106
  id: int | str
106
107
  trade_date: date
@@ -267,28 +268,6 @@ class TradeBatch:
267
268
  if round(float(self.total_target_weight), 4) != 1: # we do that to remove decimal over precision
268
269
  raise ValidationError(f"Total Weight cannot be different than 1 ({float(self.total_target_weight)})")
269
270
 
270
- def convert_to_portfolio(self, use_effective: bool = False, *extra_positions):
271
- positions = []
272
- for trade in self.trades_map.values():
273
- positions.append(
274
- Position(
275
- underlying_instrument=trade.underlying_instrument,
276
- instrument_type=trade.instrument_type,
277
- weighting=trade.target_weight if not use_effective else trade.previous_weight,
278
- daily_return=trade.daily_return if use_effective else Decimal("0"),
279
- shares=trade.target_shares,
280
- currency=trade.currency,
281
- date=trade.date,
282
- is_cash=trade.is_cash,
283
- price=trade.price,
284
- currency_fx_rate=trade.currency_fx_rate,
285
- )
286
- )
287
- for position in extra_positions:
288
- if position.weighting:
289
- positions.append(position)
290
- return Portfolio(tuple(positions))
291
-
292
271
  def normalize(self, total_target_weight: Decimal = Decimal("1.0")):
293
272
  """
294
273
  Normalize the instantiate trades batch so that the target weight is 100%
@@ -89,7 +89,7 @@ class OrderOrderProposalListModelSerializer(wb_serializers.ModelSerializer):
89
89
  ) is not None and portfolio_value:
90
90
  data["target_weight"] = target_total_value_fx_portfolio / portfolio_value
91
91
 
92
- if data.get("weighting") or data.get("target_weight"):
92
+ if data.get("weighting") is not None or data.get("target_weight") is not None:
93
93
  weighting = data.pop("weighting", None)
94
94
  if (target_weight := data.pop("target_weight", None)) is not None:
95
95
  weighting = target_weight - effective_weight
@@ -99,7 +99,7 @@ class OrderOrderProposalListModelSerializer(wb_serializers.ModelSerializer):
99
99
  data.pop("shares", None)
100
100
  data.pop("target_shares", None)
101
101
 
102
- if data.get("shares") or data.get("target_shares"):
102
+ if data.get("shares") is not None or data.get("target_shares") is not None:
103
103
  shares = data.pop("shares", None)
104
104
  if (target_shares := data.pop("target_shares", None)) is not None:
105
105
  shares = target_shares - effective_shares
@@ -96,7 +96,7 @@ class OrderProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserP
96
96
  )
97
97
  if (
98
98
  instance.execution_status in [ExecutionStatus.IN_DRAFT, ExecutionStatus.COMPLETED]
99
- and instance.orders.filter(execution_confirmed=False).exists()
99
+ and instance.orders.exclude(shares=0, weighting=0).filter(execution_confirmed=False).exists()
100
100
  ):
101
101
  warning(request, "Some orders failed confirmation. Check the list for further details.")
102
102
  if instance.execution_status in [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.56.1
3
+ Version: 1.56.3
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*
@@ -20,8 +20,8 @@ wbportfolio/admin/reconciliations.py,sha256=s7aYxdJE6jX72bnKCrj-YdZBl9bFV_n44Bo2
20
20
  wbportfolio/admin/registers.py,sha256=N5zcfFgxR9H37YEJxFIBif7v5uLN2mGNV3lf9c5OYnw,505
21
21
  wbportfolio/admin/roles.py,sha256=h7d0aYBctghj1zFphWkMVHywnCjF8bewVbk8lq1IX8U,446
22
22
  wbportfolio/admin/orders/__init__.py,sha256=m5iZgRJfsorMe7o-vt4qL3Jn1QMRJFXzJQSa1fljfvQ,87
23
- wbportfolio/admin/orders/order_proposals.py,sha256=xcJp7CsBM5fgsqmBYvUWz7utSKZrJj-Pxiymp303fwI,422
24
- wbportfolio/admin/orders/orders.py,sha256=iozEJGWEplncjRw2cM4SoJx2dZxR1sRZvxl0bRpLbz4,658
23
+ wbportfolio/admin/orders/order_proposals.py,sha256=RU5RpAtJUhhhvfLhALmCMm-RserVLYszdswjHa7FFII,480
24
+ wbportfolio/admin/orders/orders.py,sha256=FMHuBWRVft5jc_11bfVpINl8c4F1PXZNC_rfCdeNtxI,705
25
25
  wbportfolio/admin/transactions/__init__.py,sha256=WJRFKwreNwFsQdSrjaK7HUZAzlnSeYNdciLyeoOzFRg,131
26
26
  wbportfolio/admin/transactions/claim.py,sha256=DaE5vtmSlt_ya55bCAM75UD8smNisA5kl5jrAANm0yA,613
27
27
  wbportfolio/admin/transactions/dividends.py,sha256=2Hx0yvcMp0VvzYfuO_jl-9KFrpq6RUd5S7NVrc10Rsg,1604
@@ -299,9 +299,9 @@ wbportfolio/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
299
299
  wbportfolio/models/mixins/instruments.py,sha256=SuMPquQ93D4pZMK-4hQbJtV58_NOyf3wVOctQq7LNXQ,7054
300
300
  wbportfolio/models/mixins/liquidity_stress_test.py,sha256=I_pgJ3QVFBtq0SeljJerhtlrZESRDNAe4On6QfMHvXc,58834
301
301
  wbportfolio/models/orders/__init__.py,sha256=EH9UacGR3npBMje5FGTeLOh1xqFBh9kc24WbGmBIA3g,69
302
- wbportfolio/models/orders/order_proposals.py,sha256=fzj6UwikU5IIs04s4wjPD9S71I-CzVHPgY8ZgcC7EpE,58463
302
+ wbportfolio/models/orders/order_proposals.py,sha256=UYNuW5PguEjVD0h66vyGjagWlWqq46cpv80BORDiT3U,58281
303
303
  wbportfolio/models/orders/orders.py,sha256=-bIle6Ftt_eRfP5R_J2Y4LEh9HqOUF0sbLUxQftd684,11588
304
- wbportfolio/models/orders/routing.py,sha256=OpeP-rfMm1UN7a3vbNxBEQxqtGfE57AeGWnbTZa1fRQ,2223
304
+ wbportfolio/models/orders/routing.py,sha256=7nu7-3zmGsVA3tyymKK_ywY7V7RtKkGcXkyk2V8dXMw,2191
305
305
  wbportfolio/models/reconciliations/__init__.py,sha256=MXH5fZIPGDRBgJkO6wVu_NLRs8fkP1im7G6d-h36lQY,127
306
306
  wbportfolio/models/reconciliations/account_reconciliation_lines.py,sha256=QP6M7hMcyFbuXBa55Y-azui6Dl_WgbzMntEqWzQkbfM,7394
307
307
  wbportfolio/models/reconciliations/account_reconciliations.py,sha256=rofSxetFfEJov6mPyoTvGxELA16HILyJZtQvm_kwYU0,4405
@@ -314,9 +314,9 @@ wbportfolio/models/transactions/trades.py,sha256=fzt52pD7GnvIhVGX8NuArFVzxKEQfPc
314
314
  wbportfolio/models/transactions/transactions.py,sha256=X7vT0t6UNHFKVU_vCZsA_YQJuOpYisF1vVEeKaZD5CA,4245
315
315
  wbportfolio/order_routing/__init__.py,sha256=kYloUGytPBNB8KVy5ysm9PlEEU_sGPk5c3ch46dXeJo,604
316
316
  wbportfolio/order_routing/adapters/__init__.py,sha256=YiM5FFvLucDTBgIn3mCBjKNSRU7K2pokHdbWBoaZA94,1549
317
- wbportfolio/order_routing/adapters/ubs.py,sha256=_Z9-j1a6_F08VlNIjLLn5XmhoKSZQTTH14lGeTgbu9A,6328
317
+ wbportfolio/order_routing/adapters/ubs.py,sha256=RLBiZ98mWijuvtfI8e5C8xH7l4p7OckmpHy6KYjbiIQ,6394
318
318
  wbportfolio/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
319
- wbportfolio/pms/typing.py,sha256=zhFHlYgO9oC5sxBz0oWkXfEVw-Oez_Ex4ojyQSHC0Oo,10627
319
+ wbportfolio/pms/typing.py,sha256=6fog4ENENVMlv_4bpgCMSY9qWHZSaiEuPAMv4l3AcqM,9701
320
320
  wbportfolio/pms/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
321
321
  wbportfolio/pms/analytics/portfolio.py,sha256=u_S-e6HUQwAyq90gweDmxyTHWrIc5nd84sLuUF2iJzA,2031
322
322
  wbportfolio/pms/analytics/utils.py,sha256=EfhKdo9B2ABaUPppb8DgZSqpNkSze8Rjej1xDjv-XcQ,282
@@ -376,7 +376,7 @@ wbportfolio/serializers/roles.py,sha256=T-9NqTldpvaEMFy-Bib5MB6MeboygEOqcMP61mzz
376
376
  wbportfolio/serializers/signals.py,sha256=hD6R4oFtwhvnsJPteytPKy2JwEelmxrapdfoLSnluaE,7053
377
377
  wbportfolio/serializers/orders/__init__.py,sha256=PKJRksA1pWsh8nVfGASoB0m3LyUzVRnq1m9VPp90J7k,271
378
378
  wbportfolio/serializers/orders/order_proposals.py,sha256=Jxea2-Ze8Id5URv4UV-vTfCQGt11tjR27vRRfCs0gXU,4791
379
- wbportfolio/serializers/orders/orders.py,sha256=OfE2ETFu0MvxDWh7o6_4nFunqmKQ-edNT0bNxyF-No4,9473
379
+ wbportfolio/serializers/orders/orders.py,sha256=sab4cGfo-zGPz_5W9DuwOqeo_ja0VobZGU01PjcZUHM,9521
380
380
  wbportfolio/serializers/transactions/__init__.py,sha256=-7Pan4n7YI3iDvGXff6okzk4ycEURRxp5n_SHCY_g_I,493
381
381
  wbportfolio/serializers/transactions/claim.py,sha256=mEt67F2v8HC6roemDT3S0dD0cZIVl1U9sASbLW3Vpyo,11611
382
382
  wbportfolio/serializers/transactions/dividends.py,sha256=ADXf9cXe8rq55lC_a8vIzViGLmQ-yDXkgR54k2m-N0w,1814
@@ -548,7 +548,7 @@ wbportfolio/viewsets/configs/titles/registers.py,sha256=-C-YeGBhGva48oER6EIQ8CxW
548
548
  wbportfolio/viewsets/configs/titles/roles.py,sha256=9LoJa3jgenXJ5UWRlIErTzdbjpSWMKsyJZtv-eDRTK4,739
549
549
  wbportfolio/viewsets/configs/titles/trades.py,sha256=29XCLxvY0Xe3a2tjCno3tN2rRXCr9RWpbWnzurJfnYI,1986
550
550
  wbportfolio/viewsets/orders/__init__.py,sha256=N8v9jdEXryOzrLlc7ML3iBCO2lmNXph9_TWoQ7PTvi4,195
551
- wbportfolio/viewsets/orders/order_proposals.py,sha256=IMiRh7kFqzmHjC34k-FjV21NSxkWVBi5NTEtUysN9qg,8302
551
+ wbportfolio/viewsets/orders/order_proposals.py,sha256=nWUlyVN4Ox54nVHyqN2ryHf_BPXuFjrW-FK5sp5WrHA,8333
552
552
  wbportfolio/viewsets/orders/orders.py,sha256=O6Mo5t18FEGDLzAZZvgl8PY1STLiLgv1fyYVhupuSSY,11678
553
553
  wbportfolio/viewsets/orders/configs/__init__.py,sha256=5MU57JXiKi32_PicHtiNr7YHmMN020FrlF5NFJf_Wds,94
554
554
  wbportfolio/viewsets/orders/configs/buttons/__init__.py,sha256=EHzNmAfa0UQFITEF-wxj_s4wn3Y5DE3DCbEUmmvCTIs,106
@@ -567,7 +567,7 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
567
567
  wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
568
568
  wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
569
569
  wbportfolio/viewsets/transactions/trades.py,sha256=xBgOGaJ8aEg-2RxEJ4FDaBs4SGwuLasun3nhpis0WQY,12363
570
- wbportfolio-1.56.1.dist-info/METADATA,sha256=cyu99QOKfM_ltC_DHi7dFqeTEoyf241oW0zDFIECNkc,751
571
- wbportfolio-1.56.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
572
- wbportfolio-1.56.1.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
573
- wbportfolio-1.56.1.dist-info/RECORD,,
570
+ wbportfolio-1.56.3.dist-info/METADATA,sha256=OBSV7sthodbAftQh-j9KrB9hzIFPcV1j5cLbX6tPwcE,751
571
+ wbportfolio-1.56.3.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
572
+ wbportfolio-1.56.3.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
573
+ wbportfolio-1.56.3.dist-info/RECORD,,