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

@@ -0,0 +1,59 @@
1
+ from decimal import Decimal
2
+ from typing import Any, Dict
3
+
4
+ from django.db import models
5
+ from wbcore.contrib.io.exceptions import DeserializationError
6
+ from wbcore.contrib.io.imports import ImportExportHandler, ImportState
7
+ from wbcore.contrib.io.utils import nest_row
8
+ from wbfdm.import_export.handlers.instrument import InstrumentImportHandler
9
+
10
+ from wbportfolio.pms.typing import Portfolio, Position
11
+
12
+
13
+ class OrderImportHandler(ImportExportHandler):
14
+ MODEL_APP_LABEL: str = "wbportfolio.Order"
15
+
16
+ def __init__(self, *args, **kwargs):
17
+ super().__init__(*args, **kwargs)
18
+ self.instrument_handler = InstrumentImportHandler(self.import_source)
19
+ self.order_proposal = None
20
+
21
+ def process_object(
22
+ self,
23
+ data: Dict[str, Any],
24
+ **kwargs,
25
+ ):
26
+ from wbportfolio.models import OrderProposal
27
+
28
+ data = nest_row(data)
29
+ underlying_instrument = self.instrument_handler.process_object(
30
+ data["underlying_instrument"], only_security=False, read_only=True
31
+ )[0]
32
+ self.order_proposal = OrderProposal.objects.get(id=data.pop("order_proposal_id"))
33
+ weighting = data.get("target_weight", data.get("weighting"))
34
+ shares = data.get("target_shares", data.get("shares", 0))
35
+ if not weighting:
36
+ raise DeserializationError("We couldn't figure out the target weight column")
37
+ position_dto = Position(
38
+ underlying_instrument=underlying_instrument.id,
39
+ instrument_type=underlying_instrument.instrument_type.id,
40
+ weighting=Decimal(weighting),
41
+ shares=Decimal(shares),
42
+ currency=underlying_instrument.currency,
43
+ date=self.order_proposal.trade_date,
44
+ is_cash=underlying_instrument.is_cash,
45
+ )
46
+ return position_dto, ImportState.CREATED
47
+
48
+ def _get_history(self, history: Dict[str, Any]) -> models.QuerySet:
49
+ from wbportfolio.models.orders.order_proposals import OrderProposal
50
+
51
+ if order_proposal_id := history.get("order_proposal_id"):
52
+ # if a order proposal is provided, we delete the existing history first as otherwise, it would mess with the target weight computation
53
+ order_proposal = OrderProposal.objects.get(id=order_proposal_id)
54
+ order_proposal.orders.all().delete()
55
+ return self.model.objects.none()
56
+
57
+ def _post_processing_objects(self, positions: list[Position], *args, **kwargs):
58
+ target_portfolio = Portfolio(positions)
59
+ self.order_proposal.reset_orders(target_portfolio=target_portfolio)
@@ -25,7 +25,6 @@ class TradeImportHandler(ImportExportHandler):
25
25
  self.instrument_handler = InstrumentImportHandler(self.import_source)
26
26
  self.register_handler = RegisterImportHandler(self.import_source)
27
27
  self.currency_handler = CurrencyImportHandler(self.import_source)
28
- self.order_proposals = set()
29
28
 
30
29
  def _data_changed(self, _object, change_data: Dict[str, Any], initial_data: Dict[str, Any], **kwargs):
31
30
  if (new_register := change_data.get("register")) and (current_register := _object.register):
@@ -35,41 +34,32 @@ class TradeImportHandler(ImportExportHandler):
35
34
  return super()._data_changed(_object, change_data, initial_data, **kwargs)
36
35
 
37
36
  def _deserialize(self, data: Dict[str, Any]):
38
- from wbportfolio.models import OrderProposal, Product
37
+ from wbportfolio.models import Product
39
38
 
40
39
  if underlying_instrument := data.get("underlying_instrument", None):
41
40
  data["underlying_instrument"] = self.instrument_handler.process_object(
42
41
  underlying_instrument, only_security=False, read_only=True
43
42
  )[0]
44
43
 
