wbportfolio 1.45.0__py2.py3-none-any.whl → 1.45.1__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.

@@ -2,6 +2,8 @@ import json
2
2
  from contextlib import suppress
3
3
  from datetime import datetime
4
4
 
5
+ from wbportfolio.models import Product
6
+
5
7
 
6
8
  def parse(import_source):
7
9
  data = []
@@ -9,12 +11,13 @@ def parse(import_source):
9
11
  series_data = json.loads(import_source.file.read())["payload"]["series"]
10
12
  for series in series_data:
11
13
  isin = series["item"]["priceIdentifier"]
12
- for point in series["points"]:
13
- data.append(
14
- {
15
- "instrument": {"isin": isin},
16
- "date": datetime.fromtimestamp(int(point["timestamp"]) / 1000).strftime("%Y-%m-%d"),
17
- "net_value": point["close"],
18
- }
19
- )
14
+ if Product.objects.filter(isin=isin).exists(): # ensure the timeseries contain data for products we handle
15
+ for point in series["points"]:
16
+ data.append(
17
+ {
18
+ "instrument": {"isin": isin},
19
+ "date": datetime.fromtimestamp(int(point["timestamp"]) / 1000).strftime("%Y-%m-%d"),
20
+ "net_value": point["close"],
21
+ }
22
+ )
20
23
  return {"data": data}
@@ -15,6 +15,7 @@ from .utils import networkx_graph_to_plotly
15
15
  class PortfolioGraph:
16
16
  def __init__(self, portfolio: Portfolio, val_date: date, **graph_kwargs):
17
17
  self.graph = pydot.Dot("Portfolio Tree", strict=True, **graph_kwargs)
18
+ self.base_portfolio = portfolio
18
19
  self.discovered_portfolios = set()
19
20
  self.val_date = val_date
20
21
  self._extend_portfolio_graph(portfolio)
@@ -31,13 +32,17 @@ class PortfolioGraph:
31
32
  pydot.Node(
32
33
  str(parent_portfolio.id),
33
34
  label=self._convert_to_multilines(str(parent_portfolio)),
34
- shape="circle",
35
- orientation="45",
36
- style="solid",
35
+ **self._get_node_kwargs(parent_portfolio),
37
36
  )
38
37
  )
38
+ self.graph.del_edge((str(portfolio.id), str(parent_portfolio.id)))
39
39
  self.graph.add_edge(
40
- pydot.Edge(str(portfolio.id), str(parent_portfolio.id), label=f"{weighting:.2%}", style="dashed")
40
+ pydot.Edge(
41
+ str(portfolio.id),
42
+ str(parent_portfolio.id),
43
+ label=f"Invest in ({weighting:.2%})",
44
+ style="dashed",
45
+ )
41
46
  )
42
47
  # composition_edges.append((str(portfolio.id), str(parent_portfolio.id)))
43
48
  self._extend_parent_portfolios_to_graph(parent_portfolio)
@@ -49,28 +54,42 @@ class PortfolioGraph:
49
54
  pydot.Node(
50
55
  str(child_portfolio.id),
51
56
  label=self._convert_to_multilines(str(child_portfolio)),
52
- shape="circle",
53
- orientation="45",
54
- style="solid",
57
+ **self._get_node_kwargs(child_portfolio),
55
58
  )
56
59
  )
57
- # self.graph.add_edge(pydot.Edge(str(child_portfolio.id), str(portfolio.id), label="child", style="dashed"))
60
+ # we add this edge only if the opposite relationship is not already added
61
+ graph_edge = pydot.Edge(str(child_portfolio.id), str(portfolio.id), label="implements", style="dashed")
62
+ if (graph_edge.get_source(), graph_edge.get_destination()) not in self.graph.obj_dict["edges"]:
63
+ self.graph.add_edge(graph_edge)
58
64
  # composition_edges.append((str(child_portfolio.id), str(portfolio.id)))
59
65
  self._extend_child_portfolios_to_graph(child_portfolio)
60
66
 
67
+ def _get_node_kwargs(self, portfolio):
68
+ node_args = {
69
+ "shape": "circle",
70
+ "orientation": "45",
71
+ }
72
+ if portfolio == self.base_portfolio:
73
+ node_args.update({"style": "filled", "fillcolor": "lightgrey"})
74
+ else:
75
+ node_args.update(
76
+ {
77
+ "style": "solid",
78
+ }
79
+ )
80
+ return node_args
81
+
61
82
  def _extend_portfolio_graph(self, portfolio):
