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

@@ -88,6 +88,8 @@ class TradeImportHandler(ImportExportHandler):
88
88
  1 / (math.pow(10, 4))
89
89
  ) # we need that convertion mechanism otherwise there is floating point approximation error while casting to decimal and get_instance does not work as expected
90
90
  data[field.name] = Decimal(value).quantize(Decimal(str(q)))
91
+ if (target_weight := data.pop("target_weight", None)) is not None:
92
+ data["_target_weight"] = target_weight
91
93
 
92
94
  def _create_instance(self, data: Dict[str, Any], **kwargs) -> models.Model:
93
95
  if "transaction_date" not in data: # we might get only book date and not transaction date
@@ -54,6 +54,8 @@ logger = logging.getLogger("pms")
54
54
  if TYPE_CHECKING:
55
55
  from wbportfolio.models.transactions.trade_proposals import TradeProposal
56
56
 
57
+ MARKET_HOLIDAY_MAX_DURATION = 15
58
+
57
59
 
58
60
  def get_returns(
59
61
  instrument_ids: list[int],
@@ -105,6 +107,7 @@ def get_returns(
105
107
 
106
108
  prices_df = df["close"]
107
109
  fx_rate_df = df["fx_rate"]
110
+
108
111
  returns = prices_to_returns(fx_rate_df * prices_df, drop_inceptions_nan=False, fill_nan=ffill_returns)
109
112
  return {ts.date(): row for ts, row in prices_df.replace(np.nan, None).to_dict("index").items()}, returns.replace(
110
113
  [np.inf, -np.inf, np.nan], 0
@@ -400,7 +403,11 @@ class Portfolio(DeleteToDisableMixin, WBModel):
400
403
  weights = self.get_weights(val_date)
401
404
  return_date = (val_date + BDay(1)).date()
402
405
  _, returns = get_returns(
403
- list(weights.keys()), (val_date - BDay(2)).date(), return_date, to_currency=self.currency, **kwargs
406
+ list(weights.keys()),
407
+ (val_date - BDay(MARKET_HOLIDAY_MAX_DURATION)).date(),
408
+ return_date,
409
+ to_currency=self.currency,
410
+ **kwargs,
404
411
  )
405
412
  if pd.Timestamp(return_date) not in returns.index:
406
413
  raise InvalidAnalyticPortfolio()
@@ -822,7 +829,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
822
829
 
823
830
  prices, returns = get_returns(
824
831
  instrument_ids,
825
- (start_date - BDay(3)).date(),
832
+ (start_date - BDay(MARKET_HOLIDAY_MAX_DURATION)).date(),
826
833
  end_date,
827
834
  to_currency=self.currency,
828
835
  ffill_returns=True,
@@ -397,7 +397,7 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
397
397
  if approve_automatically and self.portfolio.can_be_rebalanced:
398
398
  self.approve(replay=False, broadcast_changes_at_date=broadcast_changes_at_date)
399
399
 
400
- def replay(self, force_reset_trade: bool = False, broadcast_changes_at_date: bool = True):
400
+ def replay(self, broadcast_changes_at_date: bool = True):
401
401
  last_trade_proposal = self
402
402
  last_trade_proposal_created = False
403
403
  while last_trade_proposal and last_trade_proposal.status == TradeProposal.Status.APPROVED:
@@ -405,7 +405,7 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
405
405
  logger.info(f"Replaying trade proposal {last_trade_proposal}")
406
406
  last_trade_proposal.approve_workflow(
407
407
  silent_exception=True,
408
- force_reset_trade=force_reset_trade,
408
+ force_reset_trade=True,
409
409
  broadcast_changes_at_date=broadcast_changes_at_date,
410
410
  )
411
411
  last_trade_proposal.save()
@@ -803,9 +803,8 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
803
803
  ) # we delete the existing portfolio as it has been reverted
804
804
  for trade in self.trades.all():
805
805
  trade.status = Trade.Status.DRAFT
806
- trade.drift_factor = Decimal("1")
807
806
  trades.append(trade)
808
- Trade.objects.bulk_update(trades, ["status", "drift_factor"])
807
+ Trade.objects.bulk_update(trades, ["status"])
809
808
 
810
809
  def can_revert(self):
811
810
  errors = dict()
@@ -439,7 +439,7 @@ class Trade(TransactionMixin, ImportMixin, OrderedModel, models.Model):
439
439
  method=RequestType.PATCH,
440
440
  identifiers=("wbportfolio:trade",),
441
441
  icon=WBIcon.UNDO.icon,
442
- key="reverte",
442
+ key="revert",
443
443
  label="Revert",
444
444
  action_label="revert",
445
445
  # description_fields="<p>Start: {{start}}</p><p>End: {{end}}</p><p>Title: {{title}}</p>",
@@ -518,6 +518,11 @@ class Trade(TransactionMixin, ImportMixin, OrderedModel, models.Model):
518
518
  self, "target_weight", round(self._effective_weight + self.weighting, self.TRADE_WEIGHTING_PRECISION)
519
519
  )
520
520
 
521
+ @_target_weight.setter
522
+ def _target_weight(self, target_weight):
523
+ self.weighting = Decimal(target_weight) - self._effective_weight
524
+ self._set_type()
525
+
521
526
  @property
522
527
  @admin.display(description="Target Shares")
523
528
  def _target_shares(self) -> Decimal:
@@ -558,12 +563,6 @@ class Trade(TransactionMixin, ImportMixin, OrderedModel, models.Model):
558
563
  ]
559
564
  # notification_email_template = "portfolio/email/trade_notification.html"
560
565
 
561
- def __init__(self, *args, target_weight: Decimal | None = None, **kwargs):
562
- super().__init__(*args, **kwargs)
563
- if target_weight is not None: # if target weight is provided, we guess the corresponding weighting
564
- self.weighting = Decimal(target_weight) - self._effective_weight
565
- self._set_type()
566
-
567
566
  def save(self, *args, **kwargs):
568
567
  if abs(self.weighting) < 10e-6:
569
568
  self.weighting = Decimal("0")
@@ -445,14 +445,20 @@ class TradeTradeProposalModelViewSet(
445
445
 
446
446
  agg = {
447
447
  "effective_weight": {
448
- "Cash": format_number(cash_sum_effective_weight, decimal=6),
449
- "Non-Cash": format_number(noncash_sum_effective_weight, decimal=6),
450
- "Total": format_number(noncash_sum_effective_weight + cash_sum_effective_weight, decimal=6),
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
+ ),
451
454
  },
452
455
  "target_weight": {
453
- "Cash": format_number(cash_sum_target_cash_weight, decimal=6),
454
- "Non-Cash": format_number(noncash_sum_target_weight, decimal=6),
455
- "Total": format_number(cash_sum_target_cash_weight + noncash_sum_target_weight, decimal=6),
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
+ ),
456
462
  },
457
463
  "effective_total_value_fx_portfolio": {
458
464
  "Cash": format_number(cash_sum_effective_total_value_fx_portfolio, decimal=6),
@@ -471,9 +477,12 @@ class TradeTradeProposalModelViewSet(
471
477
  ),
472
478
  },
473
479
  "weighting": {
474
- "Cash Flow": format_number(cash_sum_target_cash_weight - cash_sum_effective_weight, decimal=6),
475
- "Buy": format_number(sum_buy_weight, decimal=6),
476
- "Sell": format_number(sum_sell_weight, decimal=6),
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),
477
486
  },
