wbportfolio 1.49.11__py2.py3-none-any.whl → 1.50.1__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 +2 -2
- wbportfolio/models/transactions/trade_proposals.py +1 -1
- wbportfolio/models/transactions/trades.py +23 -23
- wbportfolio/risk_management/backends/liquidity_risk.py +1 -1
- wbportfolio/risk_management/backends/mixins.py +13 -5
- wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +36 -31
- {wbportfolio-1.49.11.dist-info → wbportfolio-1.50.1.dist-info}/METADATA +1 -1
- {wbportfolio-1.49.11.dist-info → wbportfolio-1.50.1.dist-info}/RECORD +10 -10
- {wbportfolio-1.49.11.dist-info → wbportfolio-1.50.1.dist-info}/WHEEL +0 -0
- {wbportfolio-1.49.11.dist-info → wbportfolio-1.50.1.dist-info}/licenses/LICENSE +0 -0
wbportfolio/models/asset.py
CHANGED
|
@@ -185,8 +185,8 @@ class AssetPositionIterator:
|
|
|
185
185
|
return dict(self._weights)
|
|
186
186
|
|
|
187
187
|
def __iter__(self):
|
|
188
|
-
# return an iterable excluding the position
|
|
189
|
-
yield from filter(lambda a: a.weighting, self.positions.values())
|
|
188
|
+
# return an iterable excluding the position with a null weight if the portfolio is manageable (otherwise, we assume the 0-weight position is valid)
|
|
189
|
+
yield from filter(lambda a: not a.portfolio.is_manageable or a.weighting, self.positions.values())
|
|
190
190
|
|
|
191
191
|
def __getitem__(self, item: tuple[date, Instrument]) -> float:
|
|
192
192
|
return self._weights[item[0]][item[1].id]
|
|
@@ -524,7 +524,7 @@ class TradeProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
524
524
|
|
|
525
525
|
for trade in self.trades.all():
|
|
526
526
|
with suppress(ValueError):
|
|
527
|
-
asset = trade.
|
|
527
|
+
asset = trade.get_asset()
|
|
528
528
|
# we add the corresponding asset only if it is not the cache position (already included in estimated_cash_position)
|
|
529
529
|
if asset.underlying_quote != estimated_cash_position.underlying_quote:
|
|
530
530
|
assets.append(asset)
|
|
@@ -299,31 +299,9 @@ class Trade(ShareMixin, Transaction, OrderedModel, WBModel):
|
|
|
299
299
|
)
|
|
300
300
|
},
|
|
301
301
|
)
|
|
302
|
-
def to_asset(self) -> AssetPosition:
|
|
303
|
-
last_underlying_quote_price = self.last_underlying_quote_price
|
|
304
|
-
if not last_underlying_quote_price:
|
|
305
|
-
raise ValueError("No price found")
|
|
306
|
-
asset = AssetPosition(
|
|
307
|
-
underlying_quote=self.underlying_instrument,
|
|
308
|
-
portfolio_created=None,
|
|
309
|
-
portfolio=self.portfolio,
|
|
310
|
-
date=self.transaction_date,
|
|
311
|
-
initial_currency_fx_rate=self.currency_fx_rate,
|
|
312
|
-
weighting=self._target_weight,
|
|
313
|
-
initial_price=self.last_underlying_quote_price.net_value,
|
|
314
|
-
initial_shares=None,
|
|
315
|
-
underlying_quote_price=self.last_underlying_quote_price,
|
|
316
|
-
asset_valuation_date=self.transaction_date,
|
|
317
|
-
currency=self.currency,
|
|
318
|
-
is_estimated=False,
|
|
319
|
-
)
|
|
320
|
-
asset.set_weighting(self._target_weight)
|
|
321
|
-
asset.pre_save()
|
|
322
|
-
return asset
|
|
323
|
-
|
|
324
302
|
def execute(self, **kwargs):
|
|
325
303
|
with suppress(ValueError):
|
|
326
|
-
asset = self.
|
|
304
|
+
asset = self.get_asset()
|
|
327
305
|
AssetPosition.unannotated_objects.update_or_create(
|
|
328
306
|
underlying_quote=asset.underlying_quote,
|
|
329
307
|
portfolio_created=asset.portfolio_created,
|
|
@@ -570,6 +548,28 @@ class Trade(ShareMixin, Transaction, OrderedModel, WBModel):
|
|
|
570
548
|
|
|
571
549
|
"""
|
|
572
550
|
|
|
551
|
+
def get_asset(self) -> AssetPosition:
|
|
552
|
+
last_underlying_quote_price = self.last_underlying_quote_price
|
|
553
|
+
if not last_underlying_quote_price:
|
|
554
|
+
raise ValueError("No price found")
|
|
555
|
+
asset = AssetPosition(
|
|
556
|
+
underlying_quote=self.underlying_instrument,
|
|
557
|
+
portfolio_created=None,
|
|
558
|
+
portfolio=self.portfolio,
|
|
559
|
+
date=self.transaction_date,
|
|
560
|
+
initial_currency_fx_rate=self.currency_fx_rate,
|
|
561
|
+
weighting=self._target_weight,
|
|
562
|
+
initial_price=self.last_underlying_quote_price.net_value,
|
|
563
|
+
initial_shares=None,
|
|
564
|
+
underlying_quote_price=self.last_underlying_quote_price,
|
|
565
|
+
asset_valuation_date=self.transaction_date,
|
|
566
|
+
currency=self.currency,
|
|
567
|
+
is_estimated=False,
|
|
568
|
+
)
|
|
569
|
+
asset.set_weighting(self._target_weight)
|
|
570
|
+
asset.pre_save()
|
|
571
|
+
return asset
|
|
572
|
+
|
|
573
573
|
def delete(self, **kwargs):
|
|
574
574
|
pre_collection.send(sender=self.__class__, instance=self)
|
|
575
575
|
super().delete(**kwargs)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
from contextlib import suppress
|
|
1
2
|
from datetime import date, timedelta
|
|
2
3
|
from typing import Generator
|
|
3
4
|
|
|
4
5
|
import pandas as pd
|
|
5
6
|
from django.contrib.contenttypes.models import ContentType
|
|
6
7
|
from django.db import models
|
|
8
|
+
from django.db.models import Sum
|
|
7
9
|
from pandas.tseries.offsets import BDay
|
|
8
10
|
from wbcompliance.models.risk_management import backend
|
|
9
11
|
from wbcore import serializers as wb_serializers
|
|
@@ -14,7 +16,7 @@ from wbfdm.serializers import (
|
|
|
14
16
|
SecurityRepresentationSerializer,
|
|
15
17
|
)
|
|
16
18
|
|
|
17
|
-
from wbportfolio.models import InstrumentPortfolioThroughModel, Portfolio
|
|
19
|
+
from wbportfolio.models import AssetPosition, InstrumentPortfolioThroughModel, Portfolio
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class ActivePortfolioRelationshipMixin(backend.AbstractRuleBackend):
|
|
@@ -173,10 +175,16 @@ class StopLossMixin(backend.AbstractRuleBackend):
|
|
|
173
175
|
for threshold in self.thresholds:
|
|
174
176
|
if threshold.is_inrange(total_perf):
|
|
175
177
|
instrument = Instrument.objects.get(id=tested_instrument_id)
|
|
176
|
-
report_details = {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
report_details = {}
|
|
179
|
+
with suppress(AssetPosition.DoesNotExist):
|
|
180
|
+
previous_portfolio_date = (
|
|
181
|
+
self.product.portfolio.assets.filter(date__lt=self.evaluation_date).latest("date").date
|
|
182
|
+
)
|
|
183
|
+
weighting = self.product.portfolio.assets.filter(
|
|
184
|
+
underlying_quote=instrument, date=previous_portfolio_date
|
|
185
|
+
).aggregate(s=Sum("weighting"))["s"]
|
|
186
|
+
report_details["Portfolio Impact"] = f"{float(weighting) * total_perf:+,.2%}"
|
|
187
|
+
report_details["Field"] = field_label
|
|
180
188
|
if self.benchmark:
|
|
181
189
|
report_details[f"Relative Percentage VS {str(self.benchmark)}"] = f"{total_perf:,.3%}"
|
|
182
190
|
if total_perf < 0:
|
|
@@ -3,11 +3,12 @@ from decimal import Decimal
|
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
5
5
|
from faker import Faker
|
|
6
|
+
from pandas._libs.tslibs.offsets import BDay
|
|
6
7
|
from psycopg.types.range import NumericRange
|
|
7
8
|
from wbcompliance.factories.risk_management import RuleThresholdFactory
|
|
8
9
|
|
|
9
|
-
from wbportfolio.risk_management.backends.
|
|
10
|
-
RuleBackend as
|
|
10
|
+
from wbportfolio.risk_management.backends.stop_loss_portfolio import (
|
|
11
|
+
RuleBackend as StopLossPortfolioRuleBackend,
|
|
11
12
|
)
|
|
12
13
|
from wbportfolio.tests.models.utils import PortfolioTestMixin
|
|
13
14
|
|
|
@@ -21,7 +22,7 @@ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
|
|
|
21
22
|
parameters = {"freq": freq, "date_interval_option": date_interval_option}
|
|
22
23
|
lower = random.random()
|
|
23
24
|
upper = random.uniform(lower, 1)
|
|
24
|
-
return
|
|
25
|
+
return StopLossPortfolioRuleBackend(
|
|
25
26
|
weekday,
|
|
26
27
|
product,
|
|
27
28
|
parameters,
|
|
@@ -31,8 +32,8 @@ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
|
|
|
31
32
|
@pytest.mark.parametrize(
|
|
32
33
|
"date_interval_option, freq",
|
|
33
34
|
[
|
|
34
|
-
("ROLLING_WINDOWS",
|
|
35
|
-
*[("FREQUENCY", option) for option in
|
|
35
|
+
("ROLLING_WINDOWS", StopLossPortfolioRuleBackend.FreqChoices.BUSINESS_DAY),
|
|
36
|
+
*[("FREQUENCY", option) for option in StopLossPortfolioRuleBackend.FreqChoices.values],
|
|
36
37
|
],
|
|
37
38
|
)
|
|
38
39
|
def test_check_rule_frequency(
|
|
@@ -41,40 +42,49 @@ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
|
|
|
41
42
|
date_interval_option,
|
|
42
43
|
freq,
|
|
43
44
|
product,
|
|
44
|
-
|
|
45
|
+
instrument,
|
|
45
46
|
instrument_price_factory,
|
|
46
47
|
asset_position_factory,
|
|
47
48
|
stop_loss_portfolio_backend,
|
|
48
|
-
instrument_portfolio_through_model_factory,
|
|
49
49
|
):
|
|
50
|
-
|
|
51
|
-
instrument=product,
|
|
52
|
-
portfolio=portfolio,
|
|
53
|
-
primary_portfolio=True,
|
|
54
|
-
)
|
|
50
|
+
previous_date = (weekday - BDay(1)).date()
|
|
55
51
|
|
|
56
52
|
d1 = stop_loss_portfolio_backend._get_start_interval()
|
|
57
53
|
|
|
58
54
|
threshold = stop_loss_portfolio_backend.thresholds[0]
|
|
59
55
|
breach_perf = random.uniform(threshold.range.lower, threshold.range.upper)
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
instrument_price_factory.create(
|
|
63
|
-
|
|
57
|
+
instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
|
|
58
|
+
instrument_price_factory.create(date=previous_date, net_value=100, calculated=False, instrument=product)
|
|
59
|
+
instrument_price_factory.create(date=weekday, net_value=100, calculated=False, instrument=product)
|
|
60
|
+
|
|
61
|
+
i1 = instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=instrument)
|
|
62
|
+
instrument_price_factory.create(date=previous_date, calculated=False, instrument=instrument)
|
|
63
|
+
i2 = instrument_price_factory.create(
|
|
64
|
+
date=weekday, net_value=Decimal(breach_perf + 1) * i1.net_value, calculated=False, instrument=instrument
|
|
64
65
|
)
|
|
65
66
|
|
|
66
|
-
asset_position_factory.create(
|
|
67
|
-
|
|
67
|
+
previous_position = asset_position_factory.create(
|
|
68
|
+
date=previous_date, underlying_instrument=instrument, portfolio=product.portfolio
|
|
69
|
+
)
|
|
70
|
+
asset_position_factory.create(date=weekday, underlying_instrument=instrument, portfolio=product.portfolio)
|
|
68
71
|
|
|
72
|
+
expected_performance = i2.net_value / i1.net_value - Decimal(1.0)
|
|
69
73
|
res = list(stop_loss_portfolio_backend.check_rule())
|
|
70
74
|
assert len(res) == 1
|
|
71
|
-
|
|
75
|
+
incident = res[0]
|
|
76
|
+
assert incident.breached_object.id == instrument.id
|
|
77
|
+
assert incident.breached_value == f'<span style="color:green">{expected_performance:+,.2%}</span>'
|
|
78
|
+
assert incident.report_details == {
|
|
79
|
+
"Field": "Net Value",
|
|
80
|
+
"Portfolio Impact": f"{previous_position.weighting * expected_performance:+,.2%}",
|
|
81
|
+
}
|
|
72
82
|
|
|
73
83
|
@pytest.mark.parametrize(
|
|
74
84
|
"date_interval_option, freq",
|
|
75
85
|
[
|
|
76
|
-
("ROLLING_WINDOWS",
|
|
77
|
-
*[("FREQUENCY", option) for option in
|
|
86
|
+
("ROLLING_WINDOWS", StopLossPortfolioRuleBackend.FreqChoices.BUSINESS_DAY),
|
|
87
|
+
*[("FREQUENCY", option) for option in StopLossPortfolioRuleBackend.FreqChoices.values],
|
|
78
88
|
],
|
|
79
89
|
)
|
|
80
90
|
def test_check_rule_frequency_2(
|
|
@@ -83,33 +93,28 @@ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
|
|
|
83
93
|
date_interval_option,
|
|
84
94
|
freq,
|
|
85
95
|
product,
|
|
86
|
-
portfolio,
|
|
87
96
|
instrument_price_factory,
|
|
88
97
|
asset_position_factory,
|
|
89
98
|
instrument_factory,
|
|
90
99
|
stop_loss_portfolio_backend,
|
|
91
|
-
instrument_portfolio_through_model_factory,
|
|
92
100
|
):
|
|
93
|
-
instrument_portfolio_through_model_factory.create(
|
|
94
|
-
instrument=product,
|
|
95
|
-
portfolio=portfolio,
|
|
96
|
-
primary_portfolio=True,
|
|
97
|
-
)
|
|
98
101
|
d1 = stop_loss_portfolio_backend._get_start_interval()
|
|
99
102
|
benchmark = instrument_factory.create()
|
|
103
|
+
instrument = instrument_factory.create()
|
|
100
104
|
|
|
101
105
|
threshold = stop_loss_portfolio_backend.thresholds[0]
|
|
102
106
|
threshold.range = NumericRange(upper=-0.5, lower=None) # type: ignore
|
|
103
107
|
threshold.save()
|
|
104
108
|
|
|
105
|
-
instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=product)
|
|
106
109
|
instrument_price_factory.create(date=weekday, net_value=100, calculated=False, instrument=product)
|
|
107
110
|
|
|
111
|
+
instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=instrument)
|
|
112
|
+
instrument_price_factory.create(date=weekday, net_value=100, calculated=False, instrument=instrument)
|
|
113
|
+
|
|
108
114
|
instrument_price_factory.create(date=d1, net_value=100, calculated=False, instrument=benchmark)
|
|
109
115
|
instrument_price_factory.create(date=weekday, net_value=300, calculated=False, instrument=benchmark)
|
|
110
116
|
|
|
111
|
-
asset_position_factory.create(date=weekday, underlying_instrument=
|
|
112
|
-
asset_position_factory.create(date=weekday, underlying_instrument=product, portfolio=portfolio)
|
|
117
|
+
asset_position_factory.create(date=weekday, underlying_instrument=instrument, portfolio=product.portfolio)
|
|
113
118
|
|
|
114
119
|
res = list(stop_loss_portfolio_backend.check_rule())
|
|
115
120
|
assert len(res) == 0
|
|
@@ -117,4 +122,4 @@ class TestStopLossPortfolioRuleModel(PortfolioTestMixin):
|
|
|
117
122
|
setattr(stop_loss_portfolio_backend, "static_benchmark", benchmark)
|
|
118
123
|
res = list(stop_loss_portfolio_backend.check_rule())
|
|
119
124
|
assert len(res) == 1
|
|
120
|
-
assert res[0].breached_object.id ==
|
|
125
|
+
assert res[0].breached_object.id == instrument.id
|
|
@@ -245,7 +245,7 @@ wbportfolio/migrations/0076_alter_dividendtransaction_price_and_more.py,sha256=4
|
|
|
245
245
|
wbportfolio/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
246
246
|
wbportfolio/models/__init__.py,sha256=IIS_PNRxyX2Dcvyk1bcQOUzFt0B9SPC0WlM88CXqj04,881
|
|
247
247
|
wbportfolio/models/adjustments.py,sha256=osXWkJZOiansPWYPyHtl7Z121zDWi7u1YMtrBQtbHVo,10272
|
|
248
|
-
wbportfolio/models/asset.py,sha256
|
|
248
|
+
wbportfolio/models/asset.py,sha256=wwAWW2vl21nJS7RVswjvB1PAZRjJ1VzCyLF4fb7R0Gw,45264
|
|
249
249
|
wbportfolio/models/custodians.py,sha256=owTiS2Vm5CRKzh9M_P9GOVg-s-ndQ9UvRmw3yZP7cw0,3815
|
|
250
250
|
wbportfolio/models/exceptions.py,sha256=3ix0tWUO-O6jpz8f07XIwycw2x3JFRoWzjwil8FVA2Q,52
|
|
251
251
|
wbportfolio/models/indexes.py,sha256=iLYF2gzNzX4GLj_Nh3fybUcAQ1TslnT0wgQ6mN164QI,728
|
|
@@ -276,8 +276,8 @@ wbportfolio/models/transactions/dividends.py,sha256=92-jG8bZN9nU9oDubpu-UDH43Ri7
|
|
|
276
276
|
wbportfolio/models/transactions/expiry.py,sha256=vnNHdcC1hf2HP4rAbmoGgOfagBYKNFytqOwzOI0MlVI,144
|
|
277
277
|
wbportfolio/models/transactions/fees.py,sha256=ffvqo8I4A0l5rLi00jJ6sGot0jmnkoxaNsbDzdPLwCg,5712
|
|
278
278
|
wbportfolio/models/transactions/rebalancing.py,sha256=obzgewWKOD4kJbCoF5fhtfDk502QkbrjPKh8T9KDGew,7355
|
|
279
|
-
wbportfolio/models/transactions/trade_proposals.py,sha256=
|
|
280
|
-
wbportfolio/models/transactions/trades.py,sha256=
|
|
279
|
+
wbportfolio/models/transactions/trade_proposals.py,sha256=qUuCftA7iPLprSVSM7Opkf4hgWxhE-WivRkVWIaFzBw,30541
|
|
280
|
+
wbportfolio/models/transactions/trades.py,sha256=4qA0cEff_hcV3kMnmJKXr0wSThpKhhm0HbzHbspBuxw,28919
|
|
281
281
|
wbportfolio/models/transactions/transactions.py,sha256=fWoDf0TSV0L0gLUDOQpCRLzjMt1H4MUvUHGEaMsilCc,7027
|
|
282
282
|
wbportfolio/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
283
283
|
wbportfolio/pms/typing.py,sha256=b2pBWYt1E8ok-Kqm0lEFIakSnWJ6Ib57z-VX3C3gkQc,6081
|
|
@@ -302,9 +302,9 @@ wbportfolio/risk_management/backends/accounts.py,sha256=VMHwhzeFV2bzobb1RlEJadTV
|
|
|
302
302
|
wbportfolio/risk_management/backends/controversy_portfolio.py,sha256=ncEHyJMVomv0ehx7LoWcux1YHLV6KYYmiOkIBzJ0P1M,2852
|
|
303
303
|
wbportfolio/risk_management/backends/exposure_portfolio.py,sha256=8T97J0pbnGdme_Ji9ntA5FV9MCouSxDz5grMvRvpl70,10760
|
|
304
304
|
wbportfolio/risk_management/backends/instrument_list_portfolio.py,sha256=Sh8PZHHKFAl4Aw8fOlu17fUSiEFYYemb-B0iltAXc10,4186
|
|
305
|
-
wbportfolio/risk_management/backends/liquidity_risk.py,sha256=
|
|
305
|
+
wbportfolio/risk_management/backends/liquidity_risk.py,sha256=OnuUypmN86RDnch1JOb8UJo5j1z1_X7sjazqAo7cbwM,3694
|
|
306
306
|
wbportfolio/risk_management/backends/liquidity_stress_instrument.py,sha256=oitzsaZu-HhYn9Avku3322GtDmf6QGsfyRzGPGZoM1Y,3612
|
|
307
|
-
wbportfolio/risk_management/backends/mixins.py,sha256=
|
|
307
|
+
wbportfolio/risk_management/backends/mixins.py,sha256=C7D8MXInl3m05GiLxQP4K5uomq4mpvi81zqsCiXq6g0,10006
|
|
308
308
|
wbportfolio/risk_management/backends/product_integrity.py,sha256=3BbQBYYzLdj2iuQb9QOIgPjWMz1FaPkvEJoNgxWVfz0,4598
|
|
309
309
|
wbportfolio/risk_management/backends/stop_loss_instrument.py,sha256=qIzeV26TRdbgG8-Nsh5O0NPMrPmIXeaRdPR-EF_uaLo,1128
|
|
310
310
|
wbportfolio/risk_management/backends/stop_loss_portfolio.py,sha256=yiDa3FkZKdrmHSf8p1XmT8RhJW6KyiMuypYpY39SiO8,1674
|
|
@@ -318,7 +318,7 @@ wbportfolio/risk_management/tests/test_instrument_list_portfolio.py,sha256=1pAsu
|
|
|
318
318
|
wbportfolio/risk_management/tests/test_liquidity_risk.py,sha256=UtzBrW7LeY0Z0ARX9pg4CUjgdw8u5Hfrdkddro5EEZ8,1713
|
|
319
319
|
wbportfolio/risk_management/tests/test_product_integrity.py,sha256=wqJ4b5DO2BrEVQF4eVX2B9AvfrRMAyC_lppGrgKc_ug,1959
|
|
320
320
|
wbportfolio/risk_management/tests/test_stop_loss_instrument.py,sha256=ZueuA9qQOv6JdKuRiPLGV3auOLRnoUCZzfqcQzGJGig,4197
|
|
321
|
-
wbportfolio/risk_management/tests/test_stop_loss_portfolio.py,sha256=
|
|
321
|
+
wbportfolio/risk_management/tests/test_stop_loss_portfolio.py,sha256=_CLyC4mWPCP92LuERk6gnrr-JG78WCB83ezl5fPugLo,5195
|
|
322
322
|
wbportfolio/risk_management/tests/test_ucits_portfolio.py,sha256=UcZLhatl8iU9AhNNYv6OBs_cjILZKaws2nvUen6orkc,1807
|
|
323
323
|
wbportfolio/serializers/__init__.py,sha256=w1nPxkapcnGngYwiU7BGSLfyhJ67_PQWVjuya7YSxX0,1795
|
|
324
324
|
wbportfolio/serializers/adjustments.py,sha256=0yTPgDjGRyzSa-ecCklENKtLJXZhq1dryMI3nTRsQRo,783
|
|
@@ -521,7 +521,7 @@ wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQV
|
|
|
521
521
|
wbportfolio/viewsets/transactions/trade_proposals.py,sha256=iQpC_Thbj56SmM05vPRsF1JZguGBDaTUH3I-_iCHCV0,5958
|
|
522
522
|
wbportfolio/viewsets/transactions/trades.py,sha256=-yJ4j8NJTu2VWyhCq5BXGNND_925Ietoxx9k07SLVh0,21634
|
|
523
523
|
wbportfolio/viewsets/transactions/transactions.py,sha256=ixDp-nsNA8t_A06rBCT19hOMJHy0iRmdz1XKdV1OwAs,4450
|
|
524
|
-
wbportfolio-1.
|
|
525
|
-
wbportfolio-1.
|
|
526
|
-
wbportfolio-1.
|
|
527
|
-
wbportfolio-1.
|
|
524
|
+
wbportfolio-1.50.1.dist-info/METADATA,sha256=Yi-dunYxBzs9RvSKEaW9q4m6NkFz9WvbeNf9gWUSJfY,734
|
|
525
|
+
wbportfolio-1.50.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
526
|
+
wbportfolio-1.50.1.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
527
|
+
wbportfolio-1.50.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|