62
83
  self.graph.add_node(
63
84
  pydot.Node(
64
85
  str(portfolio.id),
65
86
  label=self._convert_to_multilines(str(portfolio)),
66
- shape="circle",
67
- orientation="45",
68
- style="solid",
87
+ **self._get_node_kwargs(portfolio),
69
88
  )
70
89
  )
71
90
 
72
- self._extend_parent_portfolios_to_graph(portfolio)
73
91
  self._extend_child_portfolios_to_graph(portfolio)
92
+ self._extend_parent_portfolios_to_graph(portfolio)
74
93
 
75
94
  # composition_edges = []
76
95
  # if composition_edges:
@@ -87,37 +106,35 @@ class PortfolioGraph:
87
106
  pydot.Node(
88
107
  str(rel.portfolio.id),
89
108
  label=self._convert_to_multilines(str(rel.portfolio)),
90
- shape="square",
91
- orientation="45",
92
- style="solid",
109
+ **self._get_node_kwargs(rel.portfolio),
93
110
  )
94
111
  )
95
112
  self.graph.add_node(
96
113
  pydot.Node(
97
114
  str(rel.dependency_portfolio.id),
98
115
  label=self._convert_to_multilines(str(rel.dependency_portfolio)),
99
- shape="square",
100
- orientation="45",
101
- style="solid",
116
+ **self._get_node_kwargs(rel.dependency_portfolio),
102
117
  )
103
118
  )
104
119
  label = PortfolioPortfolioThroughModel.Type[rel.type].label
105
120
  if rel.dependency_portfolio.is_composition:
106
121
  label += " (Composition)"
107
- self.graph.add_edge(
108
- pydot.Edge(
109
- str(rel.dependency_portfolio.id),
110
- str(rel.portfolio.id),
111
- label=label,
112
- style="bold",
113
- )
114
- )
122
+
115
123
  if rel.portfolio.is_lookthrough and rel.type == PortfolioPortfolioThroughModel.Type.PRIMARY:
116
124
  self.graph.add_edge(
117
125
  pydot.Edge(
118
126
  str(rel.portfolio.id), str(rel.dependency_portfolio.id), label="Look-Through", style="dotted"
119
127
  )
120
128
  )
129
+ else:
130
+ self.graph.add_edge(
131
+ pydot.Edge(
132
+ str(rel.portfolio.id),
133
+ str(rel.dependency_portfolio.id),
134
+ label=label,
135
+ style="bold",
136
+ )
137
+ )
121
138
  if rel.dependency_portfolio not in self.discovered_portfolios:
122
139
  self._extend_portfolio_graph(rel.dependency_portfolio)
123
140
  if rel.portfolio not in self.discovered_portfolios:
@@ -46,19 +46,25 @@ class RuleBackend(ActivePortfolioRelationshipMixin, backend.AbstractRuleBackend)
46
46
  def _process_dto(self, portfolio: PortfolioDTO, **kwargs) -> Generator[backend.IncidentResult, None, None]:
47
47
  if not (df := self._filter_df(pd.DataFrame(portfolio.to_df()).astype({"weighting": float}))).empty:
48
48
  df = df[["underlying_instrument", "weighting"]].groupby("underlying_instrument").sum()
49
- total_weight_threshold_1_2 = df.loc[df["weighting"] < self.threshold_2, "weighting"].sum()
49
+ total_weight_threshold_1_2 = df["weighting"].sum()
50
50
  highest_incident_type = RiskIncidentType.objects.order_by("-severity_order").first()
51
+
51
52
  for id, row in df.to_dict("index").items():
52
53
  if (row["weighting"] > self.threshold_2) or (total_weight_threshold_1_2 > self.threshold_3):
53
54
  instrument = Instrument.objects.get(id=id)