45
- if order_proposal_id := data.pop("order_proposal_id", None):
46
- order_proposal = OrderProposal.objects.get(id=order_proposal_id)
47
- self.order_proposals.add(order_proposal)
48
- data["value_date"] = order_proposal.last_effective_date
49
- data["transaction_date"] = order_proposal.trade_date
50
- data["order_proposal"] = order_proposal
51
- data["portfolio"] = order_proposal.portfolio
52
- data["status"] = "DRAFT"
53
- else:
54
- if external_id_alternative := data.get("external_id_alternative", None):
55
- data["external_id_alternative"] = str(external_id_alternative)
56
- if transaction_date_str := data.get("transaction_date", None):
57
- data["transaction_date"] = datetime.strptime(transaction_date_str, "%Y-%m-%d").date()
58
- if value_date_str := data.get("value_date", None):
59
- data["value_date"] = datetime.strptime(value_date_str, "%Y-%m-%d").date()
60
- if book_date_str := data.get("book_date", None):
61
- data["book_date"] = datetime.strptime(book_date_str, "%Y-%m-%d").date()
62
- data["portfolio"] = Portfolio._get_or_create_portfolio(
63
- self.instrument_handler, data.get("portfolio", data["underlying_instrument"])
64
- )
44
+ if external_id_alternative := data.get("external_id_alternative", None):
45
+ data["external_id_alternative"] = str(external_id_alternative)
46
+ if transaction_date_str := data.get("transaction_date", None):
47
+ data["transaction_date"] = datetime.strptime(transaction_date_str, "%Y-%m-%d").date()
48
+ if value_date_str := data.get("value_date", None):
49
+ data["value_date"] = datetime.strptime(value_date_str, "%Y-%m-%d").date()
50
+ if book_date_str := data.get("book_date", None):
51
+ data["book_date"] = datetime.strptime(book_date_str, "%Y-%m-%d").date()
52
+ data["portfolio"] = Portfolio._get_or_create_portfolio(
53
+ self.instrument_handler, data.get("portfolio", data["underlying_instrument"])
54
+ )
65
55
 
66
- if currency_data := data.get("currency", None):
67
- data["currency"] = self.currency_handler.process_object(currency_data, read_only=True)[0]
56
+ if currency_data := data.get("currency", None):
57
+ data["currency"] = self.currency_handler.process_object(currency_data, read_only=True)[0]
68
58
 
69
- if register_data := data.get("register", None):
70
- data["register"] = self.register_handler.process_object(register_data)[0]
59
+ if register_data := data.get("register", None):
60
+ data["register"] = self.register_handler.process_object(register_data)[0]
71
61
 
72
- data["marked_for_deletion"] = data.get("marked_for_deletion", False)
62
+ data["marked_for_deletion"] = data.get("marked_for_deletion", False)
73
63
  if underlying_instrument := data.get("underlying_instrument"):
74
64
  if nominal := data.pop("nominal", None):
75
65
  try:
@@ -160,39 +150,30 @@ class TradeImportHandler(ImportExportHandler):
160
150
  self.import_source.log += "\nNo trade was successfully matched."
161
151
 
162
152
  def _get_history(self, history: Dict[str, Any]) -> models.QuerySet:
163
- from wbportfolio.models.orders.order_proposals import OrderProposal
164
-
165
- if order_proposal_id := history.get("order_proposal_id"):
166
- # if a order proposal is provided, we delete the existing history first as otherwise, it would mess with the target weight computation
167
- order_proposal = OrderProposal.objects.get(id=order_proposal_id)
168
- order_proposal.trades.all().delete()
169
- order_proposal.reset_orders()
170
- trades = self.model.objects.none()
171
- else:
172
- trades = self.model.objects.filter(
173
- exclude_from_history=False,
174
- pending=False,
175
- transaction_subtype__in=[
176
- self.model.Type.SUBSCRIPTION,
177
- self.model.Type.REDEMPTION,
178
- ], # we cannot exclude marked for deleted trade because otherwise they are never consider in the history
179
- )
180
- if transaction_date := history.get("transaction_date"):
181
- trades = trades.filter(transaction_date__lte=transaction_date)
182
- elif book_date := history.get("book_date"):
183
- trades = trades.filter(book_date__lte=book_date)
184
- if underlying_instrument_data := history.get("underlying_instrument"):
185
- if isinstance(underlying_instrument_data, dict):
186
- trades = trades.filter(
187
- **{f"underlying_instrument__{k}": v for k, v in underlying_instrument_data.items()}
188
- )
189
- else:
190
- trades = trades.filter(underlying_instrument__id=underlying_instrument_data)
191
-
192
- elif "underlying_instruments" in history:
193
- trades = trades.filter(underlying_instrument__id__in=history["underlying_instruments"])
153
+ trades = self.model.objects.filter(
154
+ exclude_from_history=False,
155
+ pending=False,
156
+ transaction_subtype__in=[
157
+ self.model.Type.SUBSCRIPTION,
158
+ self.model.Type.REDEMPTION,
159
+ ], # we cannot exclude marked for deleted trade because otherwise they are never consider in the history
160
+ )
161
+ if transaction_date := history.get("transaction_date"):
162
+ trades = trades.filter(transaction_date__lte=transaction_date)
163
+ elif book_date := history.get("book_date"):
164
+ trades = trades.filter(book_date__lte=book_date)
165
+ if underlying_instrument_data := history.get("underlying_instrument"):
166
+ if isinstance(underlying_instrument_data, dict):
167
+ trades = trades.filter(
168
+ **{f"underlying_instrument__{k}": v for k, v in underlying_instrument_data.items()}
169
+ )
194
170
  else:
195
- raise ValueError("We cannot estimate history without at least the underlying instrument")
171
+ trades = trades.filter(underlying_instrument__id=underlying_instrument_data)
172
+
173
+ elif "underlying_instruments" in history:
174
+ trades = trades.filter(underlying_instrument__id__in=history["underlying_instruments"])
175
+ else:
176
+ raise ValueError("We cannot estimate history without at least the underlying instrument")
196
177
  return trades
197
178
 
198
179
  def _post_processing_objects(
@@ -201,18 +182,12 @@ class TradeImportHandler(ImportExportHandler):
201
182
  modified_objs: list[models.Model],
202
183
  unmodified_objs: list[models.Model],
203
184
  ):
204
- from wbportfolio.models.orders.order_proposals import replay_as_task
205
-
206
185
  for instrument in set(
207
186
  map(lambda x: x.underlying_instrument, filter(lambda t: t.is_customer_trade, created_objs + modified_objs))
208
187
  ):
209
188
  if instrument.instrument_type.key == "product":
210
189
  update_outstanding_shares_as_task.delay(instrument.id)
211
190
 
212
- # if the trade import relates to a order proposal, we reset the TP after the import to ensure it contains the deleted positions (often forgotten by user)
213
- for changed_order_proposal in self.order_proposals:
214
- replay_as_task.delay(changed_order_proposal.id)
215
-
216
191
  def _post_processing_updated_object(self, _object):
217
192
  if _object.marked_for_deletion:
218
193
  _object.marked_for_deletion = False
@@ -231,8 +206,5 @@ class TradeImportHandler(ImportExportHandler):
231
206
  self.import_source.log += (
232
207
  f"{trade.transaction_date:%d.%m.%Y}: {trade.shares} {trade.bank} ==> Marked for deletion"
233
208
  )
234
- if trade.order_proposal:
235
- trade.delete()
236
- else:
237
- trade.marked_for_deletion = True
238
- trade.save()
209
+ trade.marked_for_deletion = True
210
+ trade.save()
@@ -394,35 +394,34 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
394
394
  orders = service.trades_batch.trades_map.values()
395
395
  for order_dto in orders:
396
396
  instrument = Instrument.objects.get(id=order_dto.underlying_instrument)
