wbportfolio 1.54.13__py2.py3-none-any.whl → 1.54.15__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} +289 -245
  24. wbportfolio/models/orders/orders.py +243 -0
  25. wbportfolio/models/portfolio.py +17 -20
  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 +6 -4
  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} +23 -15
  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} +218 -246
  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} +22 -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 -45
  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.13.dist-info → wbportfolio-1.54.15.dist-info}/METADATA +1 -1
  83. {wbportfolio-1.54.13.dist-info → wbportfolio-1.54.15.dist-info}/RECORD +85 -58
  84. wbportfolio/viewsets/configs/endpoints/trade_proposals.py +0 -18
  85. {wbportfolio-1.54.13.dist-info → wbportfolio-1.54.15.dist-info}/WHEEL +0 -0
  86. {wbportfolio-1.54.13.dist-info → wbportfolio-1.54.15.dist-info}/licenses/LICENSE +0 -0
wbportfolio/pms/typing.py CHANGED
@@ -19,7 +19,7 @@ class Position:
19
19
  weighting: Decimal
20
20
  date: date_lib
21
21
 
22
- drift_factor: Decimal = Decimal("1")
22
+ daily_return: Decimal = Decimal("0")
23
23
  currency: int | None = None
24
24
  instrument_type: int | None = None
25
25
  asset_valuation_date: date_lib | None = None
@@ -73,6 +73,10 @@ class Portfolio:
73
73
  def total_shares(self):
74
74
  return sum([pos.target_shares for pos in self.positions if pos.target_shares is not None])
75
75
 
76
+ @property
77
+ def portfolio_contribution(self) -> Decimal:
78
+ return sum(map(lambda pos: pos.weighting * (Decimal("1") + pos.daily_return), self.positions))
79
+
76
80
  def to_df(self):
77
81
  return pd.DataFrame([asdict(pos) for pos in self.positions])
78
82
 
@@ -98,7 +102,8 @@ class Trade:
98
102
  currency_fx_rate: Decimal = Decimal("1")
99
103
  effective_shares: Decimal = Decimal("0")
100
104
  target_shares: Decimal = Decimal("0")
101
- drift_factor: Decimal = Decimal("1")
105
+ daily_return: Decimal = Decimal("0")
106
+ portfolio_contribution: Decimal = Decimal("1")
102
107
  id: int | None = None
103
108
  is_cash: bool = False
104
109
 
@@ -109,8 +114,9 @@ class Trade:
109
114
  target_weight=self.target_weight + other.target_weight,
110
115
  effective_shares=self.effective_shares,
111
116
  target_shares=self.target_shares + other.target_shares,
112
- drift_factor=self.drift_factor
113
- ** {
117
+ daily_return=self.daily_return,
118
+ portfolio_contribution=self.portfolio_contribution,
119
+ **{
114
120
  f.name: getattr(self, f.name)
115
121
  for f in fields(Trade)
116
122
  if f.name
@@ -120,7 +126,8 @@ class Trade:
120
126
  "effective_shares",
121
127
  "target_shares",
122
128
  "underlying_instrument",
123
- "drift_factor",
129
+ "daily_return",
130
+ "portfolio_contribution",
124
131
  ]
125
132
  },
126
133
  )
@@ -132,7 +139,11 @@ class Trade:
132
139
 
133
140
  @property
134
141
  def effective_weight(self) -> Decimal:
135
- return self.previous_weight * self.drift_factor
142
+ return (
143
+ self.previous_weight * (round(self.daily_return, 16) + 1) / self.portfolio_contribution
144
+ if self.portfolio_contribution
145
+ else self.previous_weight
146
+ )
136
147
 
137
148
  @property
138
149
  def delta_weight(self) -> Decimal:
@@ -210,7 +221,7 @@ class TradeBatch:
210
221
  underlying_instrument=trade.underlying_instrument,
211
222
  instrument_type=trade.instrument_type,
212
223
  weighting=trade.target_weight if not use_effective else trade.previous_weight,
