wbportfolio 1.55.0__py2.py3-none-any.whl → 1.55.2__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 +2 -2
- wbportfolio/import_export/parsers/ubs/customer_trade.py +7 -5
- wbportfolio/models/orders/order_proposals.py +4 -2
- wbportfolio/models/portfolio.py +1 -1
- wbportfolio/tests/models/test_imports.py +2 -3
- wbportfolio/viewsets/orders/order_proposals.py +7 -8
- wbportfolio/viewsets/orders/orders.py +2 -2
- {wbportfolio-1.55.0.dist-info → wbportfolio-1.55.2.dist-info}/METADATA +1 -1
- {wbportfolio-1.55.0.dist-info → wbportfolio-1.55.2.dist-info}/RECORD +11 -11
- {wbportfolio-1.55.0.dist-info → wbportfolio-1.55.2.dist-info}/WHEEL +0 -0
- {wbportfolio-1.55.0.dist-info → wbportfolio-1.55.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -99,7 +99,7 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
99
99
|
if history.exists():
|
|
100
100
|
queryset = history
|
|
101
101
|
else:
|
|
102
|
-
queryset = self.model.objects.filter(marked_for_deletion=False)
|
|
102
|
+
queryset = self.model.objects.filter(marked_for_deletion=False).exclude(id__in=self.processed_ids)
|
|
103
103
|
|
|
104
104
|
queryset = queryset.filter(
|
|
105
105
|
models.Q(underlying_instrument=data["underlying_instrument"]) & models.Q(**dates_lookup)
|
|
@@ -116,7 +116,6 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
116
116
|
if external_id_queryset.count() == 1:
|
|
117
117
|
self.import_source.log += f"External ID {external_id} provided -> Load CustomerTrade"
|
|
118
118
|
return external_id_queryset.first()
|
|
119
|
-
|
|
120
119
|
if portfolio := data.get("portfolio", None):
|
|
121
120
|
queryset = queryset.filter(portfolio=portfolio)
|
|
122
121
|
if queryset.exists():
|
|
@@ -142,6 +141,7 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
142
141
|
if queryset.exists():
|
|
143
142
|
# We try to filter by price as well
|
|
144
143
|
trade = queryset.first()
|
|
144
|
+
|
|
145
145
|
if queryset.count() == 1:
|
|
146
146
|
self.import_source.log += f"\nOne Trade found: {trade}"
|
|
147
147
|
if queryset.count() > 1:
|
|
@@ -3,15 +3,15 @@ from typing import Dict
|
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
6
|
-
from
|
|
6
|
+
from slugify import slugify
|
|
7
7
|
|
|
8
8
|
from wbportfolio.models import Trade
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def parse_row(obj: Dict,
|
|
11
|
+
def parse_row(obj: Dict, negate_shares: bool = False) -> Dict:
|
|
12
12
|
isin = obj["underlying_instrument__isin"]
|
|
13
13
|
shares = obj["shares"]
|
|
14
|
-
if
|
|
14
|
+
if negate_shares:
|
|
15
15
|
shares = -1 * shares
|
|
16
16
|
return {
|
|
17
17
|
"underlying_instrument": {"isin": isin, "instrument_type": "product"},
|
|
@@ -30,6 +30,9 @@ def parse(import_source):
|
|
|
30
30
|
xx, yy = np.where(df == "Trade Date")
|
|
31
31
|
df = df.iloc[xx[0] :, yy[0] :]
|
|
32
32
|
df = df.rename(columns=df.iloc[0]).drop(df.index[0]).dropna(how="all")
|
|
33
|
+
negate_shares = "net-quantity" in list(
|
|
34
|
+
map(lambda c: slugify(c), df.columns)
|
|
35
|
+
) # we slugified the column to be more robust
|
|
33
36
|
df = df.rename(columns=lambda x: x.lower())
|
|
34
37
|
df = df.rename(
|
|
35
38
|
columns={
|
|
@@ -50,6 +53,5 @@ def parse(import_source):
|
|
|
50
53
|
df.loc[df["custodian"].isnull(), "custodian"] = "N/A"
|
|
51
54
|
data = list()
|
|
52
55
|
for d in df.to_dict("records"):
|
|
53
|
-
data.append(parse_row(d,
|
|
54
|
-
|
|
56
|
+
data.append(parse_row(d, negate_shares=negate_shares))
|
|
55
57
|
return {"data": data}
|
|
@@ -319,7 +319,9 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
319
319
|
|
|
320
320
|
def prepare_orders_for_execution(self) -> list[OrderDTO]:
|
|
321
321
|
executable_orders = []
|
|
322
|
-
for order in
|
|
322
|
+
for order in (
|
|
323
|
+
self.get_orders().exclude(underlying_instrument__is_cash=True).select_related("underlying_instrument")
|
|
324
|
+
):
|
|
323
325
|
instrument = order.underlying_instrument
|
|
324
326
|
# we support only the instrument type provided by the Order DTO class
|
|
325
327
|
asset_class = instrument.get_security_ancestor().instrument_type.key.upper()
|
|
@@ -344,7 +346,7 @@ class OrderProposal(CloneMixin, RiskCheckMixin, WBModel):
|
|
|
344
346
|
order.execution_confirmed = False
|
|
345
347
|
order.execution_comment = "Underlying instrument does not have a valid identifier."
|
|
346
348
|
order.save()
|
|
347
|
-
except AttributeError:
|
|
349
|
+
except (AttributeError, KeyError):
|
|
348
350
|
order.execution_confirmed = False
|
|
349
351
|
order.execution_comment = f"Unsupported asset class {asset_class.title()}."
|
|
350
352
|
order.save()
|
wbportfolio/models/portfolio.py
CHANGED
|
@@ -669,7 +669,7 @@ class Portfolio(DeleteToDisableMixin, WBModel):
|
|
|
669
669
|
def fix_quantization(self, val_date: date):
|
|
670
670
|
assets = self.assets.filter(date=val_date)
|
|
671
671
|
total_weighting = assets.aggregate(s=Sum("weighting"))["s"]
|
|
672
|
-
if quantization_error := Decimal("1") - total_weighting:
|
|
672
|
+
if total_weighting and (quantization_error := Decimal("1") - total_weighting):
|
|
673
673
|
cash = self.cash_component
|
|
674
674
|
try:
|
|
675
675
|
cash_pos = assets.get(underlying_quote=cash)
|
|
@@ -31,16 +31,15 @@ class TestImportMixinModel:
|
|
|
31
31
|
|
|
32
32
|
trade = trade_factory.build()
|
|
33
33
|
data = {"data": [serialize(trade)]}
|
|
34
|
-
handler = TradeImportHandler(import_source)
|
|
35
34
|
|
|
36
35
|
# Import non existing data
|
|
37
|
-
|
|
36
|
+
TradeImportHandler(import_source).process(data)
|
|
38
37
|
assert Trade.objects.count() == 1
|
|
39
38
|
|
|
40
39
|
# Import already existing data
|
|
41
40
|
# import_source.data['data'][0]['shares'] *= 2
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
TradeImportHandler(import_source).process(data)
|
|
44
43
|
assert Trade.objects.count() == 1
|
|
45
44
|
|
|
46
45
|
def test_import_price(self, import_source, product, instrument_price_factory, instrument):
|
|
@@ -87,14 +87,13 @@ class OrderProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserP
|
|
|
87
87
|
|
|
88
88
|
def add_messages(self, request, instance: OrderProposal | None = None, **kwargs):
|
|
89
89
|
if instance:
|
|
90
|
-
if instance.status == OrderProposal.Status.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
90
|
+
if instance.status == OrderProposal.Status.APPROVED and not instance.portfolio.is_manageable:
|
|
91
|
+
info(request, "This order proposal cannot be approved the portfolio is considered unmanaged.")
|
|
92
|
+
if instance.status == OrderProposal.Status.PENDING and instance.has_non_successful_checks:
|
|
93
|
+
warning(
|
|
94
|
+
request,
|
|
95
|
+
"This order proposal cannot be approved because there is unsuccessful pre-trade checks. Please rectify accordingly and resubmit a valid order proposal",
|
|
96
|
+
)
|
|
98
97
|
if (
|
|
99
98
|
instance.execution_status in [ExecutionStatus.IN_DRAFT, ExecutionStatus.COMPLETED]
|
|
100
99
|
and instance.orders.filter(execution_confirmed=False).exists()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
2
|
|
|
3
|
-
from django.contrib.messages import error,
|
|
3
|
+
from django.contrib.messages import error, info
|
|
4
4
|
from django.db.models import (
|
|
5
5
|
Case,
|
|
6
6
|
F,
|
|
@@ -184,7 +184,7 @@ class OrderOrderProposalModelViewSet(
|
|
|
184
184
|
]:
|
|
185
185
|
total_target_weight = self.orders.aggregate(c=Sum(F("target_weight")))["c"] or Decimal(0)
|
|
186
186
|
if round(total_target_weight, 8) != 1:
|
|
187
|
-
|
|
187
|
+
info(
|
|
188
188
|
request,
|
|
189
189
|
"The total target weight does not equal 1. To avoid automatic cash allocation, please adjust the order weights to sum up to 1. Otherwise, a cash component will be added when this order proposal is submitted.",
|
|
190
190
|
)
|
|
@@ -126,7 +126,7 @@ wbportfolio/import_export/handlers/fees.py,sha256=BOFHAvSTlvVLaxnm6KD_fcza1TlPc0
|
|
|
126
126
|
wbportfolio/import_export/handlers/orders.py,sha256=GU3_tIy-tAw9aU-ifsnmMZPBB9sqfkFC_S1d9VziTwg,3136
|
|
127
127
|
wbportfolio/import_export/handlers/portfolio_cash_flow.py,sha256=W7QPNqEvvsq0RS016EAFBp1ezvc6G9Rk-hviRZh8o6Y,2737
|
|
128
128
|
wbportfolio/import_export/handlers/register.py,sha256=sYyXkE8b1DPZ5monxylZn0kjxLVdNYYZR-p61dwEoDM,2271
|
|
129
|
-
wbportfolio/import_export/handlers/trade.py,sha256=
|
|
129
|
+
wbportfolio/import_export/handlers/trade.py,sha256=xHg_4LMuMFRN49vEguIBDujM7vZxuhnEYMj95ZJ3s0E,11175
|
|
130
130
|
wbportfolio/import_export/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
131
131
|
wbportfolio/import_export/parsers/default_mapping.py,sha256=KrO-X5CvQCeQoBYzFDxavoQGriyUSeI2QDx5ar_zo7A,1405
|
|
132
132
|
wbportfolio/import_export/parsers/jpmorgan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -179,7 +179,7 @@ wbportfolio/import_export/parsers/tellco/customer_trade.py,sha256=U04YVJHxQLWxpN
|
|
|
179
179
|
wbportfolio/import_export/parsers/tellco/equity.py,sha256=3mPqh-kridsNCy5K4sa43SRsZIzNnRCr_3qGYlm356Q,3353
|
|
180
180
|
wbportfolio/import_export/parsers/tellco/valuation.py,sha256=QM8fYYOESGocACSbJnqD-1TtSYxOXOkkT7oLY6XY10Y,1642
|
|
181
181
|
wbportfolio/import_export/parsers/ubs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
182
|
-
wbportfolio/import_export/parsers/ubs/customer_trade.py,sha256=
|
|
182
|
+
wbportfolio/import_export/parsers/ubs/customer_trade.py,sha256=PYXVZRBoI0B6NJGhHoTboBzfA-U16hrIDDdPu--RLNs,2089
|
|
183
183
|
wbportfolio/import_export/parsers/ubs/equity.py,sha256=a3Hv6kjE__VUSdkE2TJ4NBCyVvnt34bPO-uZPR7pcEM,3471
|
|
184
184
|
wbportfolio/import_export/parsers/ubs/historical_customer_trade.py,sha256=1IggU7Ogg8-gdwLqBtz-FZ6l-UKsjbtM-KomM8u6QpA,1746
|
|
185
185
|
wbportfolio/import_export/parsers/ubs/valuation.py,sha256=6P71APM4xGoAadCywO6DBz42xL1bClacf242pb25w_s,1597
|
|
@@ -279,7 +279,7 @@ wbportfolio/models/builder.py,sha256=txZE_gEDgkPgSxSF0AlvZiRyhRQixD4Ame0wEF6Fjoo
|
|
|
279
279
|
wbportfolio/models/custodians.py,sha256=owTiS2Vm5CRKzh9M_P9GOVg-s-ndQ9UvRmw3yZP7cw0,3815
|
|
280
280
|
wbportfolio/models/exceptions.py,sha256=3ix0tWUO-O6jpz8f07XIwycw2x3JFRoWzjwil8FVA2Q,52
|
|
281
281
|
wbportfolio/models/indexes.py,sha256=gvW4K9U9Bj8BmVCqFYdWiXvDWhjHINRON8XhNsZUiQY,639
|
|
282
|
-
wbportfolio/models/portfolio.py,sha256=
|
|
282
|
+
wbportfolio/models/portfolio.py,sha256=fuYvHx4D3GqHjO0cBqe09M-t-t6jUzuOMh2ikTbykMo,54640
|
|
283
283
|
wbportfolio/models/portfolio_cash_flow.py,sha256=uElG7IJUBY8qvtrXftOoskX6EA-dKgEG1JJdvHeWV7g,7336
|
|
284
284
|
wbportfolio/models/portfolio_cash_targets.py,sha256=WmgG-etPisZsh2yaFQpz7EkpvAudKBEzqPsO715w52U,1498
|
|
285
285
|
wbportfolio/models/portfolio_relationship.py,sha256=ZGECiPZiLdlk4uSamOrEfuzO0hduK6OMKJLUSnh5_kc,5190
|
|
@@ -299,7 +299,7 @@ wbportfolio/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
299
299
|
wbportfolio/models/mixins/instruments.py,sha256=SuMPquQ93D4pZMK-4hQbJtV58_NOyf3wVOctQq7LNXQ,7054
|
|
300
300
|
wbportfolio/models/mixins/liquidity_stress_test.py,sha256=iQVzT3QM7VtHnqfj9gT6KUIe4wC4MJXery-AXJHUYns,58820
|
|
301
301
|
wbportfolio/models/orders/__init__.py,sha256=EH9UacGR3npBMje5FGTeLOh1xqFBh9kc24WbGmBIA3g,69
|
|
302
|
-
wbportfolio/models/orders/order_proposals.py,sha256=
|
|
302
|
+
wbportfolio/models/orders/order_proposals.py,sha256=1ce97DXjzZ4MsUAgA_GlnofnztAf0Y6zR1etZs-LrFI,57120
|
|
303
303
|
wbportfolio/models/orders/orders.py,sha256=kiVmVeW2D1bAuT983nSeIug6uoZ78IZovuAgls-CO8w,10150
|
|
304
304
|
wbportfolio/models/orders/routing.py,sha256=OpeP-rfMm1UN7a3vbNxBEQxqtGfE57AeGWnbTZa1fRQ,2223
|
|
305
305
|
wbportfolio/models/reconciliations/__init__.py,sha256=MXH5fZIPGDRBgJkO6wVu_NLRs8fkP1im7G6d-h36lQY,127
|
|
@@ -405,7 +405,7 @@ wbportfolio/tests/models/test_assets.py,sha256=N8OP2ZTPdq0lGonFRB0q_ruwF8QYpOGLk
|
|
|
405
405
|
wbportfolio/tests/models/test_custodians.py,sha256=WnFA4R-deVL7DSKufGE0mcFUdU05otNfGvmG4psiIh4,340
|
|
406
406
|
wbportfolio/tests/models/test_customer_trades.py,sha256=50By281j-nJ43jwRWK_KxNQGv6SuimT9e_ejgu6b19o,4942
|
|
407
407
|
wbportfolio/tests/models/test_dividends.py,sha256=SVkIW3OqN4-9tq1HIvJm8_8rOyNTHbJKFiN075DeGGc,162
|
|
408
|
-
wbportfolio/tests/models/test_imports.py,sha256=
|
|
408
|
+
wbportfolio/tests/models/test_imports.py,sha256=GktsJAlFnNTwYVXxeWcYFx4-kSI3A-JvhmvFwJsh-sE,7857
|
|
409
409
|
wbportfolio/tests/models/test_instrument_mixins.py,sha256=avX7a7Ovdzm_2v8JpSmrzqPo52qmIEa33c074D5KFCU,2206
|
|
410
410
|
wbportfolio/tests/models/test_merge.py,sha256=sdsjiZsmR6vsUKwTa5kkvL6QTeAZqtd_EPWPI11FKGY,6497
|
|
411
411
|
wbportfolio/tests/models/test_portfolio_cash_flow.py,sha256=X8dsXexsb1b0lBiuGzu40ps_Az_1UmmKT0eo1vbXH94,5792
|
|
@@ -546,8 +546,8 @@ wbportfolio/viewsets/configs/titles/registers.py,sha256=-C-YeGBhGva48oER6EIQ8CxW
|
|
|
546
546
|
wbportfolio/viewsets/configs/titles/roles.py,sha256=9LoJa3jgenXJ5UWRlIErTzdbjpSWMKsyJZtv-eDRTK4,739
|
|
547
547
|
wbportfolio/viewsets/configs/titles/trades.py,sha256=29XCLxvY0Xe3a2tjCno3tN2rRXCr9RWpbWnzurJfnYI,1986
|
|
548
548
|
wbportfolio/viewsets/orders/__init__.py,sha256=N8v9jdEXryOzrLlc7ML3iBCO2lmNXph9_TWoQ7PTvi4,195
|
|
549
|
-
wbportfolio/viewsets/orders/order_proposals.py,sha256
|
|
550
|
-
wbportfolio/viewsets/orders/orders.py,sha256=
|
|
549
|
+
wbportfolio/viewsets/orders/order_proposals.py,sha256=IMiRh7kFqzmHjC34k-FjV21NSxkWVBi5NTEtUysN9qg,8302
|
|
550
|
+
wbportfolio/viewsets/orders/orders.py,sha256=bSJ8na5XO5HcmrDdLkD6gH1G6J90nLa3bOTe89IWoAI,10922
|
|
551
551
|
wbportfolio/viewsets/orders/configs/__init__.py,sha256=5MU57JXiKi32_PicHtiNr7YHmMN020FrlF5NFJf_Wds,94
|
|
552
552
|
wbportfolio/viewsets/orders/configs/buttons/__init__.py,sha256=EHzNmAfa0UQFITEF-wxj_s4wn3Y5DE3DCbEUmmvCTIs,106
|
|
553
553
|
wbportfolio/viewsets/orders/configs/buttons/order_proposals.py,sha256=1BPkIYv0-K2DDGa4Gua2_Pxsx7fNurTZ2tYNdL66On0,6495
|
|
@@ -565,7 +565,7 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
|
|
|
565
565
|
wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
|
|
566
566
|
wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
|
|
567
567
|
wbportfolio/viewsets/transactions/trades.py,sha256=xBgOGaJ8aEg-2RxEJ4FDaBs4SGwuLasun3nhpis0WQY,12363
|
|
568
|
-
wbportfolio-1.55.
|
|
569
|
-
wbportfolio-1.55.
|
|
570
|
-
wbportfolio-1.55.
|
|
571
|
-
wbportfolio-1.55.
|
|
568
|
+
wbportfolio-1.55.2.dist-info/METADATA,sha256=keGWSdz-FLt0PsvSwjvhtyKIfOxmS_OM53oSOX41_o8,751
|
|
569
|
+
wbportfolio-1.55.2.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
570
|
+
wbportfolio-1.55.2.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
571
|
+
wbportfolio-1.55.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|