wbportfolio 1.54.6__py2.py3-none-any.whl → 1.54.7__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/models/asset.py +5 -5
- wbportfolio/models/portfolio.py +31 -18
- wbportfolio/models/transactions/trade_proposals.py +8 -1
- wbportfolio/models/transactions/trades.py +6 -4
- wbportfolio/serializers/transactions/trades.py +14 -3
- wbportfolio/tests/models/test_portfolios.py +8 -2
- wbportfolio/tests/models/transactions/test_trade_proposals.py +33 -7
- wbportfolio/viewsets/transactions/trade_proposals.py +1 -2
- {wbportfolio-1.54.6.dist-info → wbportfolio-1.54.7.dist-info}/METADATA +1 -1
- {wbportfolio-1.54.6.dist-info → wbportfolio-1.54.7.dist-info}/RECORD +12 -12
- {wbportfolio-1.54.6.dist-info → wbportfolio-1.54.7.dist-info}/WHEEL +0 -0
- {wbportfolio-1.54.6.dist-info → wbportfolio-1.54.7.dist-info}/licenses/LICENSE +0 -0
wbportfolio/models/asset.py
CHANGED
|
@@ -112,10 +112,10 @@ class AssetPositionIterator:
|
|
|
112
112
|
return fx_rate
|
|
113
113
|
|
|
114
114
|
def _get_price(self, val_date: date, instrument: Instrument) -> float | None:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
try:
|
|
116
|
+
return self._prices[val_date][instrument.id]
|
|
117
|
+
except KeyError:
|
|
118
|
+
return None
|
|
119
119
|
|
|
120
120
|
def _dict_to_model(self, val_date: date, instrument_id: int, weighting: float, **kwargs) -> "AssetPosition":
|
|
121
121
|
underlying_quote = self._get_instrument(instrument_id)
|
|
@@ -585,7 +585,7 @@ class AssetPosition(ImportMixin, models.Model):
|
|
|
585
585
|
|
|
586
586
|
if not getattr(self, "currency", None):
|
|
587
587
|
self.currency = self.underlying_quote.currency
|
|
588
|
-
if not self.underlying_quote_price and infer_underlying_quote_price:
|
|
588
|
+
if not self.underlying_quote_price and (infer_underlying_quote_price or not self.initial_price):
|
|
589
589
|
try:
|
|
590
590
|
# We get only the instrument price (and don't create it) because we don't want to create product instrument price on asset position propagation
|
|
591
591
|
# Instead, we decided to opt for a post_save based system that will assign the missing position price when a price is created
|
wbportfolio/models/portfolio.py
CHANGED
|
@@ -88,22 +88,28 @@ def get_returns(
|
|
|
88
88
|
# .astype(float)
|
|
89
89
|
# .sort_index()
|
|
90
90
|
# )
|
|
91
|
-
kwargs = dict(from_date=from_date, to_date=to_date, values=[MarketData.CLOSE])
|
|
91
|
+
kwargs = dict(from_date=from_date, to_date=to_date, values=[MarketData.CLOSE], apply_fx_rate=False)
|
|
92
92
|
if to_currency:
|
|
93
93
|
kwargs["target_currency"] = to_currency.key
|
|
94
|
-
|
|
95
|
-
if
|
|
94
|
+
df = pd.DataFrame(Instrument.objects.filter(id__in=instrument_ids).dl.market_data(**kwargs))
|
|
95
|
+
if df.empty:
|
|
96
96
|
raise InvalidAnalyticPortfolio()
|
|
97
|
-
|
|
97
|
+
fx_rate_df = df[["instrument_id", "fx_rate", "valuation_date"]].pivot(
|
|
98
|
+
index="valuation_date", columns="instrument_id", values="fx_rate"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
prices_df = df[["instrument_id", "close", "valuation_date"]].pivot(
|
|
98
102
|
index="valuation_date", columns="instrument_id", values="close"
|
|
99
103
|
)
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
price_fx_portfolio_df = fx_rate_df * prices_df
|
|
105
|
+
|
|
106
|
+
ts = pd.bdate_range(price_fx_portfolio_df.index.min(), price_fx_portfolio_df.index.max(), freq="B")
|
|
107
|
+
price_fx_portfolio_df = price_fx_portfolio_df.reindex(ts)
|
|
102
108
|
if ffill_returns:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
returns = prices_to_returns(
|
|
106
|
-
return {
|
|
109
|
+
price_fx_portfolio_df = price_fx_portfolio_df.ffill()
|
|
110
|
+
price_fx_portfolio_df.index = pd.to_datetime(price_fx_portfolio_df.index)
|
|
111
|
+
returns = prices_to_returns(price_fx_portfolio_df, drop_inceptions_nan=False, fill_nan=ffill_returns)
|
|
112
|
+
return {dt: row for dt, row in prices_df.replace(np.nan, None).to_dict("index").items()}, returns.replace(
|
|
107
113
|
[np.inf, -np.inf, np.nan], 0
|
|
108
114
|
)
|
|
109
115
|
|
|
@@ -702,6 +708,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
702
708
|
force_recompute_weighting: bool = False,
|
|
703
709
|
evaluate_rebalancer: bool = True,
|
|
704
710
|
changed_weights: dict[int, float] | None = None,
|
|
711
|
+
**kwargs,
|
|
705
712
|
):
|
|
706
713
|
logger.info(f"change at date for {self} at {val_date}")
|
|
707
714
|
|
|
@@ -743,23 +750,29 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
743
750
|
self.initial_position_date = val_date
|
|
744
751
|
self.save()
|
|
745
752
|
|
|
746
|
-
self.handle_controlling_portfolio_change_at_date(
|
|
753
|
+
self.handle_controlling_portfolio_change_at_date(
|
|
754
|
+
val_date,
|
|
755
|
+
recompute_weighting=recompute_weighting,
|
|
756
|
+
force_recompute_weighting=force_recompute_weighting,
|
|
757
|
+
changed_weights=changed_weights,
|
|
758
|
+
**kwargs,
|
|
759
|
+
)
|
|
747
760
|
|
|
748
|
-
def handle_controlling_portfolio_change_at_date(self, val_date: date):
|
|
761
|
+
def handle_controlling_portfolio_change_at_date(self, val_date: date, **kwargs):
|
|
749
762
|
if self.is_tracked:
|
|
750
763
|
for rel in PortfolioPortfolioThroughModel.objects.filter(
|
|
751
764
|
dependency_portfolio=self,
|
|
752
765
|
type=PortfolioPortfolioThroughModel.Type.PRIMARY,
|
|
753
766
|
portfolio__is_lookthrough=True,
|
|
754
767
|
):
|
|
755
|
-
rel.portfolio.compute_lookthrough(val_date)
|
|
768
|
+
rel.portfolio.compute_lookthrough(val_date, **kwargs)
|
|
756
769
|
for rel in PortfolioPortfolioThroughModel.objects.filter(
|
|
757
770
|
dependency_portfolio=self, type=PortfolioPortfolioThroughModel.Type.MODEL
|
|
758
771
|
):
|
|
759
772
|
rel.portfolio.evaluate_rebalancing(val_date)
|
|
760
773
|
for dependent_portfolio in self.get_child_portfolios(val_date):
|
|
761
|
-
dependent_portfolio.change_at_date(val_date)
|
|
762
|
-
dependent_portfolio.handle_controlling_portfolio_change_at_date(val_date)
|
|
774
|
+
dependent_portfolio.change_at_date(val_date, **kwargs)
|
|
775
|
+
dependent_portfolio.handle_controlling_portfolio_change_at_date(val_date, **kwargs)
|
|
763
776
|
|
|
764
777
|
def evaluate_rebalancing(self, val_date: date):
|
|
765
778
|
if hasattr(self, "automatic_rebalancer"):
|
|
@@ -952,7 +965,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
952
965
|
assets = list(self.assets.filter(date=val_date))
|
|
953
966
|
return assets
|
|
954
967
|
|
|
955
|
-
def compute_lookthrough(self, from_date: date, to_date: date | None = None):
|
|
968
|
+
def compute_lookthrough(self, from_date: date, to_date: date | None = None, **kwargs):
|
|
956
969
|
if not self.primary_portfolio or not self.is_lookthrough:
|
|
957
970
|
raise ValueError(
|
|
958
971
|
"Lookthrough position can only be computed on lookthrough portfolio with a primary portfolio"
|
|
@@ -968,7 +981,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
968
981
|
positions.add(
|
|
969
982
|
list(self.primary_portfolio.get_lookthrough_positions(from_date, portfolio_total_asset_value)),
|
|
970
983
|
)
|
|
971
|
-
self.bulk_create_positions(positions, delete_leftovers=True,
|
|
984
|
+
self.bulk_create_positions(positions, delete_leftovers=True, **kwargs)
|
|
972
985
|
|
|
973
986
|
def update_preferred_classification_per_instrument(self):
|
|
974
987
|
# Function to automatically assign Preferred instrument based on the assets' underlying instruments of the
|
|
@@ -1030,7 +1043,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
1030
1043
|
positions: AssetPositionIterator,
|
|
1031
1044
|
delete_leftovers: bool = False,
|
|
1032
1045
|
force_save: bool = False,
|
|
1033
|
-
compute_metrics: bool =
|
|
1046
|
+
compute_metrics: bool = False,
|
|
1034
1047
|
broadcast_changes_at_date: bool = True,
|
|
1035
1048
|
**kwargs,
|
|
1036
1049
|
):
|
|
@@ -361,7 +361,7 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
361
361
|
)
|
|
362
362
|
trade.price = trade.get_price()
|
|
363
363
|
# if we cannot automatically find a price, we consider the stock is invalid and we sell it
|
|
364
|
-
if trade.price
|
|
364
|
+
if not trade.price:
|
|
365
365
|
trade.price = Decimal("0.0")
|
|
366
366
|
trade.weighting = -trade_dto.effective_weight
|
|
367
367
|
|
|
@@ -439,6 +439,13 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
439
439
|
broadcast_changes_at_date=broadcast_changes_at_date,
|
|
440
440
|
evaluate_rebalancer=False,
|
|
441
441
|
)
|
|
442
|
+
for draft_tp in TradeProposal.objects.filter(
|
|
443
|
+
portfolio=self.portfolio,
|
|
444
|
+
trade_date__gt=last_trade_proposal.trade_date,
|
|
445
|
+
trade_date__lte=next_trade_date,
|
|
446
|
+
status=TradeProposal.Status.DRAFT,
|
|
447
|
+
):
|
|
448
|
+
draft_tp.reset_trades()
|
|
442
449
|
if overriding_trade_proposal:
|
|
443
450
|
last_trade_proposal_created = True
|
|
444
451
|
last_trade_proposal = overriding_trade_proposal
|
|
@@ -574,7 +574,7 @@ class Trade(TransactionMixin, ImportMixin, OrderedModel, models.Model):
|
|
|
574
574
|
self.portfolio = self.trade_proposal.portfolio
|
|
575
575
|
self.transaction_date = self.trade_proposal.trade_date
|
|
576
576
|
self.value_date = self.trade_proposal.last_effective_date
|
|
577
|
-
if self.price
|
|
577
|
+
if not self.price:
|
|
578
578
|
# we try to get the price if not provided directly from the underlying instrument
|
|
579
579
|
self.price = self.get_price()
|
|
580
580
|
if self.trade_proposal and not self.portfolio.only_weighting:
|
|
@@ -647,9 +647,11 @@ class Trade(TransactionMixin, ImportMixin, OrderedModel, models.Model):
|
|
|
647
647
|
asset.pre_save()
|
|
648
648
|
return asset
|
|
649
649
|
|
|
650
|
-
def get_price(self) -> Decimal
|
|
651
|
-
|
|
652
|
-
return
|
|
650
|
+
def get_price(self) -> Decimal:
|
|
651
|
+
try:
|
|
652
|
+
return self.underlying_instrument.get_price(self.transaction_date)
|
|
653
|
+
except ValueError:
|
|
654
|
+
return Decimal("0")
|
|
653
655
|
|
|
654
656
|
def delete(self, **kwargs):
|
|
655
657
|
pre_collection.send(sender=self.__class__, instance=self)
|
|
@@ -315,9 +315,20 @@ class TradeTradeProposalModelSerializer(TradeModelSerializer):
|
|
|
315
315
|
)
|
|
316
316
|
|
|
317
317
|
status = wb_serializers.ChoiceField(default=Trade.Status.DRAFT, choices=Trade.Status.choices)
|
|
318
|
-
|
|
319
|
-
target_weight = wb_serializers.DecimalField(
|
|
320
|
-
|
|
318
|
+
|
|
319
|
+
target_weight = wb_serializers.DecimalField(
|
|
320
|
+
max_digits=Trade.TRADE_WEIGHTING_PRECISION + 1,
|
|
321
|
+
decimal_places=Trade.TRADE_WEIGHTING_PRECISION,
|
|
322
|
+
required=False,
|
|
323
|
+
default=0,
|
|
324
|
+
)
|
|
325
|
+
effective_weight = wb_serializers.DecimalField(
|
|
326
|
+
read_only=True,
|
|
327
|
+
max_digits=Trade.TRADE_WEIGHTING_PRECISION + 1,
|
|
328
|
+
decimal_places=Trade.TRADE_WEIGHTING_PRECISION,
|
|
329
|
+
default=0,
|
|
330
|
+
)
|
|
331
|
+
|
|
321
332
|
effective_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
|
|
322
333
|
target_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
|
|
323
334
|
|
|
@@ -282,7 +282,13 @@ class TestPortfolioModel(PortfolioTestMixin):
|
|
|
282
282
|
dependent_portfolio.depends_on.add(base_portfolio)
|
|
283
283
|
base_portfolio.change_at_date(weekday)
|
|
284
284
|
|
|
285
|
-
mock_compute_lookthrough.assert_called_once_with(
|
|
285
|
+
mock_compute_lookthrough.assert_called_once_with(
|
|
286
|
+
dependent_portfolio,
|
|
287
|
+
weekday,
|
|
288
|
+
recompute_weighting=False,
|
|
289
|
+
force_recompute_weighting=False,
|
|
290
|
+
changed_weights=None,
|
|
291
|
+
)
|
|
286
292
|
|
|
287
293
|
def test_is_active_at_date(
|
|
288
294
|
self,
|
|
@@ -1083,7 +1089,7 @@ class TestPortfolioModel(PortfolioTestMixin):
|
|
|
1083
1089
|
a1 = asset_position_factory.build(date=weekday, portfolio=portfolio, underlying_instrument=i1)
|
|
1084
1090
|
|
|
1085
1091
|
# check initial creation
|
|
1086
|
-
portfolio.bulk_create_positions(AssetPositionIterator(portfolio).add([a1]))
|
|
1092
|
+
portfolio.bulk_create_positions(AssetPositionIterator(portfolio).add([a1]), compute_metrics=True)
|
|
1087
1093
|
assert AssetPosition.objects.get(portfolio=portfolio, date=weekday).weighting == a1.weighting
|
|
1088
1094
|
assert AssetPosition.objects.get(portfolio=portfolio, date=weekday).underlying_instrument == i1
|
|
1089
1095
|
|
|
@@ -7,7 +7,7 @@ import pytest
|
|
|
7
7
|
from faker import Faker
|
|
8
8
|
from pandas._libs.tslibs.offsets import BDay, BusinessMonthEnd
|
|
9
9
|
|
|
10
|
-
from wbportfolio.models import Portfolio, RebalancingModel, TradeProposal
|
|
10
|
+
from wbportfolio.models import Portfolio, RebalancingModel, Trade, TradeProposal
|
|
11
11
|
from wbportfolio.pms.typing import Portfolio as PortfolioDTO
|
|
12
12
|
from wbportfolio.pms.typing import Position
|
|
13
13
|
|
|
@@ -273,7 +273,7 @@ class TestTradeProposal:
|
|
|
273
273
|
|
|
274
274
|
# build the target portfolio
|
|
275
275
|
target_portfolio = PortfolioDTO(
|
|
276
|
-
|
|
276
|
+
[
|
|
277
277
|
Position(
|
|
278
278
|
underlying_instrument=i2.id,
|
|
279
279
|
date=trade_proposal.trade_date,
|
|
@@ -286,7 +286,7 @@ class TestTradeProposal:
|
|
|
286
286
|
weighting=Decimal("0.6"),
|
|
287
287
|
price=float(p3.net_value),
|
|
288
288
|
),
|
|
289
|
-
|
|
289
|
+
]
|
|
290
290
|
)
|
|
291
291
|
|
|
292
292
|
# Reset trades
|
|
@@ -304,7 +304,7 @@ class TestTradeProposal:
|
|
|
304
304
|
|
|
305
305
|
# build the target portfolio
|
|
306
306
|
new_target_portfolio = PortfolioDTO(
|
|
307
|
-
|
|
307
|
+
[
|
|
308
308
|
Position(
|
|
309
309
|
underlying_instrument=i1.id,
|
|
310
310
|
date=trade_proposal.trade_date,
|
|
@@ -323,7 +323,7 @@ class TestTradeProposal:
|
|
|
323
323
|
weighting=Decimal("0.5"),
|
|
324
324
|
price=float(p3.net_value),
|
|
325
325
|
),
|
|
326
|
-
|
|
326
|
+
]
|
|
327
327
|
)
|
|
328
328
|
|
|
329
329
|
trade_proposal.reset_trades(target_portfolio=new_target_portfolio)
|
|
@@ -626,5 +626,31 @@ class TestTradeProposal:
|
|
|
626
626
|
assert msft_a3.weighting == pytest.approx(target_weight, abs=Decimal("1e-6"))
|
|
627
627
|
assert apple_a3.weighting == pytest.approx(target_weight, abs=Decimal("1e-6"))
|
|
628
628
|
|
|
629
|
-
def
|
|
630
|
-
|
|
629
|
+
def test_replay_reset_draft_trade_proposal(
|
|
630
|
+
self, instrument, instrument_price_factory, trade_factory, trade_proposal_factory
|
|
631
|
+
):
|
|
632
|
+
trade_proposal = trade_proposal_factory.create(
|
|
633
|
+
status=TradeProposal.Status.DRAFT, trade_date=date.today() - BDay(2)
|
|
634
|
+
)
|
|
635
|
+
instrument_price_factory.create(instrument=instrument, date=date.today() - BDay(2))
|
|
636
|
+
instrument_price_factory.create(instrument=instrument, date=date.today() - BDay(1))
|
|
637
|
+
instrument_price_factory.create(instrument=instrument, date=date.today())
|
|
638
|
+
trade = trade_factory.create(
|
|
639
|
+
underlying_instrument=instrument,
|
|
640
|
+
trade_proposal=trade_proposal,
|
|
641
|
+
weighting=1,
|
|
642
|
+
status=TradeProposal.Status.DRAFT,
|
|
643
|
+
)
|
|
644
|
+
trade_proposal.submit()
|
|
645
|
+
trade_proposal.approve(replay=False)
|
|
646
|
+
trade_proposal.save()
|
|
647
|
+
|
|
648
|
+
draft_tp = trade_proposal_factory.create(portfolio=trade_proposal.portfolio, trade_date=date.today() - BDay(1))
|
|
649
|
+
assert not Trade.objects.filter(trade_proposal=draft_tp).exists()
|
|
650
|
+
|
|
651
|
+
trade_proposal.replay()
|
|
652
|
+
|
|
653
|
+
assert Trade.objects.filter(trade_proposal=draft_tp).count() == 1
|
|
654
|
+
assert Trade.objects.get(
|
|
655
|
+
trade_proposal=draft_tp, underlying_instrument=trade.underlying_instrument
|
|
656
|
+
).weighting == Decimal("0")
|
|
@@ -99,8 +99,7 @@ class TradeProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserP
|
|
|
99
99
|
def reset(self, request, pk=None):
|
|
100
100
|
trade_proposal = get_object_or_404(TradeProposal, pk=pk)
|
|
101
101
|
if trade_proposal.status == TradeProposal.Status.DRAFT:
|
|
102
|
-
trade_proposal.
|
|
103
|
-
trade_proposal.reset_trades(force_reset_trade=True)
|
|
102
|
+
trade_proposal.reset_trades()
|
|
104
103
|
return Response({"send": True})
|
|
105
104
|
return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
|
|
106
105
|
|
|
@@ -253,11 +253,11 @@ wbportfolio/migrations/0080_alter_trade_drift_factor_alter_trade_weighting.py,sh
|
|
|
253
253
|
wbportfolio/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
254
254
|
wbportfolio/models/__init__.py,sha256=HSpa5xwh_MHQaBpNrq9E0CbdEE5Iq-pDLIsPzZ-TRTg,904
|
|
255
255
|
wbportfolio/models/adjustments.py,sha256=osXWkJZOiansPWYPyHtl7Z121zDWi7u1YMtrBQtbHVo,10272
|
|
256
|
-
wbportfolio/models/asset.py,sha256=
|
|
256
|
+
wbportfolio/models/asset.py,sha256=b0vPt4LwNrxcMiK7UmBKViYnbNNlZzPTagvU5vFuyrc,45685
|
|
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=8cnnvIWKsBIp1WSfhFHaSB4OLCuFZ3lwJ9koZxIY40s,58080
|
|
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=2EGi03APBC9QWU99LVRA1e-_-o8AUtEW332Iuxvb-_E,38214
|
|
288
|
+
wbportfolio/models/transactions/trades.py,sha256=vTCoSBAmd6FPFyDRm5fqTqbxwzcH3ApWn0Dz2tkCCR8,34132
|
|
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
|
|
@@ -350,7 +350,7 @@ wbportfolio/serializers/transactions/claim.py,sha256=kC4E2RZRrpd9i8tGfoiV-gpWDk3
|
|
|
350
350
|
wbportfolio/serializers/transactions/dividends.py,sha256=ADXf9cXe8rq55lC_a8vIzViGLmQ-yDXkgR54k2m-N0w,1814
|
|
351
351
|
wbportfolio/serializers/transactions/fees.py,sha256=3mBzs6vdfST9roeQB-bmLJhipY7i5jBtAXjoTTE-GOg,2388
|
|
352
352
|
wbportfolio/serializers/transactions/trade_proposals.py,sha256=2oX04DSyiQ5C0-XkB7c0_wDJ-yC0niRgC72pXPPa_Xc,4014
|
|
353
|
-
wbportfolio/serializers/transactions/trades.py,sha256=
|
|
353
|
+
wbportfolio/serializers/transactions/trades.py,sha256=IpyvsMo1laDJbYfYUVcO3t2yMGLQooUDJr_gGbgRLcw,17021
|
|
354
354
|
wbportfolio/static/wbportfolio/css/macro_review.css,sha256=FAVVO8nModxwPXcTKpcfzVxBGPZGJVK1Xn-0dkSfGyc,233
|
|
355
355
|
wbportfolio/static/wbportfolio/markdown/documentation/account_holding_reconciliation.md,sha256=MabxOvOne8s5gl6osDoow6-3ghaXLAYg9THWpvy6G5I,921
|
|
356
356
|
wbportfolio/static/wbportfolio/markdown/documentation/aggregate_asset_position_liquidity.md,sha256=HEgXB7uqmqfty-GBCCXYxrAN-teqmxWuqDLK_liKWVc,1090
|
|
@@ -380,7 +380,7 @@ wbportfolio/tests/models/test_merge.py,sha256=sdsjiZsmR6vsUKwTa5kkvL6QTeAZqtd_EP
|
|
|
380
380
|
wbportfolio/tests/models/test_portfolio_cash_flow.py,sha256=X8dsXexsb1b0lBiuGzu40ps_Az_1UmmKT0eo1vbXH94,5792
|
|
381
381
|
wbportfolio/tests/models/test_portfolio_cash_targets.py,sha256=q8QWAwt-kKRkLC0E05GyRhF_TTQXIi8bdHjXVU0fCV0,965
|
|
382
382
|
wbportfolio/tests/models/test_portfolio_swing_pricings.py,sha256=kr2AOcQkyg2pX3ULjU-o9ye-NVpjMrrfoe-DVbYCbjs,1656
|
|
383
|
-
wbportfolio/tests/models/test_portfolios.py,sha256=
|
|
383
|
+
wbportfolio/tests/models/test_portfolios.py,sha256=oSlB1KdSHdR5lXm6G7wpjMjRgM79OrHM2FY_PHhna58,53570
|
|
384
384
|
wbportfolio/tests/models/test_product_groups.py,sha256=AcdxhurV-n_bBuUsfD1GqVtwLFcs7VI2CRrwzsIUWbU,3337
|
|
385
385
|
wbportfolio/tests/models/test_products.py,sha256=IcBzw9hrGiWFMRwPBTMukCMWrhqnjOVA2hhb90xYOW8,9580
|
|
386
386
|
wbportfolio/tests/models/test_roles.py,sha256=4Cn7WyrA2ztJNeWLk5cy9kYo5XLWMbFSvo1O-9JYxeA,3323
|
|
@@ -390,7 +390,7 @@ wbportfolio/tests/models/transactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
390
390
|
wbportfolio/tests/models/transactions/test_claim.py,sha256=NG3BKB-FVcIDgHSJHCjImxgMM3ISVUMl24xUPmEcPec,5570
|
|
391
391
|
wbportfolio/tests/models/transactions/test_fees.py,sha256=tAp18x2wCNQr11LUnLtHNbBDbbX0v1DZnmW7i-cEi5Q,2423
|
|
392
392
|
wbportfolio/tests/models/transactions/test_rebalancing.py,sha256=fZ5tx6kByEGXD6nhapYdvk9HOjYlmjhU2w6KlQJ6QE4,4061
|
|
393
|
-
wbportfolio/tests/models/transactions/test_trade_proposals.py,sha256=
|
|
393
|
+
wbportfolio/tests/models/transactions/test_trade_proposals.py,sha256=_EH7N2-oTqgz13NGSahD1d0rIiu93Ce2rcA9A8gUlSg,29800
|
|
394
394
|
wbportfolio/tests/models/transactions/test_trades.py,sha256=vqvOqUY_uXvBp8YOKR0Wq9ycA2oeeEBhO3dzV7sbXEU,9863
|
|
395
395
|
wbportfolio/tests/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
396
396
|
wbportfolio/tests/pms/test_analytics.py,sha256=WHicJBjAjpIRL1-AW2nZ4VD9oJRpMoeH6V1Qx2D95-w,1178
|
|
@@ -520,9 +520,9 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
|
|
|
520
520
|
wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
|
|
521
521
|
wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
|
|
522
522
|
wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQVTN2MH1kG_yCeVkmyK8k,1263
|
|
523
|
-
wbportfolio/viewsets/transactions/trade_proposals.py,sha256=
|
|
523
|
+
wbportfolio/viewsets/transactions/trade_proposals.py,sha256=kQCojTNKBEyn2NcenL3a9auzBH4sIgLEx8rLAYCGLGg,6161
|
|
524
524
|
wbportfolio/viewsets/transactions/trades.py,sha256=GHOw5jtcqoaHiRrxxxL29c9405QiPisEn4coGELKDrE,22146
|
|
525
|
-
wbportfolio-1.54.
|
|
526
|
-
wbportfolio-1.54.
|
|
527
|
-
wbportfolio-1.54.
|
|
528
|
-
wbportfolio-1.54.
|
|
525
|
+
wbportfolio-1.54.7.dist-info/METADATA,sha256=3cBCW9p4yeH2L3_e9ruCBUDkvWkzcOnj4-SQeK6fUi0,702
|
|
526
|
+
wbportfolio-1.54.7.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
527
|
+
wbportfolio-1.54.7.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
528
|
+
wbportfolio-1.54.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|