397
- if not instrument.is_cash: # we do not save order that includes cash component
398
- currency_fx_rate = instrument.currency.convert(
399
- self.value_date, self.portfolio.currency, exact_lookup=True
397
+ currency_fx_rate = instrument.currency.convert(
398
+ self.value_date, self.portfolio.currency, exact_lookup=True
399
+ )
400
+ # we cannot do a bulk-create because Order is a multi table inheritance
401
+ weighting = round(order_dto.delta_weight, Order.ORDER_WEIGHTING_PRECISION)
402
+ daily_return = order_dto.daily_return
403
+ try:
404
+ order = self.orders.get(underlying_instrument=instrument)
405
+ order.weighting = weighting
406
+ order.currency_fx_rate = currency_fx_rate
407
+ order.daily_return = daily_return
408
+ except Order.DoesNotExist:
409
+ order = Order(
410
+ underlying_instrument=instrument,
411
+ order_proposal=self,
412
+ value_date=self.trade_date,
413
+ weighting=weighting,
414
+ daily_return=daily_return,
415
+ currency_fx_rate=currency_fx_rate,
400
416
  )
401
- # we cannot do a bulk-create because Order is a multi table inheritance
402
- weighting = round(order_dto.delta_weight, Order.ORDER_WEIGHTING_PRECISION)
403
- daily_return = order_dto.daily_return
404
- try:
405
- order = self.orders.get(underlying_instrument=instrument)
406
- order.weighting = weighting
407
- order.currency_fx_rate = currency_fx_rate
408
- order.daily_return = daily_return
409
- except Order.DoesNotExist:
410
- order = Order(
411
- underlying_instrument=instrument,
412
- order_proposal=self,
413
- value_date=self.trade_date,
414
- weighting=weighting,
415
- daily_return=daily_return,
416
- currency_fx_rate=currency_fx_rate,
417
- )
418
- order.price = order.get_price()
419
- order.order_type = Order.get_type(weighting, order_dto.previous_weight, order_dto.target_weight)
420
- # if we cannot automatically find a price, we consider the stock is invalid and we sell it
421
- if not order.price:
422
- order.price = Decimal("0.0")
423
- order.weighting = -order_dto.effective_weight
424
-
425
- order.save()
417
+ order.price = order.get_price()
418
+ order.order_type = Order.get_type(weighting, order_dto.previous_weight, order_dto.target_weight)
419
+ # if we cannot automatically find a price, we consider the stock is invalid and we sell it
420
+ if not order.price:
421
+ order.price = Decimal("0.0")
422
+ order.weighting = -order_dto.effective_weight
423
+
424
+ order.save()
426
425
  # final sanity check to make sure invalid order with effective and target weight of 0 are automatically removed:
427
426
  self.get_orders().filter(target_weight=0, effective_weight=0).delete()
428
427
 
@@ -579,7 +578,9 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
579
578
  currency = self.portfolio.currency
580
579
 
581
580
  # Calculate the total target weight of all orders
582
- total_target_weight = orders.aggregate(s=models.Sum("target_weight"))["s"] or Decimal(0)
581
+ total_target_weight = orders.exclude(underlying_instrument__is_cash=True).aggregate(
582
+ s=models.Sum("target_weight")
583
+ )["s"] or Decimal(0)
583
584
 
584
585
  if target_cash_weight is None:
585
586
  target_cash_weight = Decimal("1") - total_target_weight
@@ -9,13 +9,13 @@ from django.db.models import (
9
9
  from ordered_model.models import OrderedModel
10
10
  from wbcore.contrib.io.mixins import ImportMixin
11
11
 
12
- from wbportfolio.import_export.handlers.trade import TradeImportHandler
12
+ from wbportfolio.import_export.handlers.orders import OrderImportHandler
13
13
  from wbportfolio.models.asset import AssetPosition
14
14
  from wbportfolio.models.transactions.transactions import TransactionMixin
15
15
 
16
16
 
17
17
  class Order(TransactionMixin, ImportMixin, OrderedModel, models.Model):
18
- import_export_handler_class = TradeImportHandler
18
+ import_export_handler_class = OrderImportHandler
19
19
 
20
20
  ORDER_WEIGHTING_PRECISION = (
21
21
  8 # we need to match the asset position weighting. Skfolio advices using a even smaller number (5)
@@ -145,11 +145,6 @@ class Order(TransactionMixin, ImportMixin, OrderedModel, models.Model):
145
145
  self, "target_weight", round(self._effective_weight + self.weighting, self.ORDER_WEIGHTING_PRECISION)
146
146
  )
147
147
 
148
- @_target_weight.setter
149
- def _target_weight(self, target_weight):
150
- self.weighting = Decimal(target_weight) - self._effective_weight
151
- self.order_type = self.get_type(self.weighting, self._previous_weight, self._target_weight)
152
-
153
148
  @property
154
149
  @admin.display(description="Target Shares")
155
150
  def _target_shares(self) -> Decimal:
@@ -248,7 +248,9 @@ class TestOrderProposal:
248
248
  assert t3.weighting == normalized_t3_weight
249
249
 
250
250
  # Test resetting orders
251
- def test_reset_orders(self, order_proposal, instrument_factory, instrument_price_factory, asset_position_factory):
251
+ def test_reset_orders(
252
+ self, order_proposal, instrument_factory, cash, instrument_price_factory, asset_position_factory
253
+ ):
252
254
  """
253
255
  Verify orders are correctly reset based on effective and target portfolios.
254
256
  """
@@ -334,6 +336,31 @@ class TestOrderProposal:
334
336
  assert t2.weighting == Decimal("0")
335
337
  assert t3.weighting == Decimal("0.5")
336
338
 
339
+ # assert cash position creates a proper order
340
+ # build the target portfolio
341
+ target_portfolio_with_cash = PortfolioDTO(
342
+ [
343
+ Position(
344
+ underlying_instrument=i1.id,
345
+ date=order_proposal.trade_date,
346
+ weighting=Decimal("0.5"),
347
+ price=float(p1.net_value),
348
+ ),
349
+ Position(
350
+ underlying_instrument=cash.id,
351
+ date=order_proposal.trade_date,
352
+ weighting=Decimal("0.5"),
353
+ price=1.0,
354
+ ),
355
+ ]
356
+ )
357
+ order_proposal.reset_orders(target_portfolio=target_portfolio_with_cash)
358
+
359
+ # Assert existing trade weights are correctly updated
360
+ assert order_proposal.orders.get(underlying_instrument=i1).weighting == Decimal("-0.2")
361
+ assert order_proposal.orders.get(underlying_instrument=i2).weighting == Decimal("-0.3")
362
+ assert order_proposal.orders.get(underlying_instrument=cash).weighting == Decimal("0.5")
363
+
337
364
  def test_reset_orders_remove_invalid_orders(self, order_proposal, order_factory, instrument_price_factory):
338
365
  # create a invalid trade and its price
339
366
  invalid_trade = order_factory.create(order_proposal=order_proposal, weighting=Decimal(0))
@@ -423,13 +450,20 @@ class TestOrderProposal:
423
450
  order_proposal.portfolio.only_weighting = False
424
451
  order_proposal.portfolio.save()
425
452
  mock_fct.return_value = Decimal(1_000_000) # 1 million cash
453
+ cash = cash_factory.create(currency=order_proposal.portfolio.currency)
426
454
  order_factory.create( # equity trade
427
455
  order_proposal=order_proposal,
428
456
  value_date=order_proposal.trade_date,
429
457
  portfolio=order_proposal.portfolio,
430
458
  weighting=Decimal("0.7"),
431
459
  )
432
-
460
+ order_factory.create( # cash trade
461
+ order_proposal=order_proposal,
462
+ value_date=order_proposal.trade_date,
463
+ portfolio=order_proposal.portfolio,
464
+ underlying_instrument=cash,
465
+ weighting=Decimal("0.2"),
466
+ )
433
467
  target_cash_position = order_proposal.get_estimated_target_cash()
434
468
  assert target_cash_position.weighting == Decimal("0.3")
435
469
  assert target_cash_position.initial_shares == Decimal(1_000_000) * Decimal("0.3")
@@ -198,22 +198,26 @@ class OrderOrderProposalModelViewSet(
198
198
  return Order.objects.none()
199
199
 
200
200
  def get_queryset(self):
201
- return self.orders.annotate(
202
- underlying_instrument_isin=F("underlying_instrument__isin"),
203
- underlying_instrument_ticker=F("underlying_instrument__ticker"),
204
- underlying_instrument_refinitiv_identifier_code=F("underlying_instrument__refinitiv_identifier_code"),
205
- underlying_instrument_instrument_type=Case(
206
- When(
207
- underlying_instrument__parent__is_security=True,
208
- then=F("underlying_instrument__parent__instrument_type__short_name"),
201
+ return (
202
+ self.orders.exclude(underlying_instrument__is_cash=True)
203
+ .annotate(
204
+ underlying_instrument_isin=F("underlying_instrument__isin"),
205
+ underlying_instrument_ticker=F("underlying_instrument__ticker"),
206
+ underlying_instrument_refinitiv_identifier_code=F("underlying_instrument__refinitiv_identifier_code"),
207
+ underlying_instrument_instrument_type=Case(
208
+ When(
209
+ underlying_instrument__parent__is_security=True,
210
+ then=F("underlying_instrument__parent__instrument_type__short_name"),
211
+ ),
212
+ default=F("underlying_instrument__instrument_type__short_name"),
209
213
  ),
210
- default=F("underlying_instrument__instrument_type__short_name"),
211
- ),
212
- effective_total_value_fx_portfolio=F("effective_weight") * Value(self.portfolio_total_asset_value),
213
- target_total_value_fx_portfolio=F("target_weight") * Value(self.portfolio_total_asset_value),
214
- portfolio_currency=F("portfolio__currency__symbol"),
215
- security=F("underlying_instrument__parent"),
216
- company=F("underlying_instrument__parent__parent"),
217
- ).select_related(
218
- "underlying_instrument", "underlying_instrument__parent", "underlying_instrument__parent__parent"
214
+ effective_total_value_fx_portfolio=F("effective_weight") * Value(self.portfolio_total_asset_value),
215
+ target_total_value_fx_portfolio=F("target_weight") * Value(self.portfolio_total_asset_value),
216
+ portfolio_currency=F("portfolio__currency__symbol"),
217
+ security=F("underlying_instrument__parent"),
218
+ company=F("underlying_instrument__parent__parent"),
219
+ )
220
+ .select_related(
221
+ "underlying_instrument", "underlying_instrument__parent", "underlying_instrument__parent__parent"
222
+ )
219
223
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.54.16
3
+ Version: 1.54.18
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*
@@ -119,9 +119,10 @@ wbportfolio/import_export/handlers/adjustment.py,sha256=6bdTIYFmc8_HFxcdwtnYwglM
119
119
  wbportfolio/import_export/handlers/asset_position.py,sha256=UZBDlEK5mxtGu9cZvwiQS6J8GNkMJqDZb7KZ3uwex0g,8698
120
120
  wbportfolio/import_export/handlers/dividend.py,sha256=F0oLfNt2B_QQAjHBCRpxa5HSkfkAYdal_NjLJGtVckY,4408
121
121
  wbportfolio/import_export/handlers/fees.py,sha256=BOFHAvSTlvVLaxnm6KD_fcza1TlPc02HOR9J0_jjswI,2495
122
+ wbportfolio/import_export/handlers/orders.py,sha256=gECIGItR63oNOWXKgImdjJv6drGgzJOcJG0bu5vygr4,2565
122
123
  wbportfolio/import_export/handlers/portfolio_cash_flow.py,sha256=W7QPNqEvvsq0RS016EAFBp1ezvc6G9Rk-hviRZh8o6Y,2737
123
124
  wbportfolio/import_export/handlers/register.py,sha256=sYyXkE8b1DPZ5monxylZn0kjxLVdNYYZR-p61dwEoDM,2271
124
- wbportfolio/import_export/handlers/trade.py,sha256=X_P8T3ZBGYpPmKrNmgAL9HZ-yfdnIh23LupsLMDxJ4o,12814
125
+ wbportfolio/import_export/handlers/trade.py,sha256=g2jAYYeuhZv_DuvM6zlROcq6rUlSbGaQ3tO4u6wkSRU,11140
125
126
  wbportfolio/import_export/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
127
  wbportfolio/import_export/parsers/default_mapping.py,sha256=KrO-X5CvQCeQoBYzFDxavoQGriyUSeI2QDx5ar_zo7A,1405
127
128
  wbportfolio/import_export/parsers/jpmorgan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -288,8 +289,8 @@ wbportfolio/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
288
289
  wbportfolio/models/mixins/instruments.py,sha256=SgBreTpa_X3uyCWo7t8B0VaTtl49IjmBMe4Pab6TjAM,6796
289
290
  wbportfolio/models/mixins/liquidity_stress_test.py,sha256=iQVzT3QM7VtHnqfj9gT6KUIe4wC4MJXery-AXJHUYns,58820
290
291
  wbportfolio/models/orders/__init__.py,sha256=EH9UacGR3npBMje5FGTeLOh1xqFBh9kc24WbGmBIA3g,69
291
- wbportfolio/models/orders/order_proposals.py,sha256=uo0hWOjY6lKpGwa9fnBvskQmI0S_Usg7LYbY9PCJ4xo,40333
292
- wbportfolio/models/orders/orders.py,sha256=hVVw7NAFmAFHosMMs39V9DjGmWyFC_msSxF8rpDDG60,9683
292
+ wbportfolio/models/orders/order_proposals.py,sha256=U1vLwI_yS2E1TUs04NJGperusqmNyiE42XGlAVoPatk,40196
293
+ wbportfolio/models/orders/orders.py,sha256=Rl54ON-b8K277rKeXf53-PHn98nQFYBl4Q_I0Bo6WqE,9438
293
294
  wbportfolio/models/reconciliations/__init__.py,sha256=MXH5fZIPGDRBgJkO6wVu_NLRs8fkP1im7G6d-h36lQY,127
294
295
  wbportfolio/models/reconciliations/account_reconciliation_lines.py,sha256=QP6M7hMcyFbuXBa55Y-azui6Dl_WgbzMntEqWzQkbfM,7394
295
296
  wbportfolio/models/reconciliations/account_reconciliations.py,sha256=rofSxetFfEJov6mPyoTvGxELA16HILyJZtQvm_kwYU0,4405
@@ -403,7 +404,7 @@ wbportfolio/tests/models/test_roles.py,sha256=4Cn7WyrA2ztJNeWLk5cy9kYo5XLWMbFSvo
403
404
  wbportfolio/tests/models/test_splits.py,sha256=ytKcHsI_90kj1L4s8It-KEcc24rkDcElxwQ8q0QxEvk,9689
404
405
  wbportfolio/tests/models/utils.py,sha256=ORNJq6NMo1Za22jGZXfTfKeNEnTRlfEt_8SJ6xLaQWg,325
405
406
  wbportfolio/tests/models/orders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
406
- wbportfolio/tests/models/orders/test_order_proposals.py,sha256=xVoKYHhzm_UihJuEY5P8G1kUURCYYSnyFVAR4aPFmUA,29353
407
+ wbportfolio/tests/models/orders/test_order_proposals.py,sha256=7OvZn9IhTNQ66lm2nLXhrdc9OzGuklrnbRgp2WeYQms,30802
407
408
  wbportfolio/tests/models/transactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
408
409
  wbportfolio/tests/models/transactions/test_claim.py,sha256=NG3BKB-FVcIDgHSJHCjImxgMM3ISVUMl24xUPmEcPec,5570
409
410
  wbportfolio/tests/models/transactions/test_fees.py,sha256=tAp18x2wCNQr11LUnLtHNbBDbbX0v1DZnmW7i-cEi5Q,2423
@@ -532,7 +533,7 @@ wbportfolio/viewsets/configs/titles/roles.py,sha256=9LoJa3jgenXJ5UWRlIErTzdbjpSW
532
533
  wbportfolio/viewsets/configs/titles/trades.py,sha256=29XCLxvY0Xe3a2tjCno3tN2rRXCr9RWpbWnzurJfnYI,1986
533
534
  wbportfolio/viewsets/orders/__init__.py,sha256=N8v9jdEXryOzrLlc7ML3iBCO2lmNXph9_TWoQ7PTvi4,195
534
535
  wbportfolio/viewsets/orders/order_proposals.py,sha256=mw385zzU52nCq8p6xgionh43xLmOn5aX-2BPlCHnqlE,6214
535
- wbportfolio/viewsets/orders/orders.py,sha256=WH96S97MjZvCudP4b2Hp7YZPmdKfLNGKi90HY3oVQYY,10708
536
+ wbportfolio/viewsets/orders/orders.py,sha256=mekuUVkG9-tEtWGrIW2QfgTOuwOZWONiRnbztYUmcVM,10875
536
537
  wbportfolio/viewsets/orders/configs/__init__.py,sha256=5MU57JXiKi32_PicHtiNr7YHmMN020FrlF5NFJf_Wds,94
537
538
  wbportfolio/viewsets/orders/configs/buttons/__init__.py,sha256=EHzNmAfa0UQFITEF-wxj_s4wn3Y5DE3DCbEUmmvCTIs,106
538
539
  wbportfolio/viewsets/orders/configs/buttons/order_proposals.py,sha256=Q_7LrsuLzjSXCIoscFQXMMHl8cuCporDNM5k735W7d8,3584
@@ -550,7 +551,7 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
550
551
  wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
551
552
  wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
552
553
  wbportfolio/viewsets/transactions/trades.py,sha256=xBgOGaJ8aEg-2RxEJ4FDaBs4SGwuLasun3nhpis0WQY,12363
553
- wbportfolio-1.54.16.dist-info/METADATA,sha256=ikKYoJRibnFOVA5vPklrR-QM2077_-0GboeKE0Xfp1A,703
554
- wbportfolio-1.54.16.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
555
- wbportfolio-1.54.16.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
556
- wbportfolio-1.54.16.dist-info/RECORD,,
554
+ wbportfolio-1.54.18.dist-info/METADATA,sha256=az3x7f9w0PGxU-naki13snEuZMzaKqZhC6Dv82qwcJE,703
555
+ wbportfolio-1.54.18.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
556
+ wbportfolio-1.54.18.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
557
+ wbportfolio-1.54.18.dist-info/RECORD,,