wbportfolio 1.54.7__py2.py3-none-any.whl → 1.54.9__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 +5 -1
- wbportfolio/models/portfolio.py +12 -14
- wbportfolio/tests/models/test_portfolios.py +19 -10
- {wbportfolio-1.54.7.dist-info → wbportfolio-1.54.9.dist-info}/METADATA +1 -1
- {wbportfolio-1.54.7.dist-info → wbportfolio-1.54.9.dist-info}/RECORD +7 -7
- {wbportfolio-1.54.7.dist-info → wbportfolio-1.54.9.dist-info}/WHEEL +0 -0
- {wbportfolio-1.54.7.dist-info → wbportfolio-1.54.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -158,9 +158,13 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
158
158
|
self.import_source.log += "\nNo trade was successfully matched."
|
|
159
159
|
|
|
160
160
|
def _get_history(self, history: Dict[str, Any]) -> models.QuerySet:
|
|
161
|
+
from wbportfolio.models.transactions.trade_proposals import TradeProposal
|
|
162
|
+
|
|
161
163
|
if trade_proposal_id := history.get("trade_proposal_id"):
|
|
162
164
|
# if a trade proposal is provided, we delete the existing history first as otherwise, it would mess with the target weight computation
|
|
163
|
-
|
|
165
|
+
trade_proposal = TradeProposal.objects.get(id=trade_proposal_id)
|
|
166
|
+
trade_proposal.trades.all().delete()
|
|
167
|
+
trade_proposal.reset_trades()
|
|
164
168
|
trades = self.model.objects.none()
|
|
165
169
|
else:
|
|
166
170
|
trades = self.model.objects.filter(
|
wbportfolio/models/portfolio.py
CHANGED
|
@@ -94,22 +94,19 @@ def get_returns(
|
|
|
94
94
|
df = pd.DataFrame(Instrument.objects.filter(id__in=instrument_ids).dl.market_data(**kwargs))
|
|
95
95
|
if df.empty:
|
|
96
96
|
raise InvalidAnalyticPortfolio()
|
|
97
|
-
|
|
98
|
-
index="valuation_date", columns="instrument_id", values="fx_rate"
|
|
97
|
+
df = df[["instrument_id", "fx_rate", "close", "valuation_date"]].pivot(
|
|
98
|
+
index="valuation_date", columns="instrument_id", values=["fx_rate", "close"]
|
|
99
99
|
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
index="valuation_date", columns="instrument_id", values="close"
|
|
103
|
-
)
|
|
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)
|
|
100
|
+
ts = pd.bdate_range(df.index.min(), df.index.max(), freq="B")
|
|
101
|
+
df = df.reindex(ts)
|
|
108
102
|
if ffill_returns:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
df = df.ffill()
|
|
104
|
+
df.index = pd.to_datetime(df.index)
|
|
105
|
+
|
|
106
|
+
prices_df = df["close"]
|
|
107
|
+
fx_rate_df = df["fx_rate"]
|
|
108
|
+
returns = prices_to_returns(fx_rate_df * prices_df, drop_inceptions_nan=False, fill_nan=ffill_returns)
|
|
109
|
+
return {ts.date(): row for ts, row in prices_df.replace(np.nan, None).to_dict("index").items()}, returns.replace(
|
|
113
110
|
[np.inf, -np.inf, np.nan], 0
|
|
114
111
|
)
|
|
115
112
|
|
|
@@ -1095,6 +1092,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
1095
1092
|
)
|
|
1096
1093
|
if broadcast_changes_at_date:
|
|
1097
1094
|
for update_date, changed_weights in positions.get_weights().items():
|
|
1095
|
+
kwargs.pop("changed_weights", None)
|
|
1098
1096
|
self.change_at_date(update_date, changed_weights=changed_weights, **kwargs)
|
|
1099
1097
|
|
|
1100
1098
|
@classmethod
|
|
@@ -1134,31 +1134,40 @@ class TestPortfolioModel(PortfolioTestMixin):
|
|
|
1134
1134
|
assert res == [index_portfolio, dependency_portfolio, dependant_portfolio, undependant_portfolio]
|
|
1135
1135
|
|
|
1136
1136
|
def test_get_returns(self, instrument_factory, instrument_price_factory, asset_position_factory, portfolio):
|
|
1137
|
-
v1 = date(
|
|
1138
|
-
v2 = date(2025, 1,
|
|
1139
|
-
v3 = date(2025, 1,
|
|
1137
|
+
v1 = date(2024, 12, 31)
|
|
1138
|
+
v2 = date(2025, 1, 1)
|
|
1139
|
+
v3 = date(2025, 1, 2)
|
|
1140
|
+
v4 = date(2025, 1, 3)
|
|
1140
1141
|
|
|
1141
1142
|
i1 = instrument_factory.create()
|
|
1142
1143
|
i2 = instrument_factory.create()
|
|
1143
1144
|
|
|
1144
1145
|
i11 = instrument_price_factory.create(date=v1, instrument=i1)
|
|
1145
1146
|
i12 = instrument_price_factory.create(date=v2, instrument=i1)
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
asset_position_factory.create(date=v3, portfolio=portfolio, underlying_instrument=i2)
|
|
1147
|
+
i14 = instrument_price_factory.create(date=v4, instrument=i1)
|
|
1148
|
+
i21 = instrument_price_factory.create(date=v1, instrument=i2)
|
|
1149
1149
|
i11.refresh_from_db()
|
|
1150
1150
|
i12.refresh_from_db()
|
|
1151
|
-
|
|
1152
|
-
|
|
1151
|
+
i14.refresh_from_db()
|
|
1152
|
+
prices, returns = get_returns([i1.id, i2.id], from_date=v1, to_date=v4)
|
|
1153
1153
|
|
|
1154
1154
|
expected_returns = pd.DataFrame(
|
|
1155
|
-
[[i12.net_value / i11.net_value - 1, 0.0], [
|
|
1156
|
-
index=[v2, v3],
|
|
1155
|
+
[[i12.net_value / i11.net_value - 1, 0.0], [0.0, 0.0], [i14.net_value / i12.net_value - 1, 0.0]],
|
|
1156
|
+
index=[v2, v3, v4],
|
|
1157
1157
|
columns=[i1.id, i2.id],
|
|
1158
1158
|
dtype="float64",
|
|
1159
1159
|
)
|
|
1160
1160
|
expected_returns.index = pd.to_datetime(expected_returns.index)
|
|
1161
1161
|
pd.testing.assert_frame_equal(returns, expected_returns, check_names=False, check_freq=False, atol=1e-6)
|
|
1162
|
+
assert prices[v1][i1.id] == float(i11.net_value)
|
|
1163
|
+
assert prices[v2][i1.id] == float(i12.net_value)
|
|
1164
|
+
assert prices[v3][i1.id] == float(i12.net_value)
|
|
1165
|
+
assert prices[v4][i1.id] == float(i14.net_value)
|
|
1166
|
+
# test that the returned price are ffill
|
|
1167
|
+
assert prices[v1][i2.id] == float(i21.net_value)
|
|
1168
|
+
assert prices[v2][i2.id] == float(i21.net_value)
|
|
1169
|
+
assert prices[v3][i2.id] == float(i21.net_value)
|
|
1170
|
+
assert prices[v4][i2.id] == float(i21.net_value)
|
|
1162
1171
|
|
|
1163
1172
|
@patch.object(Portfolio, "compute_lookthrough", autospec=True)
|
|
1164
1173
|
def test_handle_controlling_portfolio_change_at_date(self, mock_compute_lookthrough, weekday, portfolio_factory):
|
|
@@ -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=oLYNEAFyDeInvHjuAbB4pe60q6VmHX0wGcyuEGofbKM,12700
|
|
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=9gbT3tuo73SfhBn4_HIes71feyjTXnZnn9lX2KL_1pQ,57861
|
|
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
|
|
@@ -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=sWbt9NE8Cludu7hUhxxnIJwOSQ_U_yC6UPFPkbpV8Qk,54010
|
|
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
|
|
@@ -522,7 +522,7 @@ wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIH
|
|
|
522
522
|
wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQVTN2MH1kG_yCeVkmyK8k,1263
|
|
523
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.9.dist-info/METADATA,sha256=SL5iDKkrof3J3-X4ykL9jJslbUo8ltmEwJzni_emoXY,702
|
|
526
|
+
wbportfolio-1.54.9.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
527
|
+
wbportfolio-1.54.9.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
528
|
+
wbportfolio-1.54.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|