478
487
  "total_value_fx_portfolio": {
479
488
  "Cash Flow": format_number(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.54.9
3
+ Version: 1.54.10
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*
@@ -113,7 +113,7 @@ wbportfolio/import_export/handlers/dividend.py,sha256=F0oLfNt2B_QQAjHBCRpxa5HSkf
113
113
  wbportfolio/import_export/handlers/fees.py,sha256=BOFHAvSTlvVLaxnm6KD_fcza1TlPc02HOR9J0_jjswI,2495
114
114
  wbportfolio/import_export/handlers/portfolio_cash_flow.py,sha256=W7QPNqEvvsq0RS016EAFBp1ezvc6G9Rk-hviRZh8o6Y,2737
115
115
  wbportfolio/import_export/handlers/register.py,sha256=sYyXkE8b1DPZ5monxylZn0kjxLVdNYYZR-p61dwEoDM,2271
116
- wbportfolio/import_export/handlers/trade.py,sha256=oLYNEAFyDeInvHjuAbB4pe60q6VmHX0wGcyuEGofbKM,12700
116
+ wbportfolio/import_export/handlers/trade.py,sha256=_-P1ImDX6jfObm1WKiLtzz7RXEnXjtARAw7cxoHReCM,12826
117
117
  wbportfolio/import_export/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
118
  wbportfolio/import_export/parsers/default_mapping.py,sha256=KrO-X5CvQCeQoBYzFDxavoQGriyUSeI2QDx5ar_zo7A,1405
119
119
  wbportfolio/import_export/parsers/jpmorgan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -257,7 +257,7 @@ wbportfolio/models/asset.py,sha256=b0vPt4LwNrxcMiK7UmBKViYnbNNlZzPTagvU5vFuyrc,4
257
257
  wbportfolio/models/custodians.py,sha256=owTiS2Vm5CRKzh9M_P9GOVg-s-ndQ9UvRmw3yZP7cw0,3815
258
258
  wbportfolio/models/exceptions.py,sha256=3ix0tWUO-O6jpz8f07XIwycw2x3JFRoWzjwil8FVA2Q,52
259
259
  wbportfolio/models/indexes.py,sha256=gvW4K9U9Bj8BmVCqFYdWiXvDWhjHINRON8XhNsZUiQY,639
260
- wbportfolio/models/portfolio.py,sha256=9gbT3tuo73SfhBn4_HIes71feyjTXnZnn9lX2KL_1pQ,57861
260
+ wbportfolio/models/portfolio.py,sha256=2m35YgEsF1gkt_JN5O4UlOtNLAqSfKi8JUp0G-1HPmQ,57997
261
261
  wbportfolio/models/portfolio_cash_flow.py,sha256=uElG7IJUBY8qvtrXftOoskX6EA-dKgEG1JJdvHeWV7g,7336
262
262
  wbportfolio/models/portfolio_cash_targets.py,sha256=WmgG-etPisZsh2yaFQpz7EkpvAudKBEzqPsO715w52U,1498
263
263
  wbportfolio/models/portfolio_relationship.py,sha256=ZGECiPZiLdlk4uSamOrEfuzO0hduK6OMKJLUSnh5_kc,5190
@@ -284,8 +284,8 @@ wbportfolio/models/transactions/claim.py,sha256=SF2FlwG6SRVmA_hT0NbXah5-fYejccWK
284
284
  wbportfolio/models/transactions/dividends.py,sha256=mmOdGWR35yndUMoCuG24Y6BdtxDhSk2gMQ-8LVguqzg,1890
285
285
  wbportfolio/models/transactions/fees.py,sha256=wJtlzbBCAq1UHvv0wqWTE2BEjCF5RMtoaSDS3kODFRo,7112
286
286
  wbportfolio/models/transactions/rebalancing.py,sha256=rwePcmTZOYgfSWnBQcBrZ3DQHRJ3w17hdO_hgrRbbhI,7696
287
- wbportfolio/models/transactions/trade_proposals.py,sha256=2EGi03APBC9QWU99LVRA1e-_-o8AUtEW332Iuxvb-_E,38214
288
- wbportfolio/models/transactions/trades.py,sha256=vTCoSBAmd6FPFyDRm5fqTqbxwzcH3ApWn0Dz2tkCCR8,34132
287
+ wbportfolio/models/transactions/trade_proposals.py,sha256=UYhV2pjQE4V-k0tVTBeZgPPK0C-tBeY345iroDzulfg,38106
288
+ wbportfolio/models/transactions/trades.py,sha256=oAUWCPPdBcMf5bcYI6BwvnrFKK588ZLRHPyPr65h-0U,33966
289
289
  wbportfolio/models/transactions/transactions.py,sha256=XTcUeMUfkf5XTSZaR2UAyGqCVkOhQYk03_vzHLIgf8Q,3807
290
290
  wbportfolio/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
291
291
  wbportfolio/pms/typing.py,sha256=BV4dzazNHdfpfLV99bLVyYGcETmbQSnFV6ipc4fNKfg,8470
@@ -521,8 +521,8 @@ wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0
521
521
  wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
522
522
  wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQVTN2MH1kG_yCeVkmyK8k,1263
523
523
  wbportfolio/viewsets/transactions/trade_proposals.py,sha256=kQCojTNKBEyn2NcenL3a9auzBH4sIgLEx8rLAYCGLGg,6161
524
- wbportfolio/viewsets/transactions/trades.py,sha256=GHOw5jtcqoaHiRrxxxL29c9405QiPisEn4coGELKDrE,22146
525
- wbportfolio-1.54.9.dist-info/METADATA,sha256=SL5iDKkrof3J3-X4ykL9jJslbUo8ltmEwJzni_emoXY,702
526
- wbportfolio-1.54.9.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
527
- wbportfolio-1.54.9.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
528
- wbportfolio-1.54.9.dist-info/RECORD,,
524
+ wbportfolio/viewsets/transactions/trades.py,sha256=Y8v2cM0vpspHysaAvu8qqhzt86dNtb2Q3puo4HCJsTI,22629
525
+ wbportfolio-1.54.10.dist-info/METADATA,sha256=uKqFJps-zs7857cq_8X_JmogU7gB5LG3RaZORctTI-k,703
526
+ wbportfolio-1.54.10.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
527
+ wbportfolio-1.54.10.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
528
+ wbportfolio-1.54.10.dist-info/RECORD,,