wbportfolio 1.50.15__py2.py3-none-any.whl → 1.51.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/analysis/claims.py +6 -1
- wbportfolio/import_export/handlers/dividend.py +15 -5
- wbportfolio/import_export/handlers/fees.py +11 -4
- wbportfolio/import_export/handlers/trade.py +21 -3
- wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +3 -4
- wbportfolio/import_export/parsers/jpmorgan/fees.py +3 -4
- wbportfolio/import_export/parsers/jpmorgan/valuation.py +1 -3
- wbportfolio/import_export/parsers/leonteq/equity.py +1 -4
- wbportfolio/import_export/parsers/leonteq/fees.py +2 -4
- wbportfolio/import_export/parsers/leonteq/valuation.py +1 -4
- wbportfolio/import_export/parsers/natixis/customer_trade.py +11 -15
- wbportfolio/import_export/parsers/natixis/d1_customer_trade.py +9 -10
- wbportfolio/import_export/parsers/natixis/d1_equity.py +4 -12
- wbportfolio/import_export/parsers/natixis/d1_fees.py +3 -5
- wbportfolio/import_export/parsers/natixis/d1_trade.py +4 -13
- wbportfolio/import_export/parsers/natixis/d1_valuation.py +2 -5
- wbportfolio/import_export/parsers/natixis/dividend.py +5 -5
- wbportfolio/import_export/parsers/natixis/equity.py +4 -4
- wbportfolio/import_export/parsers/natixis/fees.py +3 -6
- wbportfolio/import_export/parsers/natixis/trade.py +4 -4
- wbportfolio/import_export/parsers/natixis/utils.py +5 -9
- wbportfolio/import_export/parsers/natixis/valuation.py +4 -5
- wbportfolio/import_export/parsers/sg_lux/fees.py +6 -6
- wbportfolio/import_export/parsers/sg_lux/perf_fees.py +3 -6
- wbportfolio/import_export/parsers/sg_lux/registers.py +6 -2
- wbportfolio/import_export/parsers/sg_lux/valuation.py +3 -4
- wbportfolio/import_export/parsers/societe_generale/customer_trade.py +13 -12
- wbportfolio/import_export/parsers/societe_generale/valuation.py +2 -3
- wbportfolio/import_export/parsers/tellco/customer_trade.py +4 -6
- wbportfolio/import_export/parsers/tellco/equity.py +2 -3
- wbportfolio/import_export/parsers/tellco/valuation.py +2 -4
- wbportfolio/import_export/parsers/ubs/api/fees.py +6 -9
- wbportfolio/import_export/parsers/ubs/customer_trade.py +14 -20
- wbportfolio/import_export/parsers/ubs/equity.py +3 -6
- wbportfolio/import_export/parsers/ubs/historical_customer_trade.py +19 -38
- wbportfolio/import_export/parsers/ubs/valuation.py +2 -3
- wbportfolio/import_export/parsers/vontobel/instrument.py +2 -2
- wbportfolio/import_export/parsers/vontobel/management_fees.py +3 -5
- wbportfolio/import_export/parsers/vontobel/performance_fees.py +2 -3
- wbportfolio/import_export/parsers/vontobel/trade.py +1 -3
- wbportfolio/import_export/parsers/vontobel/utils.py +0 -12
- wbportfolio/locale/de/LC_MESSAGES/django.po +197 -0
- wbportfolio/locale/fr/LC_MESSAGES/django.po +197 -0
- wbportfolio/models/portfolio.py +8 -0
- wbportfolio/models/transactions/trade_proposals.py +22 -15
- wbportfolio/models/transactions/trades.py +8 -2
- wbportfolio/serializers/transactions/trades.py +2 -0
- wbportfolio/viewsets/configs/display/trades.py +12 -0
- wbportfolio/viewsets/transactions/claim.py +1 -1
- wbportfolio/viewsets/transactions/trade_proposals.py +1 -1
- {wbportfolio-1.50.15.dist-info → wbportfolio-1.51.0.dist-info}/METADATA +1 -1
- {wbportfolio-1.50.15.dist-info → wbportfolio-1.51.0.dist-info}/RECORD +54 -52
- {wbportfolio-1.50.15.dist-info → wbportfolio-1.51.0.dist-info}/WHEEL +0 -0
- {wbportfolio-1.50.15.dist-info → wbportfolio-1.51.0.dist-info}/licenses/LICENSE +0 -0
wbportfolio/analysis/claims.py
CHANGED
|
@@ -228,7 +228,12 @@ class ConsolidatedTradeSummary:
|
|
|
228
228
|
),
|
|
229
229
|
fill_value=0,
|
|
230
230
|
)
|
|
231
|
-
|
|
231
|
+
df = df.reset_index().dropna()[["id", "date", "aum_sparkline"]]
|
|
232
|
+
return (
|
|
233
|
+
df.groupby("id")
|
|
234
|
+
.apply(lambda o: list(zip(o["date"].dt.strftime("%Y-%m-%d"), o["aum_sparkline"])))
|
|
235
|
+
.rename("aum_sparkline")
|
|
236
|
+
)
|
|
232
237
|
return pd.DataFrame()
|
|
233
238
|
|
|
234
239
|
def get_initial_investment_date_df(self) -> pd.DataFrame:
|
|
@@ -23,7 +23,7 @@ class DividendImportHandler(ImportExportHandler):
|
|
|
23
23
|
data["value_date"] = datetime.strptime(data["value_date"], "%Y-%m-%d").date()
|
|
24
24
|
from wbportfolio.models import Portfolio
|
|
25
25
|
|
|
26
|
-
data["portfolio"] = Portfolio.
|
|
26
|
+
data["portfolio"] = Portfolio._get_or_create_portfolio(self.instrument_handler, data.get("portfolio"))
|
|
27
27
|
instrument = self.instrument_handler.process_object(
|
|
28
28
|
data["underlying_instrument"], only_security=False, read_only=True
|
|
29
29
|
)[0]
|
|
@@ -61,12 +61,22 @@ class DividendImportHandler(ImportExportHandler):
|
|
|
61
61
|
return self.model.objects.create(**data, import_source=self.import_source)
|
|
62
62
|
|
|
63
63
|
def _get_history(self: models.Model, history: Dict[str, Any]) -> models.QuerySet:
|
|
64
|
+
from wbportfolio.models import Product
|
|
65
|
+
|
|
64
66
|
val_date = datetime.strptime(history["transaction_date"], "%Y-%m-%d")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
try:
|
|
68
|
+
product = Product.objects.get(**history.get("product", {}))
|
|
69
|
+
dividends = self.model.objects.filter(transaction_date__lte=val_date, portfolio=product.primary_portfolio)
|
|
70
|
+
if underlying_instrument_data := history.get("underlying_instrument"):
|
|
71
|
+
if isinstance(underlying_instrument_data, dict):
|
|
72
|
+
dividends = dividends.filter(
|
|
73
|
+
**{f"underlying_instrument__{k}": v for k, v in underlying_instrument_data.items()}
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
dividends = dividends.filter(underlying_instrument__id=underlying_instrument_data)
|
|
69
77
|
return dividends
|
|
78
|
+
except Product.DoesNotExist:
|
|
79
|
+
return self.models.objects.none()
|
|
70
80
|
|
|
71
81
|
def _post_processing_history(self: models.Model, history: models.QuerySet):
|
|
72
82
|
self.import_source.log += "===================="
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
3
|
from wbcore.contrib.currency.import_export.handlers import CurrencyImportHandler
|
|
4
|
+
from wbcore.contrib.io.exceptions import DeserializationError
|
|
4
5
|
from wbcore.contrib.io.imports import ImportExportHandler
|
|
5
6
|
from wbfdm.models.instruments import Cash
|
|
6
7
|
|
|
7
|
-
from wbportfolio.models.products import Product
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
class FeesImportHandler(ImportExportHandler):
|
|
11
10
|
MODEL_APP_LABEL: str = "wbportfolio.Fees"
|
|
@@ -22,9 +21,17 @@ class FeesImportHandler(ImportExportHandler):
|
|
|
22
21
|
if book_date_str := data.get("book_date", None):
|
|
23
22
|
data["book_date"] = datetime.strptime(book_date_str, "%Y-%m-%d").date()
|
|
24
23
|
|
|
25
|
-
from wbportfolio.models import Portfolio
|
|
24
|
+
from wbportfolio.models import Portfolio, Product
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
linked_product_data = data.pop("linked_product", None)
|
|
28
|
+
if isinstance(linked_product_data, dict):
|
|
29
|
+
data["linked_product"] = Product.objects.get(**linked_product_data)
|
|
30
|
+
else:
|
|
31
|
+
data["linked_product"] = Product.objects.get(id=linked_product_data)
|
|
32
|
+
except Product.DoesNotExist:
|
|
33
|
+
raise DeserializationError("There is no valid linked product for in this row.")
|
|
26
34
|
|
|
27
|
-
data["linked_product"] = Product.objects.get(id=data["linked_product"])
|
|
28
35
|
if "porfolio" in data:
|
|
29
36
|
data["portfolio"] = Portfolio.all_objects.get(id=data["portfolio"])
|
|
30
37
|
else:
|
|
@@ -35,7 +35,7 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
35
35
|
return super()._data_changed(_object, change_data, initial_data, **kwargs)
|
|
36
36
|
|
|
37
37
|
def _deserialize(self, data: Dict[str, Any]):
|
|
38
|
-
from wbportfolio.models
|
|
38
|
+
from wbportfolio.models import Product, TradeProposal
|
|
39
39
|
|
|
40
40
|
if underlying_instrument := data.get("underlying_instrument", None):
|
|
41
41
|
data["underlying_instrument"] = self.instrument_handler.process_object(
|
|
@@ -62,6 +62,7 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
62
62
|
data["portfolio"] = Portfolio._get_or_create_portfolio(
|
|
63
63
|
self.instrument_handler, data.get("portfolio", data["underlying_instrument"])
|
|
64
64
|
)
|
|
65
|
+
|
|
65
66
|
if currency_data := data.get("currency", None):
|
|
66
67
|
data["currency"] = self.currency_handler.process_object(currency_data, read_only=True)[0]
|
|
67
68
|
|
|
@@ -69,6 +70,17 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
69
70
|
data["register"] = self.register_handler.process_object(register_data)[0]
|
|
70
71
|
|
|
71
72
|
data["marked_for_deletion"] = data.get("marked_for_deletion", False)
|
|
73
|
+
if underlying_instrument := data.get("underlying_instrument"):
|
|
74
|
+
if nominal := data.pop("nominal", None):
|
|
75
|
+
try:
|
|
76
|
+
product = Product.objects.get(id=underlying_instrument.id)
|
|
77
|
+
data["shares"] = nominal / product.share_price
|
|
78
|
+
except Product.DoesNotExist:
|
|
79
|
+
raise DeserializationError(
|
|
80
|
+
"We cannot compute the number of shares from the nominal value as we cannot find the product share price."
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
raise DeserializationError("We couldn't find a valid underlying instrument this row.")
|
|
72
84
|
|
|
73
85
|
for field in self.model._meta.get_fields():
|
|
74
86
|
if not (value := data.get(field.name, None)) is None and isinstance(field, models.DecimalField):
|
|
@@ -165,8 +177,14 @@ class TradeImportHandler(ImportExportHandler):
|
|
|
165
177
|
trades = trades.filter(transaction_date__lte=transaction_date)
|
|
166
178
|
elif book_date := history.get("book_date"):
|
|
167
179
|
trades = trades.filter(book_date__lte=book_date)
|
|
168
|
-
if "underlying_instrument"
|
|
169
|
-
|
|
180
|
+
if underlying_instrument_data := history.get("underlying_instrument"):
|
|
181
|
+
if isinstance(underlying_instrument_data, dict):
|
|
182
|
+
trades = trades.filter(
|
|
183
|
+
**{f"underlying_instrument__{k}": v for k, v in underlying_instrument_data.items()}
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
trades = trades.filter(underlying_instrument__id=underlying_instrument_data)
|
|
187
|
+
|
|
170
188
|
elif "underlying_instruments" in history:
|
|
171
189
|
trades = trades.filter(underlying_instrument__id__in=history["underlying_instruments"])
|
|
172
190
|
else:
|
|
@@ -40,22 +40,21 @@ def parse(import_source):
|
|
|
40
40
|
# Iterate through the CSV File and parse the data into a list
|
|
41
41
|
data = list()
|
|
42
42
|
for nominal_data in df.to_dict("records"):
|
|
43
|
-
|
|
43
|
+
isin = nominal_data["ISIN"]
|
|
44
44
|
shares = convert_string_to_number(nominal_data["Quantity"])
|
|
45
45
|
|
|
46
46
|
# Check whether it is a buy or a sell and convert the value correspondely
|
|
47
47
|
shares = shares if nominal_data["Side"] == "S" else shares * -1
|
|
48
|
-
portfolio = product.primary_portfolio
|
|
49
48
|
bank = nominal_data["CounterParty Name"]
|
|
50
49
|
if (
|
|
51
50
|
len(bank_exclusion_list) == 0 or bank.strip().lower() not in bank_exclusion_list
|
|
52
51
|
): # we do basic string comparison to exclude appropriate banks. We might want to include regex if bank data is inconsistent
|
|
53
52
|
data.append(
|
|
54
53
|
{
|
|
55
|
-
"underlying_instrument": {"
|
|
54
|
+
"underlying_instrument": {"isin": isin, "instrument_type": "product"},
|
|
56
55
|
"transaction_date": nominal_data["Trade Date"].strftime("%Y-%m-%d"),
|
|
57
56
|
"shares": shares,
|
|
58
|
-
"portfolio":
|
|
57
|
+
"portfolio": {"isin": isin, "instrument_type": "product"},
|
|
59
58
|
# 'currency': product.currency.key,
|
|
60
59
|
"transaction_subtype": Trade.Type.REDEMPTION if shares < 0 else Trade.Type.SUBSCRIPTION,
|
|
61
60
|
"bank": bank,
|
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
8
8
|
from wbportfolio.import_export.utils import convert_string_to_number
|
|
9
|
-
from wbportfolio.models import Fees
|
|
9
|
+
from wbportfolio.models import Fees
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger("importers.parsers.jpmorgan.fee")
|
|
12
12
|
|
|
@@ -31,11 +31,10 @@ def parse(import_source):
|
|
|
31
31
|
data = list()
|
|
32
32
|
|
|
33
33
|
for fee_data in df.to_dict("records"):
|
|
34
|
-
|
|
34
|
+
isin = fee_data["ISIN"]
|
|
35
35
|
fee_date = fee_data["Date"]
|
|
36
36
|
base_data = {
|
|
37
|
-
"
|
|
38
|
-
"linked_product": product.id,
|
|
37
|
+
"linked_product": {"isin": isin},
|
|
39
38
|
"transaction_date": fee_date.strftime("%Y-%m-%d"),
|
|
40
39
|
"calculated": False,
|
|
41
40
|
}
|
|
@@ -6,7 +6,6 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
8
8
|
from wbportfolio.import_export.utils import convert_string_to_number
|
|
9
|
-
from wbportfolio.models import Product
|
|
10
9
|
|
|
11
10
|
logger = logging.getLogger("importers.parsers.jpmorgan.index")
|
|
12
11
|
|
|
@@ -27,12 +26,11 @@ def parse(import_source):
|
|
|
27
26
|
# Iterate through the CSV File and parse the data into a list
|
|
28
27
|
data = list()
|
|
29
28
|
for price_data in df.to_dict("records"):
|
|
30
|
-
product = Product.objects.get(isin=price_data["ISIN"])
|
|
31
29
|
date = datetime.datetime.strptime(price_data["Date"], "%d-%b-%y")
|
|
32
30
|
|
|
33
31
|
data.append(
|
|
34
32
|
{
|
|
35
|
-
"instrument": {"instrument_type": "product", "
|
|
33
|
+
"instrument": {"instrument_type": "product", "isin": price_data["ISIN"]},
|
|
36
34
|
"date": date.strftime("%Y-%m-%d"),
|
|
37
35
|
"net_value": round(convert_string_to_number(price_data["Indicative_MID"]), 4),
|
|
38
36
|
"calculated": False,
|
|
@@ -5,8 +5,6 @@ from io import BytesIO
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
8
|
-
from wbportfolio.models import Product
|
|
9
|
-
|
|
10
8
|
FIELD_MAP = {
|
|
11
9
|
"CCY": "currency__key",
|
|
12
10
|
"CURRENT PRICE": "initial_price",
|
|
@@ -45,7 +43,6 @@ def parse(import_source):
|
|
|
45
43
|
df.initial_currency_fx_rate = df.initial_currency_fx_rate.fillna(1.0)
|
|
46
44
|
# df["weighting"] = df.initial_currency_fx_rate * df.initial_price * df.initial_shares
|
|
47
45
|
# df["weighting"] = df.weighting / df.weighting.sum()
|
|
48
|
-
product = Product.objects.get(isin=sheet_name)
|
|
49
46
|
for position in df.to_dict("records"):
|
|
50
47
|
if position["underlying_quote__instrument_type"] == "CASH":
|
|
51
48
|
ticker = exchange = "CASH"
|
|
@@ -75,7 +72,7 @@ def parse(import_source):
|
|
|
75
72
|
"initial_shares": position["initial_shares"],
|
|
76
73
|
"weighting": position["weighting"],
|
|
77
74
|
"exchange": {"bbg_exchange_codes": exchange},
|
|
78
|
-
"portfolio": {"instrument_type": "product", "
|
|
75
|
+
"portfolio": {"instrument_type": "product", "isin": sheet_name},
|
|
79
76
|
}
|
|
80
77
|
)
|
|
81
78
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
-
from wbportfolio.models import Fees
|
|
3
|
+
from wbportfolio.models import Fees
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def parse(import_source):
|
|
@@ -17,7 +17,6 @@ def parse(import_source):
|
|
|
17
17
|
for isin in df.columns.unique(level=0):
|
|
18
18
|
product_fees_df = df.loc[:, isin]
|
|
19
19
|
|
|
20
|
-
product = Product.objects.get(isin=isin)
|
|
21
20
|
product_fees_df = product_fees_df.rename(
|
|
22
21
|
columns={
|
|
23
22
|
"TRADE DATE": "transaction_date",
|
|
@@ -51,8 +50,7 @@ def parse(import_source):
|
|
|
51
50
|
product_fees_df = product_fees_df.where(pd.notnull(product_fees_df), None)
|
|
52
51
|
for fees in product_fees_df.to_dict("records"):
|
|
53
52
|
base_data = {
|
|
54
|
-
"
|
|
55
|
-
"linked_product": product.id,
|
|
53
|
+
"linked_product": {"isin": isin},
|
|
56
54
|
"transaction_date": fees["transaction_date"].strftime("%Y-%m-%d"),
|
|
57
55
|
"calculated": False,
|
|
58
56
|
}
|
|
@@ -3,8 +3,6 @@ from io import BytesIO
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pandas as pd
|
|
5
5
|
|
|
6
|
-
from wbportfolio.models import Product
|
|
7
|
-
|
|
8
6
|
|
|
9
7
|
def parse(import_source):
|
|
10
8
|
data = list()
|
|
@@ -26,11 +24,10 @@ def parse(import_source):
|
|
|
26
24
|
for date, valuations in df.to_dict("index").items():
|
|
27
25
|
date = date.to_pydatetime()
|
|
28
26
|
for isin, value in valuations.items():
|
|
29
|
-
product = Product.objects.get(isin=isin)
|
|
30
27
|
if date.weekday() not in [5, 6]:
|
|
31
28
|
data.append(
|
|
32
29
|
{
|
|
33
|
-
"instrument": {"instrument_type": "product", "
|
|
30
|
+
"instrument": {"instrument_type": "product", "isin": isin},
|
|
34
31
|
"date": date.strftime("%Y-%m-%d"),
|
|
35
32
|
"net_value": value,
|
|
36
33
|
"calculated": False,
|
|
@@ -3,7 +3,7 @@ import pandas as pd
|
|
|
3
3
|
|
|
4
4
|
from wbportfolio.models import Trade
|
|
5
5
|
|
|
6
|
-
from .utils import
|
|
6
|
+
from .utils import file_name_parse_isin
|
|
7
7
|
|
|
8
8
|
UNVALID_CUSTODIANS = ["init"]
|
|
9
9
|
|
|
@@ -13,51 +13,47 @@ FIELD_MAP = {
|
|
|
13
13
|
"Counterparty": "bank",
|
|
14
14
|
"Identifier": "external_id",
|
|
15
15
|
"Note price in%": "price",
|
|
16
|
-
"Nominal Increase/Decrease": "
|
|
16
|
+
"Nominal Increase/Decrease": "nominal",
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def _check_if_count_towards_total_aum(row, df):
|
|
21
|
-
previous_accumulated_shares = row["Accumulated Nominal"] - row["
|
|
21
|
+
previous_accumulated_shares = row["Accumulated Nominal"] - row["nominal"]
|
|
22
22
|
dff = df[
|
|
23
23
|
(df["transaction_date"] < row["transaction_date"]) & (df["Accumulated Nominal"] == previous_accumulated_shares)
|
|
24
24
|
]
|
|
25
|
-
return not dff["
|
|
25
|
+
return not dff["nominal"].sum() == row["nominal"]
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def parse(import_source):
|
|
29
29
|
# Load file into a CSV DictReader
|
|
30
30
|
df = pd.read_csv(import_source.file, encoding="utf-8", delimiter=";")
|
|
31
31
|
df = df.rename(columns=FIELD_MAP)
|
|
32
|
-
parts =
|
|
32
|
+
parts = file_name_parse_isin(import_source.file.name)
|
|
33
33
|
|
|
34
34
|
# Get the valuation date and product from the parts list
|
|
35
35
|
valuation_date = parts["valuation_date"]
|
|
36
|
-
|
|
36
|
+
product_data = parts["product"]
|
|
37
37
|
|
|
38
38
|
df["transaction_date"] = pd.to_datetime(df["transaction_date"], format="%d/%m/%Y").dt.strftime("%Y-%m-%d")
|
|
39
39
|
df["value_date"] = pd.to_datetime(df["value_date"], format="%d/%m/%Y").dt.strftime("%Y-%m-%d")
|
|
40
40
|
df["bank"] = df["bank"].str.strip()
|
|
41
|
-
df["shares"] = df["shares"] / product.share_price
|
|
42
41
|
|
|
43
42
|
df["bank"] = df["bank"].fillna("<not specified>")
|
|
44
43
|
|
|
45
44
|
# Use the accumulated nominal (outstanding shares) to detect internal natixis accounting trade that shouldn't be imported
|
|
46
45
|
# If a trade is marked "INIT" and the previous trade sum equals to that trade shares, we assume these two groups are double accounted and we don't import the INIT trade
|
|
47
|
-
df["Accumulated Nominal"] = df["Accumulated Nominal"] / product.share_price
|
|
48
46
|
df["count_towards_total_aum"] = df.apply(_check_if_count_towards_total_aum, args=[df], axis=1)
|
|
49
47
|
init_rows = df[~df["count_towards_total_aum"] & df["bank"].str.lower().isin(UNVALID_CUSTODIANS)].index
|
|
50
48
|
df = df.drop(init_rows)
|
|
51
49
|
|
|
52
50
|
df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
|
|
53
51
|
|
|
54
|
-
df["portfolio"] = product.
|
|
55
|
-
df["
|
|
56
|
-
df["
|
|
57
|
-
df.loc[df["
|
|
58
|
-
df.loc[df["shares"] >= 0, "transaction_subtype"] = Trade.Type.SUBSCRIPTION
|
|
59
|
-
|
|
52
|
+
df["portfolio"] = [{"instrument_type": "product", **product_data}] * df.shape[0]
|
|
53
|
+
df["underlying_instrument"] = df["portfolio"]
|
|
54
|
+
df.loc[df["nominal"] < 0, "transaction_subtype"] = Trade.Type.REDEMPTION
|
|
55
|
+
df.loc[df["nominal"] >= 0, "transaction_subtype"] = Trade.Type.SUBSCRIPTION
|
|
60
56
|
return {
|
|
61
57
|
"data": df.replace([np.inf, -np.inf, np.nan], None).to_dict(orient="records"),
|
|
62
|
-
"history": {"underlying_instrument":
|
|
58
|
+
"history": {"underlying_instrument": product_data, "transaction_date": valuation_date.strftime("%Y-%m-%d")},
|
|
63
59
|
}
|
|
@@ -2,7 +2,7 @@ import datetime
|
|
|
2
2
|
|
|
3
3
|
import xlrd
|
|
4
4
|
|
|
5
|
-
from wbportfolio.models import
|
|
5
|
+
from wbportfolio.models import Trade
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def get_isin(sheet):
|
|
@@ -16,8 +16,7 @@ def parse(import_source):
|
|
|
16
16
|
|
|
17
17
|
product_sheet = book.sheet_by_name("Certificate")
|
|
18
18
|
valuation_sheet = book.sheet_by_name("Report")
|
|
19
|
-
|
|
20
|
-
product = Product.objects.get(isin=get_isin(product_sheet))
|
|
19
|
+
isin = get_isin(product_sheet)
|
|
21
20
|
|
|
22
21
|
date_column = None
|
|
23
22
|
net_value_column = None
|
|
@@ -41,20 +40,20 @@ def parse(import_source):
|
|
|
41
40
|
max_date = max(max_date, transaction_date)
|
|
42
41
|
net_value = row[net_value_column].value
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
nominal_today = row[nominal_column].value
|
|
45
44
|
|
|
46
45
|
try:
|
|
47
|
-
|
|
48
|
-
trade =
|
|
46
|
+
nominal_yesterday = valuation_sheet.cell_value(i + 1, nominal_column)
|
|
47
|
+
trade = nominal_today - nominal_yesterday
|
|
49
48
|
except IndexError:
|
|
50
|
-
trade =
|
|
49
|
+
trade = nominal_today
|
|
51
50
|
|
|
52
51
|
if trade and trade != 0:
|
|
53
52
|
data.append(
|
|
54
53
|
{
|
|
55
|
-
"underlying_instrument": {"
|
|
54
|
+
"underlying_instrument": {"isin": "isin", "instrument_type": "product"},
|
|
56
55
|
"transaction_date": transaction_date.strftime("%Y-%m-%d"),
|
|
57
|
-
"
|
|
56
|
+
"nominal": trade,
|
|
58
57
|
"transaction_subtype": Trade.Type.REDEMPTION if trade < 0 else Trade.Type.SUBSCRIPTION,
|
|
59
58
|
"bank": "Natixis Internal Trade",
|
|
60
59
|
"price": net_value,
|
|
@@ -63,5 +62,5 @@ def parse(import_source):
|
|
|
63
62
|
|
|
64
63
|
return {
|
|
65
64
|
"data": data,
|
|
66
|
-
"history": {"underlying_instrument":
|
|
65
|
+
"history": {"underlying_instrument": {"isin": isin}, "transaction_date": max_date.strftime("%Y-%m-%d")},
|
|
67
66
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import xlrd
|
|
2
2
|
|
|
3
|
-
from wbportfolio.models import Product
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
def get_isin(sheet):
|
|
7
5
|
for row in sheet.get_rows():
|
|
@@ -11,8 +9,7 @@ def get_isin(sheet):
|
|
|
11
9
|
|
|
12
10
|
def parse(import_source):
|
|
13
11
|
book = xlrd.open_workbook(file_contents=import_source.file.read())
|
|
14
|
-
|
|
15
|
-
product = Product.objects.get(isin=get_isin(book.sheet_by_name("Certificate")))
|
|
12
|
+
isin = get_isin(book.sheet_by_name("Certificate"))
|
|
16
13
|
equity_sheet = book.sheet_by_name("Report - details")
|
|
17
14
|
|
|
18
15
|
# date_column = None
|
|
@@ -42,18 +39,16 @@ def parse(import_source):
|
|
|
42
39
|
initial_currency_fx_rate = equity_sheet.cell_value(_i, 6)
|
|
43
40
|
initial_price = equity_sheet.cell_value(_i, 10)
|
|
44
41
|
|
|
45
|
-
ticker = Product.objects.get(isin=isin).ticker
|
|
46
|
-
|
|
47
42
|
data.append(
|
|
48
43
|
{
|
|
49
|
-
"underlying_quote": {"instrument_type": "product", "
|
|
44
|
+
"underlying_quote": {"instrument_type": "product", "isin": isin},
|
|
50
45
|
"currency__key": currency_key,
|
|
51
46
|
"date": valuation_date.strftime("%Y-%m-%d"),
|
|
52
47
|
"asset_valuation_date": valuation_date.strftime("%Y-%m-%d"),
|
|
53
48
|
"initial_price": initial_price,
|
|
54
49
|
"initial_currency_fx_rate": initial_currency_fx_rate,
|
|
55
50
|
"initial_shares": initial_shares,
|
|
56
|
-
"portfolio": {"
|
|
51
|
+
"portfolio": {"isin": isin, "instrument_type": "product"},
|
|
57
52
|
}
|
|
58
53
|
)
|
|
59
54
|
|
|
@@ -62,16 +57,13 @@ def parse(import_source):
|
|
|
62
57
|
"underlying_quote": {
|
|
63
58
|
"instrument_type": "cash",
|
|
64
59
|
"ticker": "CASH",
|
|
65
|
-
"name": f"1 {product.currency.key} AMC",
|
|
66
|
-
"currency__key": product.currency.key,
|
|
67
60
|
},
|
|
68
61
|
"date": valuation_date.strftime("%Y-%m-%d"),
|
|
69
62
|
"asset_valuation_date": valuation_date.strftime("%Y-%m-%d"),
|
|
70
63
|
"initial_price": 1.0,
|
|
71
64
|
"initial_currency_fx_rate": 1.0,
|
|
72
|
-
"currency__key": product.currency.key,
|
|
73
65
|
"initial_shares": equity_sheet.cell_value(first_row + 1, 6),
|
|
74
|
-
"portfolio": {"
|
|
66
|
+
"portfolio": {"isin": isin, "instrument_type": "product"},
|
|
75
67
|
}
|
|
76
68
|
)
|
|
77
69
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import xlrd
|
|
2
2
|
|
|
3
|
-
from wbportfolio.models import Fees
|
|
3
|
+
from wbportfolio.models import Fees
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def get_isin(sheet):
|
|
@@ -11,8 +11,7 @@ def get_isin(sheet):
|
|
|
11
11
|
|
|
12
12
|
def parse(import_source):
|
|
13
13
|
book = xlrd.open_workbook(file_contents=import_source.file.read())
|
|
14
|
-
|
|
15
|
-
product = Product.objects.get(isin=get_isin(book.sheet_by_name("Certificate")))
|
|
14
|
+
isin = get_isin(book.sheet_by_name("Certificate"))
|
|
16
15
|
valuation_sheet = book.sheet_by_name("Report")
|
|
17
16
|
|
|
18
17
|
date_column = None
|
|
@@ -38,8 +37,7 @@ def parse(import_source):
|
|
|
38
37
|
management_fees = row[management_fees_column].value
|
|
39
38
|
|
|
40
39
|
base_data = {
|
|
41
|
-
"
|
|
42
|
-
"linked_product": product.id,
|
|
40
|
+
"linked_product": {"isin": isin},
|
|
43
41
|
"transaction_date": valuation_date.strftime("%Y-%m-%d"),
|
|
44
42
|
"calculated": False,
|
|
45
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import xlrd
|
|
2
2
|
|
|
3
|
-
from wbportfolio.models import
|
|
3
|
+
from wbportfolio.models import Trade
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def get_isin(sheet):
|
|
@@ -11,8 +11,7 @@ def get_isin(sheet):
|
|
|
11
11
|
|
|
12
12
|
def parse(import_source):
|
|
13
13
|
book = xlrd.open_workbook(file_contents=import_source.file.read())
|
|
14
|
-
|
|
15
|
-
product = Product.objects.get(isin=get_isin(book.sheet_by_name("Certificate")))
|
|
14
|
+
isin = get_isin(book.sheet_by_name("Certificate"))
|
|
16
15
|
valuation_sheet = book.sheet_by_name("Transaction Fees")
|
|
17
16
|
|
|
18
17
|
trade_date_column = None
|
|
@@ -20,7 +19,6 @@ def parse(import_source):
|
|
|
20
19
|
price_column = None
|
|
21
20
|
currency_fx_rate_column = None
|
|
22
21
|
isin_column = None
|
|
23
|
-
execution_fees_column = None
|
|
24
22
|
|
|
25
23
|
data = list()
|
|
26
24
|
for i, row in enumerate(valuation_sheet.get_rows()):
|
|
@@ -41,28 +39,21 @@ def parse(import_source):
|
|
|
41
39
|
if "Reference" == row[column].value:
|
|
42
40
|
isin_column = column
|
|
43
41
|
|
|
44
|
-
if "Exec Fees" in row[column].value:
|
|
45
|
-
execution_fees_column = column
|
|
46
|
-
|
|
47
42
|
else:
|
|
48
43
|
transaction_date = xlrd.xldate.xldate_as_datetime(row[trade_date_column].value, 0)
|
|
49
44
|
shares = row[shares_column].value
|
|
50
45
|
price = row[price_column].value
|
|
51
46
|
currency_fx_rate = row[currency_fx_rate_column].value
|
|
52
|
-
row[execution_fees_column].value
|
|
53
|
-
|
|
54
|
-
traded_product = Product.objects.get(isin=row[isin_column].value)
|
|
55
47
|
|
|
56
48
|
data.append(
|
|
57
49
|
{
|
|
58
|
-
"portfolio": product
|
|
50
|
+
"portfolio": {"instrument_type": "product", "isin": isin},
|
|
59
51
|
"transaction_subtype": Trade.Type.REBALANCE,
|
|
60
52
|
"transaction_date": transaction_date.strftime("%Y-%m-%d"),
|
|
61
53
|
"shares": shares,
|
|
62
54
|
"price": price,
|
|
63
|
-
"currency__key": traded_product.currency.key,
|
|
64
55
|
"currency_fx_rate": currency_fx_rate,
|
|
65
|
-
"underlying_instrument": {"
|
|
56
|
+
"underlying_instrument": {"isin": row[isin_column], "instrument_type": "product"},
|
|
66
57
|
# 'execution_fees_percent': 0,
|
|
67
58
|
# 'market_fees_percent': 0,
|
|
68
59
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import xlrd
|
|
2
2
|
|
|
3
|
-
from wbportfolio.models import Product
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
def get_isin(sheet):
|
|
7
5
|
for row in sheet.get_rows():
|
|
@@ -11,8 +9,7 @@ def get_isin(sheet):
|
|
|
11
9
|
|
|
12
10
|
def parse(import_source):
|
|
13
11
|
book = xlrd.open_workbook(file_contents=import_source.file.read())
|
|
14
|
-
|
|
15
|
-
product = Product.objects.get(isin=get_isin(book.sheet_by_name("Certificate")))
|
|
12
|
+
isin = get_isin(book.sheet_by_name("Certificate"))
|
|
16
13
|
valuation_sheet = book.sheet_by_name("Report")
|
|
17
14
|
|
|
18
15
|
date_column = None
|
|
@@ -32,7 +29,7 @@ def parse(import_source):
|
|
|
32
29
|
|
|
33
30
|
data.append(
|
|
34
31
|
{
|
|
35
|
-
"instrument": {"instrument_type": "product", "
|
|
32
|
+
"instrument": {"instrument_type": "product", "isin": isin},
|
|
36
33
|
"date": valuation_date.strftime("%Y-%m-%d"),
|
|
37
34
|
"net_value": net_value,
|
|
38
35
|
"calculated": False,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import pandas as pd
|
|
3
3
|
|
|
4
|
-
from .utils import _get_exchange_from_ticker, _get_ticker,
|
|
4
|
+
from .utils import _get_exchange_from_ticker, _get_ticker, file_name_parse_isin
|
|
5
5
|
|
|
6
6
|
COLUMN_MAP = {
|
|
7
7
|
"Ticker": "underlying_instrument__ticker",
|
|
@@ -19,8 +19,8 @@ COLUMN_MAP = {
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def parse(import_source):
|
|
22
|
-
parts =
|
|
23
|
-
|
|
22
|
+
parts = file_name_parse_isin(import_source.file.name)
|
|
23
|
+
product_data = parts["product"]
|
|
24
24
|
|
|
25
25
|
df = pd.read_csv(import_source.file, sep=";", skipinitialspace=True)
|
|
26
26
|
df = df.rename(columns=COLUMN_MAP).astype("str")
|
|
@@ -42,12 +42,12 @@ def parse(import_source):
|
|
|
42
42
|
df["price"] = df["total_value"] / df["shares"] / df["retrocession"]
|
|
43
43
|
df["total_value_gross"] = df["price_gross"] * df["shares"] * df["retrocession"]
|
|
44
44
|
df["total_value_gross_fx_portfolio"] = df["total_value_gross"] * df["currency_fx_rate"]
|
|
45
|
-
df["portfolio"] = product.
|
|
45
|
+
df["portfolio"] = [{"instrument_type": "product", **product_data}] * df.shape[0]
|
|
46
46
|
df = df.replace([np.inf, -np.inf, np.nan], None)
|
|
47
47
|
return {
|
|
48
48
|
"data": df.to_dict("records"),
|
|
49
49
|
"history": {
|
|
50
50
|
"transaction_date": parts["valuation_date"].strftime("%Y-%m-%d"),
|
|
51
|
-
"
|
|
51
|
+
"product": product_data,
|
|
52
52
|
},
|
|
53
53
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import pandas as pd
|
|
3
3
|
|
|
4
|
-
from .utils import _get_underlying_instrument,
|
|
4
|
+
from .utils import _get_underlying_instrument, file_name_parse_isin
|
|
5
5
|
|
|
6
6
|
FIELD_MAP = {
|
|
7
7
|
"Close": "initial_price",
|
|
@@ -28,11 +28,11 @@ def _apply_adjusting_factor(row):
|
|
|
28
28
|
|
|
29
29
|
def parse(import_source):
|
|
30
30
|
# Parse the Parts of the filename into the different parts
|
|
31
|
-
parts =
|
|
31
|
+
parts = file_name_parse_isin(import_source.file.name)
|
|
32
32
|
|
|
33
33
|
# Get the valuation date and investment from the parts list
|
|
34
34
|
valuation_date = parts["valuation_date"]
|
|
35
|
-
|
|
35
|
+
product_data = parts["product"]
|
|
36
36
|
|
|
37
37
|
# Load file into a CSV DictReader
|
|
38
38
|
df = pd.read_csv(import_source.file, encoding="utf-16", delimiter=";")
|
|
@@ -50,7 +50,7 @@ def parse(import_source):
|
|
|
50
50
|
df = df.drop(columns=df.columns.difference(FIELD_MAP.values()))
|
|
51
51
|
|
|
52
52
|
df["portfolio__instrument_type"] = "product"
|
|
53
|
-
df["
|
|
53
|
+
df["portfolio__isin"] = product_data["isin"]
|
|
54
54
|
df["is_estimated"] = False
|
|
55
55
|
df["date"] = valuation_date.strftime("%Y-%m-%d")
|
|
56
56
|
df["asset_valuation_date"] = pd.to_datetime(df["asset_valuation_date"], dayfirst=True).dt.strftime("%Y-%m-%d")
|