wbportfolio 1.49.10__py2.py3-none-any.whl → 1.50.0__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 +4 -7
- wbportfolio/models/portfolio.py +34 -19
- 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/tests/models/test_portfolios.py +2 -2
- {wbportfolio-1.49.10.dist-info → wbportfolio-1.50.0.dist-info}/METADATA +1 -1
- {wbportfolio-1.49.10.dist-info → wbportfolio-1.50.0.dist-info}/RECORD +10 -10
- {wbportfolio-1.49.10.dist-info → wbportfolio-1.50.0.dist-info}/WHEEL +0 -0
- {wbportfolio-1.49.10.dist-info → wbportfolio-1.50.0.dist-info}/licenses/LICENSE +0 -0
wbportfolio/models/asset.py
CHANGED
|
@@ -88,7 +88,7 @@ class AssetPositionIterator:
|
|
|
88
88
|
# Initialize data stores with type hints
|
|
89
89
|
self._instruments = {}
|
|
90
90
|
self._fx_rates = defaultdict(dict)
|
|
91
|
-
self._weights = dict
|
|
91
|
+
self._weights = defaultdict(dict)
|
|
92
92
|
self._prices = prices or defaultdict(dict)
|
|
93
93
|
self.positions = dict()
|
|
94
94
|
|
|
@@ -121,7 +121,6 @@ class AssetPositionIterator:
|
|
|
121
121
|
currency_fx_rate_portfolio_to_usd = self._get_fx_rate(val_date, self.portfolio.currency)
|
|
122
122
|
currency_fx_rate_instrument_to_usd = self._get_fx_rate(val_date, underlying_quote.currency)
|
|
123
123
|
price = self._get_price(val_date, underlying_quote)
|
|
124
|
-
|
|
125
124
|
position = AssetPosition(
|
|
126
125
|
underlying_quote=underlying_quote,
|
|
127
126
|
weighting=weighting,
|
|
@@ -173,10 +172,8 @@ class AssetPositionIterator:
|
|
|
173
172
|
position.portfolio = self.portfolio
|
|
174
173
|
if position.initial_price is not None:
|
|
175
174
|
self.positions[key] = position
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
except KeyError:
|
|
179
|
-
self._weights[position.date] = {position.underlying_quote.id: float(position.weighting)}
|
|
175
|
+
self._weights[position.date][position.underlying_quote.id] = float(position.weighting)
|
|
176
|
+
|
|
180
177
|
return self
|
|
181
178
|
|
|
182
179
|
def get_dates(self) -> list[date]:
|
|
@@ -185,7 +182,7 @@ class AssetPositionIterator:
|
|
|
185
182
|
|
|
186
183
|
def get_weights(self) -> dict[date, dict[int, float]]:
|
|
187
184
|
"""Get weight structure with instrument IDs as keys"""
|
|
188
|
-
return self._weights
|
|
185
|
+
return dict(self._weights)
|
|
189
186
|
|
|
190
187
|
def __iter__(self):
|
|
191
188
|
# return an iterable excluding the position without a valid weighting
|
wbportfolio/models/portfolio.py
CHANGED
|
@@ -54,7 +54,24 @@ if TYPE_CHECKING:
|
|
|
54
54
|
from wbportfolio.models.transactions.trade_proposals import TradeProposal
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def
|
|
57
|
+
def get_prices(instrument_ids: list[int], from_date: date, to_date: date) -> dict[date, dict[int, float]]:
|
|
58
|
+
"""
|
|
59
|
+
Utility to fetch raw prices
|
|
60
|
+
"""
|
|
61
|
+
prices = InstrumentPrice.objects.filter(instrument__in=instrument_ids, date__gte=from_date, date__lte=to_date)
|
|
62
|
+
df = (
|
|
63
|
+
pd.DataFrame(
|
|
64
|
+
prices.filter_only_valid_prices().values_list("instrument", "net_value", "date"),
|
|
65
|
+
columns=["instrument", "net_value", "date"],
|
|
66
|
+
)
|
|
67
|
+
.pivot_table(index="date", values="net_value", columns="instrument")
|
|
68
|
+
.astype(float)
|
|
69
|
+
.sort_index()
|
|
70
|
+
)
|
|
71
|
+
ts = pd.bdate_range(df.index.min(), df.index.max(), freq="B")
|
|
72
|
+
df = df.reindex(ts)
|
|
73
|
+
df = df.ffill()
|
|
74
|
+
df.index = pd.to_datetime(df.index)
|
|
58
75
|
return {ts.date(): row for ts, row in df.to_dict("index").items()}
|
|
59
76
|
|
|
60
77
|
|
|
@@ -64,7 +81,7 @@ def get_returns(
|
|
|
64
81
|
to_date: date,
|
|
65
82
|
to_currency: Currency | None = None,
|
|
66
83
|
ffill_returns: bool = True,
|
|
67
|
-
) ->
|
|
84
|
+
) -> pd.DataFrame:
|
|
68
85
|
"""
|
|
69
86
|
Utility methods to get instrument returns for a given date range
|
|
70
87
|
|
|
@@ -99,7 +116,7 @@ def get_returns(
|
|
|
99
116
|
prices_df = prices_df.ffill()
|
|
100
117
|
prices_df.index = pd.to_datetime(prices_df.index)
|
|
101
118
|
returns = prices_to_returns(prices_df, drop_inceptions_nan=False, fill_nan=ffill_returns)
|
|
102
|
-
return returns.replace([np.inf, -np.inf, np.nan], 0)
|
|
119
|
+
return returns.replace([np.inf, -np.inf, np.nan], 0)
|
|
103
120
|
|
|
104
121
|
|
|
105
122
|
class DefaultPortfolioQueryset(QuerySet):
|
|
@@ -364,7 +381,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
364
381
|
|
|
365
382
|
def get_analytic_portfolio(
|
|
366
383
|
self, val_date: date, weights: dict[int, float] | None = None, **kwargs
|
|
367
|
-
) ->
|
|
384
|
+
) -> AnalyticPortfolio:
|
|
368
385
|
"""
|
|
369
386
|
Return the analytic portfolio associated with this portfolio at the given date
|
|
370
387
|
|
|
@@ -378,18 +395,15 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
378
395
|
if not weights:
|
|
379
396
|
weights = self.get_weights(val_date)
|
|
380
397
|
return_date = (val_date + BDay(1)).date()
|
|
381
|
-
returns
|
|
398
|
+
returns = get_returns(
|
|
382
399
|
list(weights.keys()), (val_date - BDay(2)).date(), return_date, to_currency=self.currency, **kwargs
|
|
383
400
|
)
|
|
384
401
|
if pd.Timestamp(return_date) not in returns.index:
|
|
385
402
|
raise InvalidAnalyticPortfolio()
|
|
386
403
|
returns = returns.fillna(0) # not sure this is what we want
|
|
387
|
-
return (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
weights=weights,
|
|
391
|
-
),
|
|
392
|
-
prices,
|
|
404
|
+
return AnalyticPortfolio(
|
|
405
|
+
X=returns,
|
|
406
|
+
weights=weights,
|
|
393
407
|
)
|
|
394
408
|
|
|
395
409
|
def is_invested_at_date(self, val_date: date) -> bool:
|
|
@@ -781,15 +795,18 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
781
795
|
weights = drifted_positions.get_weights()[start_date]
|
|
782
796
|
|
|
783
797
|
# Get returns and prices data for the whole date range
|
|
784
|
-
|
|
785
|
-
|
|
798
|
+
instrument_ids = list(weights.keys())
|
|
799
|
+
returns = get_returns(
|
|
800
|
+
instrument_ids,
|
|
786
801
|
(start_date - BDay(3)).date(),
|
|
787
802
|
end_date,
|
|
788
803
|
to_currency=self.currency,
|
|
789
804
|
ffill_returns=True,
|
|
790
805
|
)
|
|
806
|
+
# Get raw prices to speed up asset position creation
|
|
807
|
+
prices = get_prices(instrument_ids, (start_date - BDay(3)).date(), end_date)
|
|
791
808
|
# Instantiate the position iterator with the initial weights
|
|
792
|
-
positions = AssetPositionIterator(self, prices=
|
|
809
|
+
positions = AssetPositionIterator(self, prices=prices)
|
|
793
810
|
last_trade_proposal = None
|
|
794
811
|
for to_date_ts in pd.date_range(start_date + timedelta(days=1), end_date, freq="B"):
|
|
795
812
|
to_date = to_date_ts.date()
|
|
@@ -883,10 +900,8 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
883
900
|
|
|
884
901
|
setattr(position, "path", path)
|
|
885
902
|
position.initial_shares = None
|
|
886
|
-
if portfolio_total_asset_value:
|
|
887
|
-
position.initial_shares = (position.weighting * portfolio_total_asset_value) /
|
|
888
|
-
position.price * position.currency_fx_rate
|
|
889
|
-
)
|
|
903
|
+
if portfolio_total_asset_value and (price_fx_portfolio := position.price * position.currency_fx_rate):
|
|
904
|
+
position.initial_shares = (position.weighting * portfolio_total_asset_value) / price_fx_portfolio
|
|
890
905
|
if child_portfolio := position.underlying_quote.primary_portfolio:
|
|
891
906
|
if with_intermediary_position:
|
|
892
907
|
yield position
|
|
@@ -1191,7 +1206,7 @@ def default_estimate_net_value(
|
|
|
1191
1206
|
with suppress(
|
|
1192
1207
|
IndexError, InvalidAnalyticPortfolio
|
|
1193
1208
|
): # we silent any indexerror introduced by no returns for the past days
|
|
1194
|
-
analytic_portfolio
|
|
1209
|
+
analytic_portfolio = portfolio.get_analytic_portfolio(previous_val_date, weights=weights)
|
|
1195
1210
|
return analytic_portfolio.get_estimate_net_value(float(last_price.net_value))
|
|
1196
1211
|
|
|
1197
1212
|
|
|
@@ -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
|
|
@@ -925,7 +925,7 @@ class TestPortfolioModel(PortfolioTestMixin):
|
|
|
925
925
|
)
|
|
926
926
|
a2.refresh_from_db()
|
|
927
927
|
|
|
928
|
-
analytic_portfolio
|
|
928
|
+
analytic_portfolio = portfolio.get_analytic_portfolio(weekday)
|
|
929
929
|
assert analytic_portfolio.weights.tolist() == [float(a1.weighting), float(a2.weighting)]
|
|
930
930
|
expected_X = pd.DataFrame(
|
|
931
931
|
[[float(p11.net_value / p10.net_value - Decimal(1)), float(p21.net_value / p20.net_value - Decimal(1))]],
|
|
@@ -1139,7 +1139,7 @@ class TestPortfolioModel(PortfolioTestMixin):
|
|
|
1139
1139
|
i11.refresh_from_db()
|
|
1140
1140
|
i12.refresh_from_db()
|
|
1141
1141
|
i13.refresh_from_db()
|
|
1142
|
-
returns
|
|
1142
|
+
returns = get_returns([i1.id, i2.id], from_date=v1, to_date=v3)
|
|
1143
1143
|
|
|
1144
1144
|
expected_returns = pd.DataFrame(
|
|
1145
1145
|
[[i12.net_value / i11.net_value - 1, 0.0], [i13.net_value / i12.net_value - 1, 0.0]],
|
|
@@ -245,11 +245,11 @@ 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=-bw8AoIIBy68je1vnGXYbkb7Vdp_mRBNJixd0qnCWxA,45153
|
|
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
|
|
252
|
-
wbportfolio/models/portfolio.py,sha256=
|
|
252
|
+
wbportfolio/models/portfolio.py,sha256=Rq9ih9aPhLC1RukrIF_wrmtRQm0-3w4i9NEtWJGZ02g,55742
|
|
253
253
|
wbportfolio/models/portfolio_cash_flow.py,sha256=uElG7IJUBY8qvtrXftOoskX6EA-dKgEG1JJdvHeWV7g,7336
|
|
254
254
|
wbportfolio/models/portfolio_cash_targets.py,sha256=WmgG-etPisZsh2yaFQpz7EkpvAudKBEzqPsO715w52U,1498
|
|
255
255
|
wbportfolio/models/portfolio_relationship.py,sha256=mMb18UMRWg9kx_9uIPkMktwORuXXLjKdgRPQQvB6fVE,5486
|
|
@@ -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
|
|
@@ -374,7 +374,7 @@ wbportfolio/tests/models/test_merge.py,sha256=sdsjiZsmR6vsUKwTa5kkvL6QTeAZqtd_EP
|
|
|
374
374
|
wbportfolio/tests/models/test_portfolio_cash_flow.py,sha256=X8dsXexsb1b0lBiuGzu40ps_Az_1UmmKT0eo1vbXH94,5792
|
|
375
375
|
wbportfolio/tests/models/test_portfolio_cash_targets.py,sha256=q8QWAwt-kKRkLC0E05GyRhF_TTQXIi8bdHjXVU0fCV0,965
|
|
376
376
|
wbportfolio/tests/models/test_portfolio_swing_pricings.py,sha256=kr2AOcQkyg2pX3ULjU-o9ye-NVpjMrrfoe-DVbYCbjs,1656
|
|
377
|
-
wbportfolio/tests/models/test_portfolios.py,sha256=
|
|
377
|
+
wbportfolio/tests/models/test_portfolios.py,sha256=2VqUF3HALY1VvCksARk4XJlQL-qr2PbF2DMmw9KQzdY,53132
|
|
378
378
|
wbportfolio/tests/models/test_product_groups.py,sha256=AcdxhurV-n_bBuUsfD1GqVtwLFcs7VI2CRrwzsIUWbU,3337
|
|
379
379
|
wbportfolio/tests/models/test_products.py,sha256=FkQLms3kXzyg6mNEEJcHUVKu8YbATY9l6lZyaRUnpjw,9314
|
|
380
380
|
wbportfolio/tests/models/test_roles.py,sha256=4Cn7WyrA2ztJNeWLk5cy9kYo5XLWMbFSvo1O-9JYxeA,3323
|
|
@@ -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.0.dist-info/METADATA,sha256=NdlRGICQMfP56DoEYZOoiqLUhXqMOOjg1Rnrxl2KzVQ,734
|
|
525
|
+
wbportfolio-1.50.0.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
526
|
+
wbportfolio-1.50.0.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
527
|
+
wbportfolio-1.50.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|