55
+ breached_value = f"""
56
+ Sum >= {self.threshold_1:.2%}: {total_weight_threshold_1_2:+.2%}
57
+ """
58
+ if row["weighting"] > self.threshold_2:
59
+ breached_value += f"<br>Instrument >= {self.threshold_2:.2%}: {instrument.name_repr}"
54
60
  yield backend.IncidentResult(
55
61
  breached_object=instrument,
56
62
  breached_object_repr=str(instrument),
57
- breached_value=f'∑[0%, {self.threshold_2:.2%}]: {row["weighting"]:+.2%} | ∑[{self.threshold_1:.2%}, {self.threshold_2:.2%}]: {total_weight_threshold_1_2:+.2%}',
63
+ breached_value=breached_value,
58
64
  report_details={
59
- "Breach Thresholds": f"{self.threshold_1}|{self.threshold_2}|{self.threshold_3}",
60
- "Weighting": f"{row['weighting']:.3f}",
61
- f"Sum of positions whose weight are between {self.threshold_1:.2%} and {self.threshold_2:.2%}": f"{total_weight_threshold_1_2:.3f}",
65
+ "Breach Thresholds": f"{self.threshold_1:.2%}|{self.threshold_2:.2%}|{self.threshold_3:.2%}",
66
+ "Weighting": f"{row['weighting']:+.2%}",
67
+ f"Sum of positions > {self.threshold_1:.2%}": f"{total_weight_threshold_1_2:+.2%}",
62
68
  },
63
69
  severity=highest_incident_type,
64
70
  )
@@ -19,7 +19,7 @@ class TestUcitsRuleModel(PortfolioTestMixin):
19
19
  # Check No single asset can represent more than 10% of the fund's assets;
20
20
  asset_position_factory.create(date=weekday, weighting=0.05, portfolio=portfolio)
21
21
  asset_position_factory.create(date=weekday, weighting=0.05, portfolio=portfolio)
22
- a3 = asset_position_factory.create(date=weekday, weighting=0.90, portfolio=portfolio)
22
+ a3 = asset_position_factory.create(date=weekday, weighting=0.15, portfolio=portfolio)
23
23
 
24
24
  res = list(ucits_backend.check_rule())
25
25
  assert len(res) == 1
@@ -1114,3 +1114,28 @@ class TestPortfolioModel(PortfolioTestMixin):
1114
1114
  undependant_portfolio = portfolio_factory.create(name="undependant portfolio", id=4)
1115
1115
  res = list(Portfolio.objects.all().to_dependency_iterator(weekday))
1116
1116
  assert res == [index_portfolio, dependency_portfolio, dependant_portfolio, undependant_portfolio]
1117
+
1118
+ def test_get_returns(self, instrument_factory, instrument_price_factory, portfolio):
1119
+ v1 = date(2025, 1, 1)
1120
+ v2 = date(2025, 1, 2)
1121
+ v3 = date(2025, 1, 3)
1122
+
1123
+ i1 = instrument_factory.create()
1124
+ i2 = instrument_factory.create()
1125
+
1126
+ i11 = instrument_price_factory.create(date=v1, instrument=i1)
1127
+ i12 = instrument_price_factory.create(date=v2, instrument=i1)
1128
+ i13 = instrument_price_factory.create(date=v3, instrument=i1)
1129
+ i11.refresh_from_db()
1130
+ i12.refresh_from_db()
1131
+ i13.refresh_from_db()
1132
+ returns, _ = portfolio.get_returns([i1.id, i2.id], from_date=v1, to_date=v3)
1133
+
1134
+ expected_returns = pd.DataFrame(
1135
+ [[i12.net_value / i11.net_value - 1], [i13.net_value / i12.net_value - 1]],
1136
+ index=[v2, v3],
1137
+ columns=[i1.id],
1138
+ dtype="float64",
1139
+ )
1140
+ expected_returns.index = pd.to_datetime(expected_returns.index)
1141
+ pd.testing.assert_frame_equal(returns, expected_returns, check_names=False, check_freq=False, atol=1e-6)
@@ -56,6 +56,8 @@ class AssetPositionProductGroupEndpointConfig(AssetPositionEndpointConfig):
56
56
 
57
57
 
58
58
  class CashPositionPortfolioEndpointConfig(AssetPositionEndpointConfig):
59
+ PK_FIELD = "portfolio"
60
+
59
61
  def get_list_endpoint(self, **kwargs):
60
62
  return reverse(
61
63
  "wbportfolio:productcashposition-list",
@@ -63,6 +65,16 @@ class CashPositionPortfolioEndpointConfig(AssetPositionEndpointConfig):
63
65
  request=self.request,
64
66
  )
65
67
 
68
+ def get_instance_endpoint(self, **kwargs):
69
+ return reverse(
70
+ "wbportfolio:portfolio-list",
71
+ args=[],
72
+ request=self.request,
73
+ )
74
+
75
+ def get_update_endpoint(self, **kwargs):
76
+ return None
77
+
66
78
 
67
79
  class ContributorPortfolioChartEndpointConfig(AssetPositionEndpointConfig):
68
80
  def get_list_endpoint(self, **kwargs):
