wbportfolio 1.54.14__py2.py3-none-any.whl → 1.54.16__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 (86) hide show
  1. wbportfolio/admin/__init__.py +2 -0
  2. wbportfolio/admin/orders/__init__.py +2 -0
  3. wbportfolio/admin/orders/order_proposals.py +14 -0
  4. wbportfolio/admin/orders/orders.py +30 -0
  5. wbportfolio/admin/{transactions/rebalancing.py → rebalancing.py} +1 -1
  6. wbportfolio/admin/transactions/__init__.py +0 -1
  7. wbportfolio/admin/transactions/trades.py +2 -17
  8. wbportfolio/contrib/company_portfolio/tests/conftest.py +2 -2
  9. wbportfolio/factories/__init__.py +2 -1
  10. wbportfolio/factories/orders/__init__.py +2 -0
  11. wbportfolio/factories/orders/order_proposals.py +17 -0
  12. wbportfolio/factories/orders/orders.py +21 -0
  13. wbportfolio/factories/rebalancing.py +1 -1
  14. wbportfolio/factories/trades.py +2 -13
  15. wbportfolio/filters/orders/__init__.py +1 -0
  16. wbportfolio/filters/orders/orders.py +11 -0
  17. wbportfolio/import_export/handlers/trade.py +20 -20
  18. wbportfolio/import_export/resources/trades.py +2 -2
  19. wbportfolio/migrations/0082_remove_tradeproposal_creator_and_more.py +93 -0
  20. wbportfolio/migrations/0083_order_alter_trade_options_and_more.py +181 -0
  21. wbportfolio/models/__init__.py +2 -0
  22. wbportfolio/models/orders/__init__.py +2 -0
  23. wbportfolio/models/{transactions/trade_proposals.py → orders/order_proposals.py} +304 -264
  24. wbportfolio/models/orders/orders.py +243 -0
  25. wbportfolio/models/portfolio.py +16 -19
  26. wbportfolio/models/{transactions/rebalancing.py → rebalancing.py} +18 -18
  27. wbportfolio/models/transactions/__init__.py +0 -2
  28. wbportfolio/models/transactions/trades.py +10 -450
  29. wbportfolio/pms/analytics/portfolio.py +10 -6
  30. wbportfolio/pms/analytics/utils.py +9 -0
  31. wbportfolio/pms/trading/handler.py +28 -27
  32. wbportfolio/pms/typing.py +18 -7
  33. wbportfolio/rebalancing/decorators.py +1 -1
  34. wbportfolio/rebalancing/models/composite.py +3 -7
  35. wbportfolio/rebalancing/models/market_capitalization_weighted.py +3 -1
  36. wbportfolio/serializers/__init__.py +1 -0
  37. wbportfolio/serializers/orders/__init__.py +2 -0
  38. wbportfolio/serializers/{transactions/trade_proposals.py → orders/order_proposals.py} +30 -17
  39. wbportfolio/serializers/orders/orders.py +187 -0
  40. wbportfolio/serializers/portfolios.py +7 -7
  41. wbportfolio/serializers/rebalancing.py +1 -1
  42. wbportfolio/serializers/transactions/__init__.py +1 -5
  43. wbportfolio/serializers/transactions/trades.py +1 -182
  44. wbportfolio/tests/conftest.py +4 -2
  45. wbportfolio/tests/models/orders/__init__.py +0 -0
  46. wbportfolio/tests/models/{transactions/test_trade_proposals.py → orders/test_order_proposals.py} +214 -250
  47. wbportfolio/tests/models/test_portfolios.py +11 -10
  48. wbportfolio/tests/models/transactions/test_rebalancing.py +5 -5
  49. wbportfolio/tests/models/transactions/test_trades.py +0 -20
  50. wbportfolio/tests/rebalancing/test_models.py +24 -28
  51. wbportfolio/tests/signals.py +10 -10
  52. wbportfolio/tests/tests.py +1 -1
  53. wbportfolio/urls.py +7 -7
  54. wbportfolio/viewsets/__init__.py +2 -0
  55. wbportfolio/viewsets/configs/buttons/__init__.py +2 -3
  56. wbportfolio/viewsets/configs/buttons/trades.py +0 -8
  57. wbportfolio/viewsets/configs/display/__init__.py +0 -2
  58. wbportfolio/viewsets/configs/display/portfolios.py +5 -5
  59. wbportfolio/viewsets/configs/display/rebalancing.py +2 -2
  60. wbportfolio/viewsets/configs/display/trades.py +1 -225
  61. wbportfolio/viewsets/configs/endpoints/__init__.py +0 -3
  62. wbportfolio/viewsets/configs/endpoints/trades.py +0 -41
  63. wbportfolio/viewsets/orders/__init__.py +6 -0
  64. wbportfolio/viewsets/orders/configs/__init__.py +4 -0
  65. wbportfolio/viewsets/orders/configs/buttons/__init__.py +2 -0
  66. wbportfolio/viewsets/{configs/buttons/trade_proposals.py → orders/configs/buttons/order_proposals.py} +21 -21
  67. wbportfolio/viewsets/orders/configs/buttons/orders.py +9 -0
  68. wbportfolio/viewsets/orders/configs/displays/__init__.py +2 -0
  69. wbportfolio/viewsets/{configs/display/trade_proposals.py → orders/configs/displays/order_proposals.py} +21 -21
  70. wbportfolio/viewsets/orders/configs/displays/orders.py +180 -0
  71. wbportfolio/viewsets/orders/configs/endpoints/__init__.py +2 -0
  72. wbportfolio/viewsets/orders/configs/endpoints/order_proposals.py +21 -0
  73. wbportfolio/viewsets/orders/configs/endpoints/orders.py +26 -0
  74. wbportfolio/viewsets/orders/configs/titles/__init__.py +0 -0
  75. wbportfolio/viewsets/orders/configs/titles/orders.py +0 -0
  76. wbportfolio/viewsets/{transactions/trade_proposals.py → orders/order_proposals.py} +46 -46
  77. wbportfolio/viewsets/orders/orders.py +219 -0
  78. wbportfolio/viewsets/portfolios.py +12 -12
  79. wbportfolio/viewsets/{transactions/rebalancing.py → rebalancing.py} +2 -2
  80. wbportfolio/viewsets/transactions/__init__.py +1 -7
  81. wbportfolio/viewsets/transactions/trades.py +1 -199
  82. {wbportfolio-1.54.14.dist-info → wbportfolio-1.54.16.dist-info}/METADATA +1 -1
  83. {wbportfolio-1.54.14.dist-info → wbportfolio-1.54.16.dist-info}/RECORD +85 -58
  84. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +0 -18
  85. {wbportfolio-1.54.14.dist-info → wbportfolio-1.54.16.dist-info}/WHEEL +0 -0
  86. {wbportfolio-1.54.14.dist-info → wbportfolio-1.54.16.dist-info}/licenses/LICENSE +0 -0
