wbportfolio 1.50.14__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.

Files changed (54) hide show
  1. wbportfolio/analysis/claims.py +6 -1
  2. wbportfolio/import_export/handlers/dividend.py +15 -5
  3. wbportfolio/import_export/handlers/fees.py +11 -4
  4. wbportfolio/import_export/handlers/trade.py +21 -3
  5. wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +3 -4
  6. wbportfolio/import_export/parsers/jpmorgan/fees.py +3 -4
  7. wbportfolio/import_export/parsers/jpmorgan/valuation.py +1 -3
  8. wbportfolio/import_export/parsers/leonteq/equity.py +1 -4
  9. wbportfolio/import_export/parsers/leonteq/fees.py +2 -4
  10. wbportfolio/import_export/parsers/leonteq/valuation.py +1 -4
  11. wbportfolio/import_export/parsers/natixis/customer_trade.py +11 -15
  12. wbportfolio/import_export/parsers/natixis/d1_customer_trade.py +9 -10
  13. wbportfolio/import_export/parsers/natixis/d1_equity.py +4 -12
  14. wbportfolio/import_export/parsers/natixis/d1_fees.py +3 -5
  15. wbportfolio/import_export/parsers/natixis/d1_trade.py +4 -13
  16. wbportfolio/import_export/parsers/natixis/d1_valuation.py +2 -5
  17. wbportfolio/import_export/parsers/natixis/dividend.py +5 -5
  18. wbportfolio/import_export/parsers/natixis/equity.py +4 -4
  19. wbportfolio/import_export/parsers/natixis/fees.py +3 -6
  20. wbportfolio/import_export/parsers/natixis/trade.py +4 -4
  21. wbportfolio/import_export/parsers/natixis/utils.py +5 -9
  22. wbportfolio/import_export/parsers/natixis/valuation.py +4 -5
  23. wbportfolio/import_export/parsers/sg_lux/fees.py +6 -6
  24. wbportfolio/import_export/parsers/sg_lux/perf_fees.py +3 -6
  25. wbportfolio/import_export/parsers/sg_lux/registers.py +6 -2
  26. wbportfolio/import_export/parsers/sg_lux/valuation.py +3 -4
  27. wbportfolio/import_export/parsers/societe_generale/customer_trade.py +13 -12
  28. wbportfolio/import_export/parsers/societe_generale/valuation.py +2 -3
  29. wbportfolio/import_export/parsers/tellco/customer_trade.py +4 -6
  30. wbportfolio/import_export/parsers/tellco/equity.py +2 -3
  31. wbportfolio/import_export/parsers/tellco/valuation.py +2 -4
  32. wbportfolio/import_export/parsers/ubs/api/fees.py +6 -9
  33. wbportfolio/import_export/parsers/ubs/customer_trade.py +14 -20
  34. wbportfolio/import_export/parsers/ubs/equity.py +3 -6
  35. wbportfolio/import_export/parsers/ubs/historical_customer_trade.py +19 -38
  36. wbportfolio/import_export/parsers/ubs/valuation.py +2 -3
  37. wbportfolio/import_export/parsers/vontobel/instrument.py +2 -2
  38. wbportfolio/import_export/parsers/vontobel/management_fees.py +3 -5
  39. wbportfolio/import_export/parsers/vontobel/performance_fees.py +2 -3
  40. wbportfolio/import_export/parsers/vontobel/trade.py +1 -3
  41. wbportfolio/import_export/parsers/vontobel/utils.py +0 -12
  42. wbportfolio/locale/de/LC_MESSAGES/django.po +197 -0
  43. wbportfolio/locale/fr/LC_MESSAGES/django.po +197 -0
  44. wbportfolio/models/portfolio.py +8 -0
  45. wbportfolio/models/transactions/trade_proposals.py +22 -15
  46. wbportfolio/models/transactions/trades.py +8 -2
  47. wbportfolio/serializers/transactions/trades.py +2 -0
  48. wbportfolio/viewsets/configs/display/trades.py +12 -0
  49. wbportfolio/viewsets/transactions/claim.py +1 -1
  50. wbportfolio/viewsets/transactions/trade_proposals.py +1 -1
  51. {wbportfolio-1.50.14.dist-info → wbportfolio-1.51.0.dist-info}/METADATA +1 -1
  52. {wbportfolio-1.50.14.dist-info → wbportfolio-1.51.0.dist-info}/RECORD +54 -52
  53. {wbportfolio-1.50.14.dist-info → wbportfolio-1.51.0.dist-info}/WHEEL +0 -0
  54. {wbportfolio-1.50.14.dist-info → wbportfolio-1.51.0.dist-info}/licenses/LICENSE +0 -0