213
- drift_factor=trade.drift_factor if use_effective else Decimal("1"),
224
+ daily_return=trade.daily_return if use_effective else Decimal("0"),
214
225
  shares=trade.target_shares,
215
226
  currency=trade.currency,
216
227
  date=trade.date,
@@ -2,7 +2,7 @@ def register(model_name: str):
2
2
  """
3
3
  Decorator to include when a backend need automatic registration
4
4
  """
5
- from wbportfolio.models.transactions.rebalancing import RebalancingModel
5
+ from wbportfolio.models.rebalancing import RebalancingModel
6
6
 
7
7
  def _decorator(backend_class):
8
8
  defaults = {
@@ -3,7 +3,6 @@ from decimal import Decimal
3
3
  from django.core.exceptions import ObjectDoesNotExist
4
4
  from wbfdm.models import InstrumentPrice
5
5
 
6
- from wbportfolio.models import Trade
7
6
  from wbportfolio.pms.typing import Portfolio, Position
8
7
  from wbportfolio.rebalancing.base import AbstractRebalancingModel
9
8
  from wbportfolio.rebalancing.decorators import register
@@ -14,21 +13,18 @@ class CompositeRebalancing(AbstractRebalancingModel):
14
13
  @property
15
14
  def base_assets(self) -> dict[int, Decimal]:
16
15
  """
17
- Return a dictionary representation (instrument_id: target weight) of this trade proposal
16
+ Return a dictionary representation (instrument_id: target weight) of this order proposal
18
17
  Returns:
19
18
  A dictionary representation
20
19
 
21
20
  """
22
21
  try:
23
- latest_trade_proposal = self.portfolio.trade_proposals.filter(
22
+ latest_order_proposal = self.portfolio.order_proposals.filter(
24
23
  status="APPROVED", trade_date__lt=self.trade_date
25
24
  ).latest("trade_date")
26
25
  return {
27
26
  v["underlying_instrument"]: v["target_weight"]
28
- for v in latest_trade_proposal.trades.all()
29
- .annotate_base_info()
30
- .filter(status=Trade.Status.EXECUTED)
31
- .values("underlying_instrument", "target_weight")
27
+ for v in latest_order_proposal.get_orders().values("underlying_instrument", "target_weight")
32
28
  }
33
29
  except ObjectDoesNotExist:
34
30
  return dict()
@@ -12,6 +12,7 @@ from wbfdm.models import (
12
12
  InstrumentListThroughModel,
13
13
  )
14
14
 
15
+ from wbportfolio.pms.analytics.utils import fix_quantization_error
15
16
  from wbportfolio.pms.typing import Portfolio, Position
16
17
  from wbportfolio.pms.typing import Portfolio as PortfolioDTO
17
18
  from wbportfolio.rebalancing.base import AbstractRebalancingModel
@@ -120,11 +121,12 @@ class MarketCapitalizationRebalancing(AbstractRebalancingModel):
120
121
  df = self.market_cap_df / self.market_cap_df.dropna().sum()
121
122
  df = df[df > self.MIN_WEIGHT]
122
123
  df = df / df.sum()
124
+ df = fix_quantization_error(df, 8)
123
125
  for underlying_instrument, weighting in df.to_dict().items():
124
126
  if np.isnan(weighting):
125
127
  weighting = Decimal(0)
126
128
  else:
127
- weighting = Decimal(weighting)
129
+ weighting = round(Decimal(weighting), 8)
128
130
  positions.append(
129
131
  Position(
130
132
  underlying_instrument=underlying_instrument,
@@ -27,6 +27,7 @@ from .registers import RegisterModelSerializer, RegisterRepresentationSerializer
27
27
  from .roles import PortfolioRoleModelSerializer, PortfolioRoleProjectModelSerializer
28
28
  from .signals import *
29
29
  from .transactions import *
30
+ from .orders import *
30
31
  from .products import (
31
32
  ProductRepresentationSerializer,
32
33
  ProductCustomerRepresentationSerializer,
@@ -0,0 +1,2 @@
1
+ from .order_proposals import OrderProposalModelSerializer, ReadOnlyOrderProposalModelSerializer, OrderProposalRepresentationSerializer
2
+ from .orders import OrderOrderProposalModelSerializer, ReadOnlyOrderOrderProposalModelSerializer, OrderOrderProposalListModelSerializer
@@ -6,12 +6,20 @@ from rest_framework.reverse import reverse
6
6
  from wbcore import serializers as wb_serializers
7
7
  from wbcore.serializers import DefaultFromView
8
8
 
9
- from wbportfolio.models import Portfolio, RebalancingModel, TradeProposal
9
+ from wbportfolio.models import OrderProposal, Portfolio, RebalancingModel
10
10
 
11
11
  from .. import PortfolioRepresentationSerializer, RebalancingModelRepresentationSerializer
12
12
 
13
13
 
14
- class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
14
+ class OrderProposalRepresentationSerializer(wb_serializers.RepresentationSerializer):
15
+ _detail = wb_serializers.HyperlinkField(reverse_name="wbportfolio:orderproposal-detail")
16
+
17
+ class Meta:
18
+ model = OrderProposal
19
+ fields = ("id", "trade_date", "status", "_detail")
20
+
21
+
22
+ class OrderProposalModelSerializer(wb_serializers.ModelSerializer):
15
23
  rebalancing_model = wb_serializers.PrimaryKeyRelatedField(queryset=RebalancingModel.objects.all(), required=False)
16
24
  _rebalancing_model = RebalancingModelRepresentationSerializer(source="rebalancing_model")
17
25
  target_portfolio = wb_serializers.PrimaryKeyRelatedField(
@@ -46,7 +54,7 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
46
54
  if target_portfolio and not rebalancing_model:
47
55
  target_portfolio_dto = target_portfolio._build_dto(obj.trade_date)
48
56
  try:
49
- obj.reset_trades(
57
+ obj.reset_orders(
50
58
  target_portfolio=target_portfolio_dto, total_target_weight=Decimal("1.0") - total_cash_weight
51
59
  )
52
60
  except ValidationError as e:
@@ -57,21 +65,21 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
57
65
  @wb_serializers.register_only_instance_resource()
58
66
  def additional_resources(self, instance, request, user, **kwargs):
59
67
  res = {}
60
- if instance.status == TradeProposal.Status.APPROVED:
61
- res["replay"] = reverse("wbportfolio:tradeproposal-replay", args=[instance.id], request=request)
62
- if instance.status == TradeProposal.Status.DRAFT:
63
- res["reset"] = reverse("wbportfolio:tradeproposal-reset", args=[instance.id], request=request)
64
- res["normalize"] = reverse("wbportfolio:tradeproposal-normalize", args=[instance.id], request=request)
65
- res["deleteall"] = reverse("wbportfolio:tradeproposal-deleteall", args=[instance.id], request=request)
66
- res["trades"] = reverse(
67
- "wbportfolio:tradeproposal-trade-list",
68
+ if instance.status == OrderProposal.Status.APPROVED:
69
+ res["replay"] = reverse("wbportfolio:orderproposal-replay", args=[instance.id], request=request)
70
+ if instance.status == OrderProposal.Status.DRAFT:
71
+ res["reset"] = reverse("wbportfolio:orderproposal-reset", args=[instance.id], request=request)
72
+ res["normalize"] = reverse("wbportfolio:orderproposal-normalize", args=[instance.id], request=request)
73
+ res["deleteall"] = reverse("wbportfolio:orderproposal-deleteall", args=[instance.id], request=request)
74
+ res["orders"] = reverse(
75
+ "wbportfolio:orderproposal-order-list",
68
76
  args=[instance.id],
69
77
  request=request,
70
78
  )
71
79
  return res
72
80
 
73
81
  class Meta:
74
- model = TradeProposal
82
+ model = OrderProposal
75
83
  only_fsm_transition_on_instance = True
76
84
  fields = (
77
85
  "id",
@@ -88,6 +96,6 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
88
96
  )
89
97
 
90
98
 
91
- class ReadOnlyTradeProposalModelSerializer(TradeProposalModelSerializer):
92
- class Meta(TradeProposalModelSerializer.Meta):
93
- read_only_fields = TradeProposalModelSerializer.Meta.fields
99
+ class ReadOnlyOrderProposalModelSerializer(OrderProposalModelSerializer):
100
+ class Meta(OrderProposalModelSerializer.Meta):
101
+ read_only_fields = OrderProposalModelSerializer.Meta.fields
@@ -0,0 +1,187 @@
1
+ from decimal import Decimal
2
+
3
+ from rest_framework import serializers
4
+ from wbcore import serializers as wb_serializers
5
+ from wbcore.metadata.configs.display.list_display import BaseTreeGroupLevelOption
6
+ from wbfdm.models import Instrument
7
+ from wbfdm.serializers import InvestableInstrumentRepresentationSerializer
8
+ from wbfdm.serializers.instruments.instruments import (
9
+ CompanyRepresentationSerializer,
10
+ SecurityRepresentationSerializer,
11
+ )
12
+
13
+ from wbportfolio.models import Order
14
+
15
+
16
+ class GetSecurityDefault:
17
+ requires_context = True
18
+
19
+ def __call__(self, serializer_instance):
20
+ try:
21
+ instance = serializer_instance.view.get_object()
22
+ return instance.underlying_instrument.parent or instance.underlying_instrument
23
+ except Exception:
24
+ return None
25
+
26
+
27
+ class GetCompanyDefault:
28
+ requires_context = True
29
+
30
+ def __call__(self, serializer_instance):
31
+ try:
32
+ instance = serializer_instance.view.get_object()
33
+ security = instance.underlying_instrument.parent or instance.underlying_instrument
34
+ return security.parent or security
35
+ except Exception:
36
+ return None
37
+
38
+
39
+ class OrderOrderProposalListModelSerializer(wb_serializers.ModelSerializer):
40
+ underlying_instrument = wb_serializers.SlugRelatedField(read_only=True, slug_field="name")
41
+ underlying_instrument_isin = wb_serializers.CharField(read_only=True)
42
+ underlying_instrument_ticker = wb_serializers.CharField(read_only=True)
43
+ underlying_instrument_refinitiv_identifier_code = wb_serializers.CharField(read_only=True)
44
+ underlying_instrument_instrument_type = wb_serializers.CharField(read_only=True)
45
+
46
+ target_weight = wb_serializers.DecimalField(
47
+ max_digits=Order.ORDER_WEIGHTING_PRECISION + 1,
48
+ decimal_places=Order.ORDER_WEIGHTING_PRECISION,
49
+ required=False,
50
+ default=0,
51
+ )
52
+ effective_weight = wb_serializers.DecimalField(
53
+ read_only=True,
54
+ max_digits=Order.ORDER_WEIGHTING_PRECISION + 1,
55
+ decimal_places=Order.ORDER_WEIGHTING_PRECISION,
56
+ default=0,
57
+ )
58
+
59
+ effective_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
60
+ target_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
61
+
62
+ total_value_fx_portfolio = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=2, default=0)
63
+ effective_total_value_fx_portfolio = wb_serializers.DecimalField(
64
+ read_only=True, max_digits=16, decimal_places=2, default=0
65
+ )
66
+ target_total_value_fx_portfolio = wb_serializers.DecimalField(
67
+ read_only=True, max_digits=16, decimal_places=2, default=0
68
+ )
69
+
70
+ portfolio_currency = wb_serializers.CharField(read_only=True)
71
+ has_warnings = wb_serializers.BooleanField(read_only=True)
72
+
73
+ def validate(self, data):
74
+ data.pop("company", None)
75
+ data.pop("security", None)
76
+ if self.instance and "underlying_instrument" in data:
77
+ raise serializers.ValidationError(
78
+ {
79
+ "underlying_instrument": "You cannot modify the underlying instrument other than creating a new entry"
80
+ }
81
+ )
82
+ effective_weight = self.instance._effective_weight if self.instance else Decimal(0.0)
83
+ weighting = data.get("weighting", self.instance.weighting if self.instance else Decimal(0.0))
84
+ if (target_weight := data.pop("target_weight", None)) is not None:
85
+ weighting = target_weight - effective_weight
86
+ if (target_weight := data.pop("target_weight", None)) is not None:
87
+ weighting = target_weight - effective_weight
88
+ if weighting >= 0:
89
+ data["order_type"] = "BUY"
90
+ else:
91
+ data["order_type"] = "SELL"
92
+ data["weighting"] = weighting
93
+ return super().validate(data)
94
+
95
+ class Meta:
96
+ model = Order
97
+ percent_fields = ["effective_weight", "target_weight", "weighting"]
98
+ decorators = {
99
+ "total_value_fx_portfolio": wb_serializers.decorator(
100
+ decorator_type="text", position="left", value="{{portfolio_currency}}"
101
+ ),
102
+ "effective_total_value_fx_portfolio": wb_serializers.decorator(
103
+ decorator_type="text", position="left", value="{{portfolio_currency}}"
104
+ ),
105
+ "target_total_value_fx_portfolio": wb_serializers.decorator(
106
+ decorator_type="text", position="left", value="{{portfolio_currency}}"
107
+ ),
108
+ }
109
+ read_only_fields = (
110
+ "order_type",
111
+ "shares",
112
+ "effective_shares",
113
+ "target_shares",
114
+ "total_value_fx_portfolio",
115
+ "effective_total_value_fx_portfolio",
116
+ "target_total_value_fx_portfolio",
117
+ "has_warnings",
118
+ )
119
+ fields = (
120
+ "id",
121
+ "shares",
122
+ "underlying_instrument",
123
+ "underlying_instrument_isin",
124
+ "underlying_instrument_ticker",
125
+ "underlying_instrument_refinitiv_identifier_code",
126
+ "underlying_instrument_instrument_type",
127
+ "order_type",
128
+ "comment",
129
+ "effective_weight",
130
+ "target_weight",
131
+ "weighting",
132
+ "order_proposal",
133
+ "order",
134
+ "effective_shares",
135
+ "target_shares",
136
+ "total_value_fx_portfolio",
137
+ "effective_total_value_fx_portfolio",
138
+ "target_total_value_fx_portfolio",
139
+ "portfolio_currency",
140
+ "has_warnings",
141
+ )
142
+
143
+
144
+ class OrderOrderProposalModelSerializer(OrderOrderProposalListModelSerializer):
145
+ company = wb_serializers.PrimaryKeyRelatedField(
146
+ queryset=Instrument.objects.filter(level=0),
147
+ required=False,
148
+ read_only=lambda view: not view.new_mode,
149
+ default=GetCompanyDefault(),
150
+ )
151
+ _company = CompanyRepresentationSerializer(source="company", required=False)
152
+
153
+ security = wb_serializers.PrimaryKeyRelatedField(
154
+ queryset=Instrument.objects.filter(is_security=True),
155
+ required=False,
156
+ read_only=lambda view: not view.new_mode,
157
+ default=GetSecurityDefault(),
158
+ )
159
+ _security = SecurityRepresentationSerializer(
160
+ source="security",
161
+ optional_get_parameters={"company": "parent"},
162
+ depends_on=[{"field": "company", "options": {}}],
163
+ required=False,
164
+ )
165
+ underlying_instrument = wb_serializers.PrimaryKeyRelatedField(
166
+ queryset=Instrument.objects.all(), label="Quote", read_only=lambda view: not view.new_mode
167
+ )
168
+ _underlying_instrument = InvestableInstrumentRepresentationSerializer(
169
+ source="underlying_instrument",
170
+ optional_get_parameters={"security": "parent"},
171
+ depends_on=[{"field": "security", "options": {}}],
172
+ tree_config=BaseTreeGroupLevelOption(clear_filter=True, filter_key="parent"),
173
+ )
174
+
175
+ class Meta(OrderOrderProposalListModelSerializer.Meta):
176
+ fields = list(OrderOrderProposalListModelSerializer.Meta.fields) + [
177
+ "company",
178
+ "_company",
179
+ "security",
180
+ "_security",
181
+ "_underlying_instrument",
182
+ ]
183
+
184
+
185
+ class ReadOnlyOrderOrderProposalModelSerializer(OrderOrderProposalListModelSerializer):
186
+ class Meta(OrderOrderProposalListModelSerializer.Meta):
187
+ read_only_fields = OrderOrderProposalListModelSerializer.Meta.fields
@@ -34,10 +34,10 @@ class PortfolioModelSerializer(wb_serializers.ModelSerializer):
34
34
 
35
35
  last_asset_under_management_usd = wb_serializers.FloatField(read_only=True)
36
36
  last_positions = wb_serializers.FloatField(read_only=True)
37
- last_trade_proposal_date = wb_serializers.DateField(read_only=True)
38
- next_expected_trade_proposal_date = wb_serializers.SerializerMethodField(read_only=True)
37
+ last_order_proposal_date = wb_serializers.DateField(read_only=True)
38
+ next_expected_order_proposal_date = wb_serializers.SerializerMethodField(read_only=True)
39
39
 
40
- def get_next_expected_trade_proposal_date(self, obj):
40
+ def get_next_expected_order_proposal_date(self, obj):
41
41
  if (automatic_rebalancer := getattr(obj, "automatic_rebalancer", None)) and (
42
42
  _d := automatic_rebalancer.get_next_rebalancing_date(date.today())
43
43
  ):
@@ -107,8 +107,8 @@ class PortfolioModelSerializer(wb_serializers.ModelSerializer):
107
107
  args=[instance.id],
108
108
  request=request,
109
109
  )
110
- additional_resources["trade_proposals"] = reverse(
111
- "wbportfolio:portfolio-tradeproposal-list",
110
+ additional_resources["order_proposals"] = reverse(
111
+ "wbportfolio:portfolio-orderproposal-list",
112
112
  args=[instance.id],
113
113
  request=request,
114
114
  )
@@ -169,8 +169,8 @@ class PortfolioModelSerializer(wb_serializers.ModelSerializer):
169
169
  "last_position_date",
170
170
  "last_asset_under_management_usd",
171
171
  "last_positions",
172
- "last_trade_proposal_date",
173
- "next_expected_trade_proposal_date",
172
+ "last_order_proposal_date",
173
+ "next_expected_order_proposal_date",
174
174
  )
175
175
 
176
176
 
@@ -49,7 +49,7 @@ class RebalancerModelSerializer(wb_serializers.ModelSerializer):
49
49
  "computed_str",
50
50
  "_rebalancing_model",
51
51
  "rebalancing_model",
52
- "approve_trade_proposal_automatically",
52
+ "approve_order_proposal_automatically",
53
53
  "activation_date",
54
54
  "frequency",
55
55
  "frequency_repr",
@@ -1,4 +1,3 @@
1
- from .trade_proposals import TradeProposalModelSerializer, ReadOnlyTradeProposalModelSerializer
2
1
  from .claim import (
3
2
  ClaimAccountSerializer,
4
3
  ClaimAPIModelSerializer,
@@ -12,8 +11,5 @@ from .dividends import DividendModelSerializer, DividendRepresentationSerializer
12
11
  from .fees import FeesModelSerializer, FeesRepresentationSerializer
13
12
  from .trades import (
14
13
  TradeModelSerializer,
15
- TradeProposalRepresentationSerializer,
16
- TradeRepresentationSerializer,
17
- TradeTradeProposalModelSerializer,
18
- ReadOnlyTradeTradeProposalModelSerializer,
14
+ TradeRepresentationSerializer
19
15
  )