wbportfolio 1.54.15__py2.py3-none-any.whl → 1.54.16__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/orders/order_proposals.py +24 -28
- wbportfolio/pms/trading/handler.py +26 -27
- wbportfolio/serializers/orders/order_proposals.py +7 -2
- wbportfolio/tests/models/orders/test_order_proposals.py +2 -10
- {wbportfolio-1.54.15.dist-info → wbportfolio-1.54.16.dist-info}/METADATA +1 -1
- {wbportfolio-1.54.15.dist-info → wbportfolio-1.54.16.dist-info}/RECORD +8 -8
- {wbportfolio-1.54.15.dist-info → wbportfolio-1.54.16.dist-info}/WHEEL +0 -0
- {wbportfolio-1.54.15.dist-info → wbportfolio-1.54.16.dist-info}/licenses/LICENSE +0 -0
|
@@ -24,7 +24,6 @@ from django_fsm import FSMField, transition
|
|
|
24
24
|
from pandas._libs.tslibs.offsets import BDay
|
|
25
25
|
from wbcompliance.models.risk_management.mixins import RiskCheckMixin
|
|
26
26
|
from wbcore.contrib.authentication.models import User
|
|
27
|
-
from wbcore.contrib.currency.models import Currency
|
|
28
27
|
from wbcore.contrib.icons import WBIcon
|
|
29
28
|
from wbcore.contrib.notifications.dispatch import send_notification
|
|
30
29
|
from wbcore.enums import RequestType
|
|
@@ -188,6 +187,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
188
187
|
effective_weight=Round(
|
|
189
188
|
F("contribution") / Value(portfolio_contribution), precision=Order.ORDER_WEIGHTING_PRECISION
|
|
190
189
|
),
|
|
190
|
+
tmp_effective_weight=F("contribution") / Value(portfolio_contribution),
|
|
191
191
|
target_weight=Round(F("effective_weight") + F("weighting"), precision=Order.ORDER_WEIGHTING_PRECISION),
|
|
192
192
|
effective_shares=Coalesce(
|
|
193
193
|
Subquery(
|
|
@@ -206,7 +206,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
206
206
|
)
|
|
207
207
|
total_effective_weight = orders.aggregate(s=models.Sum("effective_weight"))["s"] or Decimal("1")
|
|
208
208
|
with suppress(Order.DoesNotExist):
|
|
209
|
-
largest_order = orders.latest("
|
|
209
|
+
largest_order = orders.latest("effective_weight")
|
|
210
210
|
if quant_error := Decimal("1") - total_effective_weight:
|
|
211
211
|
orders = orders.annotate(
|
|
212
212
|
effective_weight=models.Case(
|
|
@@ -226,7 +226,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
226
226
|
def __str__(self) -> str:
|
|
227
227
|
return f"{self.portfolio.name}: {self.trade_date} ({self.status})"
|
|
228
228
|
|
|
229
|
-
def convert_to_portfolio(self, use_effective: bool = False) -> PortfolioDTO:
|
|
229
|
+
def convert_to_portfolio(self, use_effective: bool = False, with_cash: bool = True) -> PortfolioDTO:
|
|
230
230
|
"""
|
|
231
231
|
Data Transfer Object
|
|
232
232
|
Returns:
|
|
@@ -258,6 +258,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
258
258
|
except InvalidAnalyticPortfolio:
|
|
259
259
|
last_returns, portfolio_contribution = {}, 1
|
|
260
260
|
positions = []
|
|
261
|
+
total_weighting = Decimal("0")
|
|
261
262
|
for instrument, row in portfolio.items():
|
|
262
263
|
weighting = row["weighting"]
|
|
263
264
|
daily_return = Decimal(last_returns.get(instrument.id, 0))
|
|
@@ -285,6 +286,10 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
285
286
|
currency_fx_rate=row["currency_fx_rate"],
|
|
286
287
|
)
|
|
287
288
|
)
|
|
289
|
+
total_weighting += weighting
|
|
290
|
+
if with_cash and (cash_weight := Decimal("1") - total_weighting):
|
|
291
|
+
cash_position = self.get_estimated_target_cash(target_cash_weight=cash_weight)
|
|
292
|
+
positions.append(cash_position._build_dto())
|
|
288
293
|
return PortfolioDTO(positions)
|
|
289
294
|
|
|
290
295
|
# Start tools methods
|
|
@@ -325,7 +330,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
325
330
|
service = TradingService(
|
|
326
331
|
self.trade_date,
|
|
327
332
|
effective_portfolio=self._get_default_effective_portfolio(),
|
|
328
|
-
target_portfolio=self.convert_to_portfolio(),
|
|
333
|
+
target_portfolio=self.convert_to_portfolio(use_effective=False, with_cash=False),
|
|
329
334
|
total_target_weight=total_target_weight,
|
|
330
335
|
)
|
|
331
336
|
leftovers_orders = self.orders.all()
|
|
@@ -352,7 +357,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
352
357
|
return self.rebalancing_model.get_target_portfolio(
|
|
353
358
|
self.portfolio, self.trade_date, self.value_date, **params
|
|
354
359
|
)
|
|
355
|
-
return self.convert_to_portfolio()
|
|
360
|
+
return self.convert_to_portfolio(use_effective=False)
|
|
356
361
|
|
|
357
362
|
def _get_default_effective_portfolio(self):
|
|
358
363
|
return self.convert_to_portfolio(use_effective=True)
|
|
@@ -455,8 +460,8 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
455
460
|
last_order_proposal = self
|
|
456
461
|
last_order_proposal_created = False
|
|
457
462
|
while last_order_proposal and last_order_proposal.status == OrderProposal.Status.APPROVED:
|
|
463
|
+
logger.info(f"Replaying order proposal {last_order_proposal}")
|
|
458
464
|
if not last_order_proposal_created:
|
|
459
|
-
logger.info(f"Replaying order proposal {last_order_proposal}")
|
|
460
465
|
last_order_proposal.approve_workflow(
|
|
461
466
|
silent_exception=True,
|
|
462
467
|
force_reset_order=True,
|
|
@@ -557,50 +562,41 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
557
562
|
shares = math.floor(shares / round_lot_size) * round_lot_size
|
|
558
563
|
return shares
|
|
559
564
|
|
|
560
|
-
def get_estimated_target_cash(self,
|
|
565
|
+
def get_estimated_target_cash(self, target_cash_weight: Decimal | None = None) -> AssetPosition:
|
|
561
566
|
"""
|
|
562
567
|
Estimates the target cash weight and shares for a order proposal.
|
|
563
568
|
|
|
564
569
|
This method calculates the target cash weight by summing the weights of cash orders and adding any leftover weight from non-cash orders. It then estimates the target shares for this cash component if the portfolio is not only weighting-based.
|
|
565
570
|
|
|
566
571
|
Args:
|
|
567
|
-
|
|
572
|
+
target_cash_weight (Decimal): the expected target cash weight (Optional). If not provided, we estimate from the existing orders
|
|
568
573
|
|
|
569
574
|
Returns:
|
|
570
575
|
tuple[Decimal, Decimal]: A tuple containing the target cash weight and the estimated target shares.
|
|
571
576
|
"""
|
|
572
577
|
# Retrieve orders with base information
|
|
573
578
|
orders = self.get_orders()
|
|
579
|
+
currency = self.portfolio.currency
|
|
574
580
|
|
|
575
|
-
# Calculate the target
|
|
576
|
-
|
|
577
|
-
underlying_instrument__is_cash=True, underlying_instrument__currency=currency
|
|
578
|
-
).aggregate(s=models.Sum("target_weight"))["s"] or Decimal(0)
|
|
579
|
-
# if the specified currency match the portfolio's currency, we include the weight leftover to this cash compoenent
|
|
580
|
-
if currency == self.portfolio.currency:
|
|
581
|
-
# Calculate the total target weight of all orders
|
|
582
|
-
total_target_weight = orders.aggregate(s=models.Sum("target_weight"))["s"] or Decimal(0)
|
|
581
|
+
# Calculate the total target weight of all orders
|
|
582
|
+
total_target_weight = orders.aggregate(s=models.Sum("target_weight"))["s"] or Decimal(0)
|
|
583
583
|
|
|
584
|
-
|
|
585
|
-
target_cash_weight
|
|
584
|
+
if target_cash_weight is None:
|
|
585
|
+
target_cash_weight = Decimal("1") - total_target_weight
|
|
586
586
|
|
|
587
587
|
# Initialize target shares to zero
|
|
588
588
|
total_target_shares = Decimal(0)
|
|
589
589
|
|
|
590
|
+
# Get or create a cash component for the portfolio's currency
|
|
591
|
+
cash_component = Cash.objects.get_or_create(
|
|
592
|
+
currency=currency, defaults={"is_cash": True, "name": currency.title}
|
|
593
|
+
)[0]
|
|
590
594
|
# If the portfolio is not only weighting-based, estimate the target shares for the cash component
|
|
591
595
|
if not self.portfolio.only_weighting:
|
|
592
|
-
# Get or create a cash component for the portfolio's currency
|
|
593
|
-
cash_component = Cash.objects.get_or_create(
|
|
594
|
-
currency=currency, defaults={"is_cash": True, "name": currency.title}
|
|
595
|
-
)[0]
|
|
596
|
-
|
|
597
596
|
# Estimate the target shares for the cash component
|
|
598
597
|
with suppress(ValueError):
|
|
599
598
|
total_target_shares = self.get_estimated_shares(target_cash_weight, cash_component, Decimal("1.0"))
|
|
600
599
|
|
|
601
|
-
cash_component = Cash.objects.get_or_create(
|
|
602
|
-
currency=self.portfolio.currency, defaults={"name": self.portfolio.currency.title}
|
|
603
|
-
)[0]
|
|
604
600
|
# otherwise, we create a new position
|
|
605
601
|
underlying_quote_price = InstrumentPrice.objects.get_or_create(
|
|
606
602
|
instrument=cash_component,
|
|
@@ -657,7 +653,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
657
653
|
Order.objects.bulk_update(orders, ["shares", "weighting"])
|
|
658
654
|
|
|
659
655
|
# If we estimate cash on this order proposal, we make sure to create the corresponding cash component
|
|
660
|
-
estimated_cash_position = self.get_estimated_target_cash(
|
|
656
|
+
estimated_cash_position = self.get_estimated_target_cash()
|
|
661
657
|
target_portfolio = self.validated_trading_service.trades_batch.convert_to_portfolio(
|
|
662
658
|
estimated_cash_position._build_dto()
|
|
663
659
|
)
|
|
@@ -717,7 +713,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
717
713
|
assets = []
|
|
718
714
|
warnings = []
|
|
719
715
|
# We do not want to create the estimated cash position if there is not orders in the order proposal (shouldn't be possible anyway)
|
|
720
|
-
estimated_cash_position = self.get_estimated_target_cash(
|
|
716
|
+
estimated_cash_position = self.get_estimated_target_cash()
|
|
721
717
|
for order in self.get_orders():
|
|
722
718
|
with suppress(ValueError):
|
|
723
719
|
asset = order.get_asset()
|
|
@@ -143,33 +143,32 @@ class TradingService:
|
|
|
143
143
|
|
|
144
144
|
trades: list[Trade] = []
|
|
145
145
|
for instrument_id, pos in instruments.items():
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
trades.append(trade)
|
|
146
|
+
previous_weight = target_weight = 0
|
|
147
|
+
effective_shares = target_shares = 0
|
|
148
|
+
daily_return = 0
|
|
149
|
+
if effective_pos := effective_portfolio.positions_map.get(instrument_id, None):
|
|
150
|
+
previous_weight = effective_pos.weighting
|
|
151
|
+
effective_shares = effective_pos.shares
|
|
152
|
+
daily_return = effective_pos.daily_return
|
|
153
|
+
if target_pos := target_portfolio.positions_map.get(instrument_id, None):
|
|
154
|
+
target_weight = target_pos.weighting
|
|
155
|
+
if target_pos.shares is not None:
|
|
156
|
+
target_shares = target_pos.shares
|
|
157
|
+
trade = Trade(
|
|
158
|
+
underlying_instrument=instrument_id,
|
|
159
|
+
previous_weight=previous_weight,
|
|
160
|
+
target_weight=target_weight,
|
|
161
|
+
effective_shares=effective_shares,
|
|
162
|
+
target_shares=target_shares,
|
|
163
|
+
date=self.trade_date,
|
|
164
|
+
instrument_type=pos.instrument_type,
|
|
165
|
+
currency=pos.currency,
|
|
166
|
+
price=Decimal(pos.price) if pos.price is not None else Decimal("0"),
|
|
167
|
+
currency_fx_rate=Decimal(pos.currency_fx_rate),
|
|
168
|
+
daily_return=Decimal(daily_return),
|
|
169
|
+
portfolio_contribution=effective_portfolio.portfolio_contribution,
|
|
170
|
+
)
|
|
171
|
+
trades.append(trade)
|
|
173
172
|
return TradeBatch(trades)
|
|
174
173
|
|
|
175
174
|
def is_valid(self, ignore_error: bool = False) -> bool:
|
|
@@ -23,7 +23,7 @@ class OrderProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
23
23
|
rebalancing_model = wb_serializers.PrimaryKeyRelatedField(queryset=RebalancingModel.objects.all(), required=False)
|
|
24
24
|
_rebalancing_model = RebalancingModelRepresentationSerializer(source="rebalancing_model")
|
|
25
25
|
target_portfolio = wb_serializers.PrimaryKeyRelatedField(
|
|
26
|
-
queryset=Portfolio.objects.all(), write_only=True, required=False
|
|
26
|
+
queryset=Portfolio.objects.all(), write_only=True, required=False
|
|
27
27
|
)
|
|
28
28
|
_target_portfolio = PortfolioRepresentationSerializer(source="target_portfolio")
|
|
29
29
|
total_cash_weight = wb_serializers.DecimalField(
|
|
@@ -51,8 +51,13 @@ class OrderProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
51
51
|
obj = super().create(validated_data)
|
|
52
52
|
|
|
53
53
|
target_portfolio_dto = None
|
|
54
|
-
if target_portfolio
|
|
54
|
+
if target_portfolio:
|
|
55
55
|
target_portfolio_dto = target_portfolio._build_dto(obj.trade_date)
|
|
56
|
+
elif rebalancing_model:
|
|
57
|
+
target_portfolio_dto = rebalancing_model.get_target_portfolio(
|
|
58
|
+
obj.portfolio, obj.trade_date, obj.last_effective_date
|
|
59
|
+
)
|
|
60
|
+
|
|
56
61
|
try:
|
|
57
62
|
obj.reset_orders(
|
|
58
63
|
target_portfolio=target_portfolio_dto, total_target_weight=Decimal("1.0") - total_cash_weight
|
|
@@ -423,23 +423,15 @@ class TestOrderProposal:
|
|
|
423
423
|
order_proposal.portfolio.only_weighting = False
|
|
424
424
|
order_proposal.portfolio.save()
|
|
425
425
|
mock_fct.return_value = Decimal(1_000_000) # 1 million cash
|
|
426
|
-
cash = cash_factory.create(currency=order_proposal.portfolio.currency)
|
|
427
426
|
order_factory.create( # equity trade
|
|
428
427
|
order_proposal=order_proposal,
|
|
429
428
|
value_date=order_proposal.trade_date,
|
|
430
429
|
portfolio=order_proposal.portfolio,
|
|
431
430
|
weighting=Decimal("0.7"),
|
|
432
431
|
)
|
|
433
|
-
order_factory.create( # cash trade
|
|
434
|
-
order_proposal=order_proposal,
|
|
435
|
-
value_date=order_proposal.trade_date,
|
|
436
|
-
portfolio=order_proposal.portfolio,
|
|
437
|
-
underlying_instrument=cash,
|
|
438
|
-
weighting=Decimal("0.2"),
|
|
439
|
-
)
|
|
440
432
|
|
|
441
|
-
target_cash_position = order_proposal.get_estimated_target_cash(
|
|
442
|
-
assert target_cash_position.weighting == Decimal("0.
|
|
433
|
+
target_cash_position = order_proposal.get_estimated_target_cash()
|
|
434
|
+
assert target_cash_position.weighting == Decimal("0.3")
|
|
443
435
|
assert target_cash_position.initial_shares == Decimal(1_000_000) * Decimal("0.3")
|
|
444
436
|
|
|
445
437
|
def test_order_proposal_update_inception_date(self, order_proposal_factory, portfolio, instrument_factory):
|
|
@@ -288,7 +288,7 @@ wbportfolio/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
288
288
|
wbportfolio/models/mixins/instruments.py,sha256=SgBreTpa_X3uyCWo7t8B0VaTtl49IjmBMe4Pab6TjAM,6796
|
|
289
289
|
wbportfolio/models/mixins/liquidity_stress_test.py,sha256=iQVzT3QM7VtHnqfj9gT6KUIe4wC4MJXery-AXJHUYns,58820
|
|
290
290
|
wbportfolio/models/orders/__init__.py,sha256=EH9UacGR3npBMje5FGTeLOh1xqFBh9kc24WbGmBIA3g,69
|
|
291
|
-
wbportfolio/models/orders/order_proposals.py,sha256=
|
|
291
|
+
wbportfolio/models/orders/order_proposals.py,sha256=uo0hWOjY6lKpGwa9fnBvskQmI0S_Usg7LYbY9PCJ4xo,40333
|
|
292
292
|
wbportfolio/models/orders/orders.py,sha256=hVVw7NAFmAFHosMMs39V9DjGmWyFC_msSxF8rpDDG60,9683
|
|
293
293
|
wbportfolio/models/reconciliations/__init__.py,sha256=MXH5fZIPGDRBgJkO6wVu_NLRs8fkP1im7G6d-h36lQY,127
|
|
294
294
|
wbportfolio/models/reconciliations/account_reconciliation_lines.py,sha256=QP6M7hMcyFbuXBa55Y-azui6Dl_WgbzMntEqWzQkbfM,7394
|
|
@@ -307,7 +307,7 @@ wbportfolio/pms/analytics/portfolio.py,sha256=u_S-e6HUQwAyq90gweDmxyTHWrIc5nd84s
|
|
|
307
307
|
wbportfolio/pms/analytics/utils.py,sha256=EfhKdo9B2ABaUPppb8DgZSqpNkSze8Rjej1xDjv-XcQ,282
|
|
308
308
|
wbportfolio/pms/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
309
309
|
wbportfolio/pms/trading/__init__.py,sha256=R_yLKc54sCak8A1cW0O1Aszrcv5KV8mC_3h17Hr20e4,36
|
|
310
|
-
wbportfolio/pms/trading/handler.py,sha256=
|
|
310
|
+
wbportfolio/pms/trading/handler.py,sha256=o31jtevfnSSv0aSzAsnthz2luM7F-D5d061LaqfOHUw,8542
|
|
311
311
|
wbportfolio/rebalancing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
312
312
|
wbportfolio/rebalancing/base.py,sha256=wpeoxdkLz5osxm5mRjkOoML7YkYvwuAlqSLLtHBbWp8,984
|
|
313
313
|
wbportfolio/rebalancing/decorators.py,sha256=162ZmXV2YQGI830LWvEnJ95RexMzHvaCGfcVOnXTOXM,502
|
|
@@ -360,7 +360,7 @@ wbportfolio/serializers/registers.py,sha256=zhdKH_mHEBE0VOhm6xpY54bTMtcSaY5BskEa
|
|
|
360
360
|
wbportfolio/serializers/roles.py,sha256=T-9NqTldpvaEMFy-Bib5MB6MeboygEOqcMP61mzzD3Q,2146
|
|
361
361
|
wbportfolio/serializers/signals.py,sha256=hD6R4oFtwhvnsJPteytPKy2JwEelmxrapdfoLSnluaE,7053
|
|
362
362
|
wbportfolio/serializers/orders/__init__.py,sha256=PKJRksA1pWsh8nVfGASoB0m3LyUzVRnq1m9VPp90J7k,271
|
|
363
|
-
wbportfolio/serializers/orders/order_proposals.py,sha256=
|
|
363
|
+
wbportfolio/serializers/orders/order_proposals.py,sha256=FegiVe1d1AZoLtOuNOUfLN7HwGy4mKGrdYHKDwuUjPY,4430
|
|
364
364
|
wbportfolio/serializers/orders/orders.py,sha256=pAKjJLRANOo1iMlcv18twuQ0aAVDVKYt-pPx6avnWRQ,7464
|
|
365
365
|
wbportfolio/serializers/transactions/__init__.py,sha256=-7Pan4n7YI3iDvGXff6okzk4ycEURRxp5n_SHCY_g_I,493
|
|
366
366
|
wbportfolio/serializers/transactions/claim.py,sha256=kC4E2RZRrpd9i8tGfoiV-gpWDk3ikR5F1Wf0v_IGIvw,11599
|
|
@@ -403,7 +403,7 @@ wbportfolio/tests/models/test_roles.py,sha256=4Cn7WyrA2ztJNeWLk5cy9kYo5XLWMbFSvo
|
|
|
403
403
|
wbportfolio/tests/models/test_splits.py,sha256=ytKcHsI_90kj1L4s8It-KEcc24rkDcElxwQ8q0QxEvk,9689
|
|
404
404
|
wbportfolio/tests/models/utils.py,sha256=ORNJq6NMo1Za22jGZXfTfKeNEnTRlfEt_8SJ6xLaQWg,325
|
|
405
405
|
wbportfolio/tests/models/orders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
406
|
-
wbportfolio/tests/models/orders/test_order_proposals.py,sha256=
|
|
406
|
+
wbportfolio/tests/models/orders/test_order_proposals.py,sha256=xVoKYHhzm_UihJuEY5P8G1kUURCYYSnyFVAR4aPFmUA,29353
|
|
407
407
|
wbportfolio/tests/models/transactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
408
408
|
wbportfolio/tests/models/transactions/test_claim.py,sha256=NG3BKB-FVcIDgHSJHCjImxgMM3ISVUMl24xUPmEcPec,5570
|
|
409
409
|
wbportfolio/tests/models/transactions/test_fees.py,sha256=tAp18x2wCNQr11LUnLtHNbBDbbX0v1DZnmW7i-cEi5Q,2423
|
|
@@ -550,7 +550,7 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
|
|
|
550
550
|
wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
|
|
551
551
|
wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
|
|
552
552
|
wbportfolio/viewsets/transactions/trades.py,sha256=xBgOGaJ8aEg-2RxEJ4FDaBs4SGwuLasun3nhpis0WQY,12363
|
|
553
|
-
wbportfolio-1.54.
|
|
554
|
-
wbportfolio-1.54.
|
|
555
|
-
wbportfolio-1.54.
|
|
556
|
-
wbportfolio-1.54.
|
|
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,,
|
|
File without changes
|
|
File without changes
|