@@ -228,7 +228,12 @@ class ConsolidatedTradeSummary:
228
228
  ),
229
229
  fill_value=0,
230
230
  )
231
- return df.reset_index().dropna()[["id", "aum_sparkline"]].groupby("id").agg(list)
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.all_objects.get(id=data["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
- if portfolio_id := history.get("portfolio", None):
66
- dividends = self.model.objects.filter(transaction_date__lte=val_date, portfolio=portfolio_id)
67
- if underlying_instrument_id := history.get("underlying_instrument", None):
68
- dividends = dividends.filter(underlying_instrument=underlying_instrument_id)
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.transactions.trade_proposals import TradeProposal
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" in history:
169
- trades = trades.filter(underlying_instrument__id=history["underlying_instrument"])
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
- product = Product.objects.get(isin=nominal_data["ISIN"])
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": {"id": product.id, "instrument_type": "product"},
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": portfolio.id,
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, Product
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
- product = Product.objects.get(isin=fee_data["ISIN"])
34
+ isin = fee_data["ISIN"]
35
35
  fee_date = fee_data["Date"]
36
36
  base_data = {
37
- "portfolio": product.primary_portfolio.id,
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", "id": product.id},
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", "id": product.id},
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, Product
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
- "portfolio": product.primary_portfolio.id,
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", "id": product.id},
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 file_name_parse
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": "shares",
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["shares"]
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["shares"].sum() == row["shares"]
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 = file_name_parse(import_source.file.name)
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
- product = parts["product"]
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.primary_portfolio.id
55
- df["currency__key"] = product.currency.key
56
- df["underlying_instrument"] = product.id
57
- df.loc[df["shares"] < 0, "transaction_subtype"] = Trade.Type.REDEMPTION
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": product.id, "transaction_date": valuation_date.strftime("%Y-%m-%d")},
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 Product, Trade
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
- shares_today = row[nominal_column].value / product.share_price
43
+ nominal_today = row[nominal_column].value
45
44
 
46
45
  try:
47
- shares_yesterday = valuation_sheet.cell_value(i + 1, nominal_column) / product.share_price
48
- trade = shares_today - shares_yesterday
46
+ nominal_yesterday = valuation_sheet.cell_value(i + 1, nominal_column)
47
+ trade = nominal_today - nominal_yesterday
49
48
  except IndexError:
50
- trade = shares_today
49
+ trade = nominal_today
51
50
 
52
51
  if trade and trade != 0:
53
52
  data.append(
54
53
  {
55
- "underlying_instrument": {"id": product.id, "instrument_type": "product"},
54
+ "underlying_instrument": {"isin": "isin", "instrument_type": "product"},
56
55
  "transaction_date": transaction_date.strftime("%Y-%m-%d"),
57
- "shares": trade,
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": product.id, "transaction_date": max_date.strftime("%Y-%m-%d")},
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", "ticker": ticker},
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": {"id": product.id, "instrument_type": "product"},
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": {"id": product.id, "instrument_type": "product"},
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, Product
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
- "portfolio": product.primary_portfolio.id,
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 Product, Trade
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.primary_portfolio.id,
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": {"id": traded_product.id, "instrument_type": "product"},
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", "id": product.id},
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, file_name_parse
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 = file_name_parse(import_source.file.name)
23
- product = parts["product"]
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.primary_portfolio.id
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
- "portfolio": product.primary_portfolio.id,
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, file_name_parse
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 = file_name_parse(import_source.file.name)
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
- product = parts["product"]
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["portfolio__id"] = product.id
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")