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.
- wbportfolio/import_export/handlers/trade.py +2 -0
- wbportfolio/models/portfolio.py +9 -2
- wbportfolio/models/transactions/trade_proposals.py +3 -4
- wbportfolio/models/transactions/trades.py +6 -7
- wbportfolio/viewsets/transactions/trades.py +18 -9
- {wbportfolio-1.54.9.dist-info → wbportfolio-1.54.10.dist-info}/METADATA +1 -1
- {wbportfolio-1.54.9.dist-info → wbportfolio-1.54.10.dist-info}/RECORD +9 -9
- {wbportfolio-1.54.9.dist-info → wbportfolio-1.54.10.dist-info}/WHEEL +0 -0
- {wbportfolio-1.54.9.dist-info → wbportfolio-1.54.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
wbportfolio/models/portfolio.py
CHANGED
|
@@ -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()),
|
|
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(
|
|
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,
|
|
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=
|
|
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"
|
|
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="
|
|
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=
|
|
449
|
-
"Non-Cash": format_number(noncash_sum_effective_weight, decimal=
|
|
450
|
-
"Total": format_number(
|
|
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=
|
|
454
|
-
"Non-Cash": format_number(noncash_sum_target_weight, decimal=
|
|
455
|
-
"Total": format_number(
|
|
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(
|
|
475
|
-
|
|
476
|
-
|
|
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(
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
288
|
-
wbportfolio/models/transactions/trades.py,sha256=
|
|
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=
|
|
525
|
-
wbportfolio-1.54.
|
|
526
|
-
wbportfolio-1.54.
|
|
527
|
-
wbportfolio-1.54.
|
|
528
|
-
wbportfolio-1.54.
|
|
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,,
|
|
File without changes
|
|
File without changes
|