@@ -21,11 +21,11 @@ from wbfdm.models import Instrument
21
21
  from wbportfolio.filters import PortfolioFilterSet, PortfolioTreeGraphChartFilterSet
22
22
  from wbportfolio.models import (
23
23
  AssetPosition,
24
+ OrderProposal,
24
25
  Portfolio,
25
26
  PortfolioPortfolioThroughModel,
26
27
  Rebalancer,
27
28
  RebalancingModel,
28
- TradeProposal,
29
29
  )
30
30
  from wbportfolio.models.portfolio import compute_lookthrough_as_task
31
31
  from wbportfolio.serializers import (
@@ -77,7 +77,7 @@ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPer
77
77
  "last_asset_under_management_usd",
78
78
  "last_positions",
79
79
  "automatic_rebalancer",
80
- "last_trade_proposal_date",
80
+ "last_order_proposal_date",
81
81
  "is_manageable",
82
82
  "is_tracked",
83
83
  "only_weighting",
@@ -111,8 +111,8 @@ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPer
111
111
  .annotate(s=Sum("shares"))
112
112
  .values("s")[:1]
113
113
  ),
114
- last_trade_proposal_date=Subquery(
115
- TradeProposal.objects.filter(portfolio=OuterRef("pk"))
114
+ last_order_proposal_date=Subquery(
115
+ OrderProposal.objects.filter(portfolio=OuterRef("pk"))
116
116
  .order_by("-trade_date")
117
117
  .values("trade_date")[:1]
118
118
  ),
@@ -123,10 +123,10 @@ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPer
123
123
  def rebalance(self, request, pk=None):
124
124
  if date_str := request.POST.get("trade_date", None):
125
125
  trade_date = datetime.strptime(date_str, "%Y-%m-%d")
126
- trade_proposal, _ = TradeProposal.objects.get_or_create(portfolio_id=pk, trade_date=trade_date)
127
- trade_proposal.reset_trades()
126
+ order_proposal, _ = OrderProposal.objects.get_or_create(portfolio_id=pk, trade_date=trade_date)
127
+ order_proposal.reset_orders()
128
128
  return Response(
129
- {"endpoint": reverse("wbportfolio:tradeproposal-detail", args=[trade_proposal.id], request=request)}
129
+ {"endpoint": reverse("wbportfolio:orderproposal-detail", args=[order_proposal.id], request=request)}
130
130
  )
131
131
  raise HttpResponse("Bad Request", status=400)
132
132
 
@@ -147,8 +147,8 @@ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPer
147
147
  activation_date = datetime.strptime(request.POST["activation_date"], "%Y-%m-%d")
148
148
  rebalancing_model = get_object_or_404(RebalancingModel, pk=request.POST["rebalancing_model"])
149
149
  frequency = request.POST["frequency"]
150
- approve_trade_proposal_automatically = (
151
- request.POST.get("approve_trade_proposal_automatically", "false") == "true"
150
+ approve_order_proposal_automatically = (
151
+ request.POST.get("approve_order_proposal_automatically", "false") == "true"
152
152
  )
153
153
 
154
154
  rebalancer, _ = Rebalancer.objects.update_or_create(
@@ -157,7 +157,7 @@ class PortfolioModelViewSet(UserPortfolioRequestPermissionMixin, InternalUserPer
157
157
  "rebalancing_model": rebalancing_model,
158
158
  "frequency": frequency,
159
159
  "activation_date": activation_date,
160
- "approve_trade_proposal_automatically": approve_trade_proposal_automatically,
160
+ "approve_order_proposal_automatically": approve_order_proposal_automatically,
161
161
  },
162
162
  )
163
163
  return Response(
@@ -242,9 +242,9 @@ class TopDownPortfolioCompositionPandasAPIView(UserPortfolioRequestPermissionMix
242
242
  @cached_property
243
243
  def last_rebalancing_date(self) -> date | None:
244
244
  if self.composition_portfolio and self.last_effective_date:
245
- with suppress(TradeProposal.DoesNotExist):
245
+ with suppress(OrderProposal.DoesNotExist):
246
246
  return (
247
- self.composition_portfolio.trade_proposals.filter(trade_date__lte=self.last_effective_date)
247
+ self.composition_portfolio.order_proposals.filter(trade_date__lte=self.last_effective_date)
248
248
  .latest("trade_date")
249
249
  .trade_date
250
250
  )
@@ -8,8 +8,8 @@ from wbportfolio.serializers import (
8
8
  RebalancingModelRepresentationSerializer,
9
9
  )
10
10
 
11
- from ..configs.display import RebalancerDisplayConfig
12
- from ..configs.endpoints import RebalancerEndpointConfig
11
+ from .configs.display import RebalancerDisplayConfig
12
+ from .configs.endpoints import RebalancerEndpointConfig
13
13
 
14
14
 
15
15
  class RebalancingModelRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
@@ -17,12 +17,7 @@ from .fees import (
17
17
  FeesModelViewSet,
18
18
  FeesProductModelViewSet,
19
19
  )
20
- from .rebalancing import RebalancingModelRepresentationViewSet, RebalancerRepresentationViewSet, RebalancerModelViewSet
21
- from .trade_proposals import (
22
- TradeProposalModelViewSet,
23
- TradeProposalPortfolioModelViewSet,
24
- TradeProposalRepresentationViewSet,
25
- )
20
+
26
21
  from .trades import (
27
22
  CustodianDistributionInstrumentChartViewSet,
28
23
  CustomerDistributionInstrumentChartViewSet,
@@ -32,5 +27,4 @@ from .trades import (
32
27
  TradeModelViewSet,
33
28
  TradePortfolioModelViewSet,
34
29
  TradeRepresentationViewSet,
35
- TradeTradeProposalModelViewSet,
36
30
  )
@@ -2,7 +2,6 @@ from decimal import Decimal
2
2
 
3
3
  import pandas as pd
4
4
  import plotly.graph_objects as go
5
- from django.contrib.messages import error, warning
6
5
  from django.db.models import (
7
6
  BooleanField,
8
7
  Case,
@@ -16,14 +15,11 @@ from django.db.models import (
16
15
  When,
17
16
  )
18
17
  from django.db.models.functions import Coalesce
19
- from django.shortcuts import get_object_or_404
20
- from django.utils.functional import cached_property
21
18
  from django_filters.rest_framework import DjangoFilterBackend
22
19
  from wbcore import viewsets
23
20
  from wbcore.contrib.currency.models import CurrencyFXRates
24
21
  from wbcore.permissions.permissions import InternalUserPermissionMixin
25
22
  from wbcore.utils.strings import format_number
26
- from wbcore.viewsets.mixins import OrderableMixin
27
23
  from wbcrm.models import Account
28
24
 
29
25
  from wbportfolio.filters import (
@@ -33,15 +29,11 @@ from wbportfolio.filters import (
33
29
  TradeInstrumentFilterSet,
34
30
  TradePortfolioFilter,
35
31
  )
36
- from wbportfolio.import_export.resources.trades import TradeProposalTradeResource
37
- from wbportfolio.models import Trade, TradeProposal
32
+ from wbportfolio.models import Trade
38
33
  from wbportfolio.models.transactions.claim import Claim
39
34
  from wbportfolio.serializers import (
40
- ReadOnlyTradeTradeProposalModelSerializer,
41
35
  TradeModelSerializer,
42
- TradeProposalRepresentationSerializer,
43
36
  TradeRepresentationSerializer,
44
- TradeTradeProposalModelSerializer,
45
37
  )
46
38
  from wbportfolio.viewsets.configs.titles.trades import (
47
39
  CustomerDistributionInstrumentTitleConfig,
@@ -63,19 +55,10 @@ from ..configs import (
63
55
  TradePortfolioEndpointConfig,
64
56
  TradePortfolioTitleConfig,
65
57
  TradeTitleConfig,
66
- TradeTradeProposalButtonConfig,
67
- TradeTradeProposalDisplayConfig,
68
- TradeTradeProposalEndpointConfig,
69
58
  )
70
59
  from ..mixins import UserPortfolioRequestPermissionMixin
71
60
 
72
61
 
73
- class TradeProposalRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
74
- IDENTIFIER = "wbportfolio:trade"
75
- queryset = TradeProposal.objects.all()
76
- serializer_class = TradeProposalRepresentationSerializer
77
-
78
-
79
62
  class TradeRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
80
63
  filterset_class = TradeFilter
81
64
 
@@ -357,184 +340,3 @@ class CustomerDistributionInstrumentChartViewSet(UserPortfolioRequestPermissionM
357
340
  )
358
341
  .filter(account_name__isnull=False)
359
342
  )
360
-
361
-
362
- class TradeTradeProposalModelViewSet(
363
- UserPortfolioRequestPermissionMixin, InternalUserPermissionMixin, OrderableMixin, viewsets.ModelViewSet
364
- ):
365
- IMPORT_ALLOWED = True
366
- ordering = (
367
- "trade_proposal",
368
- "order",
369
- )
370
- ordering_fields = (
371
- "underlying_instrument__name",
372
- "underlying_instrument_isin",
373
- "underlying_instrument_ticker",
374
- "underlying_instrument_refinitiv_identifier_code",
375
- "underlying_instrument_instrument_type",
376
- "target_weight",
377
- "effective_weight",
378
- "effective_shares",
379
- "target_shares",
380
- "shares",
381
- "weighting",
382
- )
383
- IDENTIFIER = "wbportfolio:tradeproposal"
384
- search_fields = ("underlying_instrument__name",)
385
- filterset_fields = {"status": ["exact"]}
386
- queryset = Trade.objects.all()
387
-
388
- display_config_class = TradeTradeProposalDisplayConfig
389
- endpoint_config_class = TradeTradeProposalEndpointConfig
390
- serializer_class = TradeTradeProposalModelSerializer
391
- button_config_class = TradeTradeProposalButtonConfig
392
-
393
- @cached_property
394
- def trade_proposal(self):
395
- return get_object_or_404(TradeProposal, pk=self.kwargs["trade_proposal_id"])
396
-
397
- @cached_property
398
- def portfolio_total_asset_value(self):
399
- return self.trade_proposal.portfolio_total_asset_value
400
-
401
- def has_import_permission(self, request) -> bool: # allow import only on draft trade proposal
402
- return super().has_import_permission(request) and self.trade_proposal.status == TradeProposal.Status.DRAFT
403
-
404
- def get_import_resource_kwargs(self):
405
- resource_kwargs = super().get_import_resource_kwargs()
406
- resource_kwargs["columns_mapping"] = {"underlying_instrument": "underlying_instrument__isin"}
407
- return resource_kwargs
408
-
409
- def get_resource_class(self):
410
- return TradeProposalTradeResource
411
-
412
- def get_aggregates(self, queryset, *args, **kwargs):
413
- agg = {}
414
- if queryset.exists():
415
- noncash_aggregates = queryset.filter(underlying_instrument__is_cash=False).aggregate(
416
- sum_target_weight=Sum(F("target_weight")),
417
- sum_effective_weight=Sum(F("effective_weight")),
418
- sum_target_total_value_fx_portfolio=Sum(F("target_total_value_fx_portfolio")),
419
- sum_effective_total_value_fx_portfolio=Sum(F("effective_total_value_fx_portfolio")),
420
- )
421
-
422
- # weights aggregates
423
- cash_sum_effective_weight = Decimal("1.0") - noncash_aggregates["sum_effective_weight"]
424
- cash_sum_target_cash_weight = Decimal("1.0") - noncash_aggregates["sum_target_weight"]
425
- noncash_sum_effective_weight = noncash_aggregates["sum_effective_weight"] or Decimal(0)
426
- noncash_sum_target_weight = noncash_aggregates["sum_target_weight"] or Decimal(0)
427
- sum_buy_weight = queryset.filter(weighting__gte=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
428
- sum_sell_weight = queryset.filter(weighting__lt=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
429
-
430
- # shares aggregates
431
- cash_sum_effective_total_value_fx_portfolio = cash_sum_effective_weight * self.portfolio_total_asset_value
432
- cash_sum_target_total_value_fx_portfolio = cash_sum_target_cash_weight * self.portfolio_total_asset_value
433
- noncash_sum_effective_total_value_fx_portfolio = noncash_aggregates[
434
- "sum_effective_total_value_fx_portfolio"
435
- ] or Decimal(0)
436
- noncash_sum_target_total_value_fx_portfolio = noncash_aggregates[
437
- "sum_target_total_value_fx_portfolio"
438
- ] or Decimal(0)
439
- sum_buy_total_value_fx_portfolio = queryset.filter(total_value_fx_portfolio__gte=0).aggregate(
440
- s=Sum(F("total_value_fx_portfolio"))
441
- )["s"] or Decimal(0)
442
- sum_sell_total_value_fx_portfolio = queryset.filter(total_value_fx_portfolio__lt=0).aggregate(
443
- s=Sum(F("total_value_fx_portfolio"))
444
- )["s"] or Decimal(0)
445
-
446
- agg = {
447
- "effective_weight": {
448
- "Cash": format_number(cash_sum_effective_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
449
- "Non-Cash": format_number(noncash_sum_effective_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
450
- "Total": format_number(
451
- noncash_sum_effective_weight + cash_sum_effective_weight,
452
- decimal=Trade.TRADE_WEIGHTING_PRECISION,
453
- ),
454
- },
455
- "target_weight": {
456
- "Cash": format_number(cash_sum_target_cash_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
457
- "Non-Cash": format_number(noncash_sum_target_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
458
- "Total": format_number(
459
- cash_sum_target_cash_weight + noncash_sum_target_weight,
460
- decimal=Trade.TRADE_WEIGHTING_PRECISION,
461
- ),
462
- },
463
- "effective_total_value_fx_portfolio": {
464
- "Cash": format_number(cash_sum_effective_total_value_fx_portfolio, decimal=6),
465
- "Non-Cash": format_number(noncash_sum_effective_total_value_fx_portfolio, decimal=6),
466
- "Total": format_number(
467
- cash_sum_effective_total_value_fx_portfolio + noncash_sum_effective_total_value_fx_portfolio,
468
- decimal=6,
469
- ),
470
- },
471
- "target_total_value_fx_portfolio": {
472
- "Cash": format_number(cash_sum_target_total_value_fx_portfolio, decimal=6),
473
- "Non-Cash": format_number(noncash_sum_target_total_value_fx_portfolio, decimal=6),
474
- "Total": format_number(
475
- cash_sum_target_total_value_fx_portfolio + noncash_sum_target_total_value_fx_portfolio,
476
- decimal=6,
477
- ),
478
- },
479
- "weighting": {
480
- "Cash Flow": format_number(
481
- cash_sum_target_cash_weight - cash_sum_effective_weight,
482
- decimal=Trade.TRADE_WEIGHTING_PRECISION,
483
- ),
484
- "Buy": format_number(sum_buy_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
485
- "Sell": format_number(sum_sell_weight, decimal=Trade.TRADE_WEIGHTING_PRECISION),
486
- },
487
- "total_value_fx_portfolio": {
488
- "Cash Flow": format_number(
489
- cash_sum_target_total_value_fx_portfolio - cash_sum_effective_total_value_fx_portfolio,
490
- decimal=6,
491
- ),
492
- "Buy": format_number(sum_buy_total_value_fx_portfolio, decimal=6),
493
- "Sell": format_number(sum_sell_total_value_fx_portfolio, decimal=6),
494
- },
495
- }
496
-
497
- return agg
498
-
499
- def get_serializer_class(self):
500
- if self.trade_proposal.status != TradeProposal.Status.DRAFT:
501
- return ReadOnlyTradeTradeProposalModelSerializer
502
- return TradeTradeProposalModelSerializer
503
-
504
- def add_messages(self, request, queryset=None, paginated_queryset=None, instance=None, initial=False):
505
- if queryset is not None and queryset.exists() and self.trade_proposal.status != TradeProposal.Status.APPROVED:
506
- total_target_weight = queryset.aggregate(c=Sum(F("target_weight")))["c"] or Decimal(0)
507
- if round(total_target_weight, 3) != 1:
508
- warning(
509
- request,
510
- "The total target weight does not equal 1. To avoid automatic cash allocation, please adjust the trade weights to sum up to 1. Otherwise, a cash component will be added when this trade proposal is submitted.",
511
- )
512
- if queryset.filter(status=Trade.Status.FAILED):
513
- error(
514
- request,
515
- "Some trades failed preparation. To resolve this, please revert the trade proposal to draft, review and correct the trades, and then resubmit.",
516
- )
517
-
518
- def get_queryset(self):
519
- if self.is_portfolio_manager:
520
- qs = super().get_queryset().filter(trade_proposal=self.kwargs["trade_proposal_id"]).annotate_base_info()
521
- else:
522
- qs = TradeProposal.objects.none()
523
-
524
- return qs.annotate(
525
- underlying_instrument_isin=F("underlying_instrument__isin"),
526
- underlying_instrument_ticker=F("underlying_instrument__ticker"),
527
- underlying_instrument_refinitiv_identifier_code=F("underlying_instrument__refinitiv_identifier_code"),
528
- underlying_instrument_instrument_type=Case(
529
- When(
530
- underlying_instrument__parent__is_security=True,
531
- then=F("underlying_instrument__parent__instrument_type__short_name"),
532
- ),
533
- default=F("underlying_instrument__instrument_type__short_name"),
534
- ),
535
- effective_total_value_fx_portfolio=F("effective_weight") * Value(self.portfolio_total_asset_value),
536
- target_total_value_fx_portfolio=F("target_weight") * Value(self.portfolio_total_asset_value),
537
- portfolio_currency=F("portfolio__currency__symbol"),
538
- security=F("underlying_instrument__parent"),
539
- company=F("underlying_instrument__parent__parent"),
540
- )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.54.14
3
+ Version: 1.54.16
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*