@@ -181,7 +181,7 @@ class PortfolioTreeGraphChartViewSet(UserPortfolioRequestPermissionMixin, viewse
181
181
  return parse_date(self.request.GET["date"])
182
182
 
183
183
  def get_html(self, queryset) -> str:
184
- portfolio_graph = PortfolioGraph(self.portfolio, self.val_date)
184
+ portfolio_graph = PortfolioGraph(self.portfolio, self.val_date, rankdir="LR", size="20,11")
185
185
  return portfolio_graph.to_svg()
186
186
 
187
187
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbportfolio
3
- Version: 1.45.0
3
+ Version: 1.45.1
4
4
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
5
5
  License-File: LICENSE
6
6
  Requires-Dist: cryptography==3.4.*
@@ -187,7 +187,7 @@ wbportfolio/import_export/parsers/vontobel/performance_fees.py,sha256=DlpP2Skih2
187
187
  wbportfolio/import_export/parsers/vontobel/trade.py,sha256=Eu5qR-cjnkA_jwtrJ78xoi9ealysjHDLXIKSandqDe0,1570
188
188
  wbportfolio/import_export/parsers/vontobel/utils.py,sha256=toleo4gRb4YFtiD8m2d7z1GGGdG-O3mpTMjOO9R_E1Q,516
189
189
  wbportfolio/import_export/parsers/vontobel/valuation.py,sha256=iav8_xYpTJchmTa7KOPmFr1gi9xxLwq3e-VcZ9MDiRk,1220
190
- wbportfolio/import_export/parsers/vontobel/valuation_api.py,sha256=4-PwFCPt9pmSBgHgBGF62uEaYTNZalc2P_5piJYjmvw,690
190
+ wbportfolio/import_export/parsers/vontobel/valuation_api.py,sha256=WLkZ5z-WqhFraNorWlOhIpSx1pQ2fnjdsLHwSTA7O2o,882
191
191
  wbportfolio/import_export/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
192
  wbportfolio/import_export/resources/assets.py,sha256=zjgHlQWpud41jHrKdRyqGUt1KUJQm9Z7pt0uVh4qBWQ,2234
193
193
  wbportfolio/import_export/resources/trades.py,sha256=5HC0YrE1fnc_dOMfkOEj7vsICv1JN_OEZiGryg2GboY,1125
@@ -256,7 +256,7 @@ wbportfolio/models/registers.py,sha256=qA6T33t4gxFYnabQFBMd90WGIr6wxxirDLKDFqjOf
256
256
  wbportfolio/models/roles.py,sha256=34BwZleaPMHnUqDK1nHett45xaNNsUqSHY44844itW8,7387
257
257
  wbportfolio/models/utils.py,sha256=iBdMjRCvr6aOL0nLgfSCWUKe0h39h3IGmUbYo6l9t6w,394
258
258
  wbportfolio/models/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
259
- wbportfolio/models/graphs/portfolio.py,sha256=yztomiujS_HwXAslc3WwkIA4DH3OTE8sk71kiIHOVWU,5814
259
+ wbportfolio/models/graphs/portfolio.py,sha256=NwkehWvTcyTYrKO5ku3eNNaYLuBwuLdSbTEuugGuSIU,6541
260
260
  wbportfolio/models/graphs/utils.py,sha256=1AMpEE9mDuUZ82XgN2irxjCW1-LmziROhKevEBo0mJE,2347
261
261
  wbportfolio/models/llm/wbcrm/analyze_relationship.py,sha256=_y2Myc-M2hXQDkRGXvzsM0ZNC31dmxSHHz5BKMtymww,2106
262
262
  wbportfolio/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -303,7 +303,7 @@ wbportfolio/risk_management/backends/mixins.py,sha256=anuOlop9gc9X8tywx7_wbH7fAJ
303
303
  wbportfolio/risk_management/backends/product_integrity.py,sha256=3BbQBYYzLdj2iuQb9QOIgPjWMz1FaPkvEJoNgxWVfz0,4598
304
304
  wbportfolio/risk_management/backends/stop_loss_instrument.py,sha256=qIzeV26TRdbgG8-Nsh5O0NPMrPmIXeaRdPR-EF_uaLo,1128
305
305
  wbportfolio/risk_management/backends/stop_loss_portfolio.py,sha256=yiDa3FkZKdrmHSf8p1XmT8RhJW6KyiMuypYpY39SiO8,1674
306
- wbportfolio/risk_management/backends/ucits_portfolio.py,sha256=tBj3HWHVu4fEwLQuVhQsO4tnpOQieSpwnjPMGzWgVNU,3366
306
+ wbportfolio/risk_management/backends/ucits_portfolio.py,sha256=WAw2emAcwDBtYuYoieW0KqGST_CJ27UQuKY0B6jav_Q,3477
307
307
  wbportfolio/risk_management/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
308
  wbportfolio/risk_management/tests/conftest.py,sha256=ChVocx8SDmh13ixkRmnKe_MMudQAX0495o7nG4NDlsg,440
309
309
  wbportfolio/risk_management/tests/test_accounts.py,sha256=q9Z3hDw0Yw86VealdmUfiJBkzRcqDwfybteGxS0hAJA,3990
@@ -314,7 +314,7 @@ wbportfolio/risk_management/tests/test_liquidity_risk.py,sha256=UtzBrW7LeY0Z0ARX
314
314
  wbportfolio/risk_management/tests/test_product_integrity.py,sha256=wqJ4b5DO2BrEVQF4eVX2B9AvfrRMAyC_lppGrgKc_ug,1959
315
315
  wbportfolio/risk_management/tests/test_stop_loss_instrument.py,sha256=ZueuA9qQOv6JdKuRiPLGV3auOLRnoUCZzfqcQzGJGig,4197
316
316
  wbportfolio/risk_management/tests/test_stop_loss_portfolio.py,sha256=bFQqIUs1FZVn1SwiNkzt7hVrvT-qs3EcxOfI20xAjOM,4595
317
- wbportfolio/risk_management/tests/test_ucits_portfolio.py,sha256=7ZYVfIN0vdAcVUmu8RJtjKgzPHIwjHkPewzRrULybVY,1807
317
+ wbportfolio/risk_management/tests/test_ucits_portfolio.py,sha256=UcZLhatl8iU9AhNNYv6OBs_cjILZKaws2nvUen6orkc,1807
318
318
  wbportfolio/serializers/__init__.py,sha256=w1nPxkapcnGngYwiU7BGSLfyhJ67_PQWVjuya7YSxX0,1795
319
319
  wbportfolio/serializers/adjustments.py,sha256=0yTPgDjGRyzSa-ecCklENKtLJXZhq1dryMI3nTRsQRo,783
320
320
  wbportfolio/serializers/assets.py,sha256=EHTPwicRbdklLHvKS0_Y7pNtw7YC5iW_AuecPoTfJnc,6477
@@ -369,7 +369,7 @@ wbportfolio/tests/models/test_merge.py,sha256=sdsjiZsmR6vsUKwTa5kkvL6QTeAZqtd_EP
369
369
  wbportfolio/tests/models/test_portfolio_cash_flow.py,sha256=X8dsXexsb1b0lBiuGzu40ps_Az_1UmmKT0eo1vbXH94,5792
370
370
  wbportfolio/tests/models/test_portfolio_cash_targets.py,sha256=q8QWAwt-kKRkLC0E05GyRhF_TTQXIi8bdHjXVU0fCV0,965
371
371
  wbportfolio/tests/models/test_portfolio_swing_pricings.py,sha256=kr2AOcQkyg2pX3ULjU-o9ye-NVpjMrrfoe-DVbYCbjs,1656
372
- wbportfolio/tests/models/test_portfolios.py,sha256=tfQCTmJBZvhzd1iC5B7NW8vi3IySrioWG6ZAHFk4_oA,50675
372
+ wbportfolio/tests/models/test_portfolios.py,sha256=YF1SRS-REJFQhbtxs1LfIUs91jmHpI7pXBE70pW4W7E,51735
373
373
  wbportfolio/tests/models/test_product_groups.py,sha256=AcdxhurV-n_bBuUsfD1GqVtwLFcs7VI2CRrwzsIUWbU,3337
374
374
  wbportfolio/tests/models/test_products.py,sha256=5YYmQreFnaKLbWmrSib103wgLalqn8u01Fnh3A0XMz8,8217
375
375
  wbportfolio/tests/models/test_roles.py,sha256=4Cn7WyrA2ztJNeWLk5cy9kYo5XLWMbFSvo1O-9JYxeA,3323
@@ -403,7 +403,7 @@ wbportfolio/viewsets/portfolio_cash_flow.py,sha256=jkBfdZRQ3KsxGMJpltRjmdrZ2qEFJ
403
403
  wbportfolio/viewsets/portfolio_cash_targets.py,sha256=CvHlrDE8qnnnfRpTYnFu-Uu15MDbF5d5gTmEKth2S24,322
404
404
  wbportfolio/viewsets/portfolio_relationship.py,sha256=RGyvxd8NfFEs8YdqEvVD3VbrISvAO5UtCTlocSIuWQw,2109
405
405
  wbportfolio/viewsets/portfolio_swing_pricing.py,sha256=-57l3WLQZRslIV67OT0ucHE5JXTtTtLvd3t7MppdVn8,357
406
- wbportfolio/viewsets/portfolios.py,sha256=PZ9ZLZavQuFv6sMQzB1rCq0Ycj5Cc2Q0ddXDD-I9xRo,13014
406
+ wbportfolio/viewsets/portfolios.py,sha256=i4swoycpL3utZdTCgVAYSI2KE6bA2JyTetRh6taYUAc,13042
407
407
  wbportfolio/viewsets/positions.py,sha256=MDf_0x9La2qE6qjaIqBtfV5VC0RfJ1chZIim45Emk10,13198
408
408
  wbportfolio/viewsets/product_groups.py,sha256=YvmuXPPy98K1J_rz6YPsx9gNK-tCS2P-wc1uRYgfyo0,2399
409
409
  wbportfolio/viewsets/product_performance.py,sha256=dRfRgifjGS1RgZSu9uJRM0SmB7eLnNUkPuqARMO4gyo,28371
@@ -452,7 +452,7 @@ wbportfolio/viewsets/configs/display/trades.py,sha256=e61wLzTEDwewgctpztDa4Exvie
452
452
  wbportfolio/viewsets/configs/display/transactions.py,sha256=DOM3eV1DxBwX6Iiw3C2sJamWh6A_3ZSYC9447Jc3Wmo,2586
453
453
  wbportfolio/viewsets/configs/endpoints/__init__.py,sha256=E13AYY3CIW4CZtmqwBVMPDYA5zNyKJeRZtiXKtad68Y,2871
454
454
  wbportfolio/viewsets/configs/endpoints/adjustments.py,sha256=9CcnfNuFcxsZ8YvfUitSeyCvpLxY-jU-gIw3GG0mIp4,697
455
- wbportfolio/viewsets/configs/endpoints/assets.py,sha256=VfM9IwyYDLVcN-Hemj5IHx_5ylzhPzksqSEmOM94ys4,3618
455
+ wbportfolio/viewsets/configs/endpoints/assets.py,sha256=fe_D-x3z4xzzAJ7ktvvvwGuoX51l8iTPdDNugagxHYc,3891
456
456
  wbportfolio/viewsets/configs/endpoints/claim.py,sha256=H7oUbatjyq6HiSrMzgSMV8KqzH_PNQ5XcwHuL1yX1aA,3650
457
457
  wbportfolio/viewsets/configs/endpoints/custodians.py,sha256=Ve7TxfvFnmvm9x0cx6taO502EVWJb23fdVT9yX3ME5o,194
458
458
  wbportfolio/viewsets/configs/endpoints/esg.py,sha256=9wGxw1fW8UQJtGd-cT9XWl5gfKN68SSft3wqmkUK4MQ,467
@@ -515,7 +515,7 @@ wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQV
515
515
  wbportfolio/viewsets/transactions/trade_proposals.py,sha256=fYTvvRk7k5xsBzbIgJvU4I4OrllF0VkhlrekD4GVgDk,4296
516
516
  wbportfolio/viewsets/transactions/trades.py,sha256=6iTIM5g7TUlRtiCjIG4EdYvyfaoB6K3UC2WRX9BD9Jg,15850
517
517
  wbportfolio/viewsets/transactions/transactions.py,sha256=ixDp-nsNA8t_A06rBCT19hOMJHy0iRmdz1XKdV1OwAs,4450
518
- wbportfolio-1.45.0.dist-info/METADATA,sha256=KiCMJmtZLiBOiNHnBttN66pzpw2q1_LZU31dGkbhH_0,734
519
- wbportfolio-1.45.0.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
520
- wbportfolio-1.45.0.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
521
- wbportfolio-1.45.0.dist-info/RECORD,,
518
+ wbportfolio-1.45.1.dist-info/METADATA,sha256=A-LmtAIGxt9NHM9lduI0ajteCd-0BbzXyDcQu35Jpik,734
519
+ wbportfolio-1.45.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
520
+ wbportfolio-1.45.1.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
521
+ wbportfolio-1.45.1.dist-info/RECORD,,