wbportfolio 1.52.4__py2.py3-none-any.whl → 1.52.6__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/models/asset.py +1 -1
- wbportfolio/models/portfolio.py +1 -1
- wbportfolio/models/transactions/trade_proposals.py +59 -68
- wbportfolio/models/transactions/trades.py +26 -17
- wbportfolio/pms/trading/handler.py +37 -56
- wbportfolio/pms/typing.py +27 -9
- wbportfolio/rebalancing/models/model_portfolio.py +11 -14
- wbportfolio/serializers/transactions/trade_proposals.py +18 -1
- wbportfolio/serializers/transactions/trades.py +30 -3
- wbportfolio/tests/models/transactions/test_trade_proposals.py +7 -5
- wbportfolio/tests/rebalancing/test_models.py +10 -16
- wbportfolio/viewsets/assets.py +1 -3
- wbportfolio/viewsets/configs/buttons/trade_proposals.py +9 -1
- wbportfolio/viewsets/configs/display/trade_proposals.py +5 -4
- wbportfolio/viewsets/configs/display/trades.py +36 -0
- wbportfolio/viewsets/transactions/trade_proposals.py +3 -1
- wbportfolio/viewsets/transactions/trades.py +51 -45
- {wbportfolio-1.52.4.dist-info → wbportfolio-1.52.6.dist-info}/METADATA +1 -1
- {wbportfolio-1.52.4.dist-info → wbportfolio-1.52.6.dist-info}/RECORD +21 -21
- {wbportfolio-1.52.4.dist-info → wbportfolio-1.52.6.dist-info}/WHEEL +0 -0
- {wbportfolio-1.52.4.dist-info → wbportfolio-1.52.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
1
3
|
from django.contrib.messages import warning
|
|
2
4
|
from django.core.exceptions import ValidationError
|
|
3
5
|
from rest_framework.reverse import reverse
|
|
@@ -16,6 +18,17 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
16
18
|
queryset=Portfolio.objects.all(), write_only=True, required=False, default=DefaultFromView("portfolio")
|
|
17
19
|
)
|
|
18
20
|
_target_portfolio = PortfolioRepresentationSerializer(source="target_portfolio")
|
|
21
|
+
total_cash_weight = wb_serializers.DecimalField(
|
|
22
|
+
default=0,
|
|
23
|
+
decimal_places=4,
|
|
24
|
+
max_digits=5,
|
|
25
|
+
write_only=True,
|
|
26
|
+
required=False,
|
|
27
|
+
precision=4,
|
|
28
|
+
percent=True,
|
|
29
|
+
label="Target Cash",
|
|
30
|
+
help_text="Enter the desired percentage for the cash component. The remaining percentage (100% minus this value) will be allocated to total target weighting. Default is 0%.",
|
|
31
|
+
)
|
|
19
32
|
|
|
20
33
|
trade_date = wb_serializers.DateField(
|
|
21
34
|
read_only=lambda view: not view.new_mode, default=DefaultFromView("default_trade_date")
|
|
@@ -23,6 +36,7 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
23
36
|
|
|
24
37
|
def create(self, validated_data):
|
|
25
38
|
target_portfolio = validated_data.pop("target_portfolio", None)
|
|
39
|
+
total_cash_weight = validated_data.pop("total_cash_weight", Decimal("0.0"))
|
|
26
40
|
rebalancing_model = validated_data.get("rebalancing_model", None)
|
|
27
41
|
if request := self.context.get("request"):
|
|
28
42
|
validated_data["creator"] = request.user.profile
|
|
@@ -32,7 +46,9 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
32
46
|
if target_portfolio and not rebalancing_model and (last_effective_date := obj.last_effective_date):
|
|
33
47
|
target_portfolio_dto = target_portfolio._build_dto(last_effective_date)
|
|
34
48
|
try:
|
|
35
|
-
obj.reset_trades(
|
|
49
|
+
obj.reset_trades(
|
|
50
|
+
target_portfolio=target_portfolio_dto, total_target_weight=Decimal("1.0") - total_cash_weight
|
|
51
|
+
)
|
|
36
52
|
except ValidationError as e:
|
|
37
53
|
if request := self.context.get("request"):
|
|
38
54
|
warning(request, str(e), extra_tags="auto_close=0")
|
|
@@ -60,6 +76,7 @@ class TradeProposalModelSerializer(wb_serializers.ModelSerializer):
|
|
|
60
76
|
fields = (
|
|
61
77
|
"id",
|
|
62
78
|
"trade_date",
|
|
79
|
+
"total_cash_weight",
|
|
63
80
|
"comment",
|
|
64
81
|
"status",
|
|
65
82
|
"portfolio",
|
|
@@ -320,6 +320,16 @@ class TradeTradeProposalModelSerializer(TradeModelSerializer):
|
|
|
320
320
|
effective_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
|
|
321
321
|
target_shares = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=6, default=0)
|
|
322
322
|
|
|
323
|
+
total_value_fx_portfolio = wb_serializers.DecimalField(read_only=True, max_digits=16, decimal_places=2, default=0)
|
|
324
|
+
effective_total_value_fx_portfolio = wb_serializers.DecimalField(
|
|
325
|
+
read_only=True, max_digits=16, decimal_places=2, default=0
|
|
326
|
+
)
|
|
327
|
+
target_total_value_fx_portfolio = wb_serializers.DecimalField(
|
|
328
|
+
read_only=True, max_digits=16, decimal_places=2, default=0
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
portfolio_currency = wb_serializers.CharField(read_only=True)
|
|
332
|
+
|
|
323
333
|
def validate(self, data):
|
|
324
334
|
data.pop("company", None)
|
|
325
335
|
data.pop("security", None)
|
|
@@ -331,6 +341,8 @@ class TradeTradeProposalModelSerializer(TradeModelSerializer):
|
|
|
331
341
|
)
|
|
332
342
|
effective_weight = self.instance._effective_weight if self.instance else Decimal(0.0)
|
|
333
343
|
weighting = data.get("weighting", self.instance.weighting if self.instance else Decimal(0.0))
|
|
344
|
+
if (target_weight := data.pop("target_weight", None)) is not None:
|
|
345
|
+
weighting = target_weight - effective_weight
|
|
334
346
|
if (target_weight := data.pop("target_weight", None)) is not None:
|
|
335
347
|
weighting = target_weight - effective_weight
|
|
336
348
|
if weighting >= 0:
|
|
@@ -343,14 +355,25 @@ class TradeTradeProposalModelSerializer(TradeModelSerializer):
|
|
|
343
355
|
class Meta:
|
|
344
356
|
model = Trade
|
|
345
357
|
percent_fields = ["effective_weight", "target_weight", "weighting"]
|
|
358
|
+
decorators = {
|
|
359
|
+
"total_value_fx_portfolio": wb_serializers.decorator(
|
|
360
|
+
decorator_type="text", position="left", value="{{portfolio_currency}}"
|
|
361
|
+
),
|
|
362
|
+
"effective_total_value_fx_portfolio": wb_serializers.decorator(
|
|
363
|
+
decorator_type="text", position="left", value="{{portfolio_currency}}"
|
|
364
|
+
),
|
|
365
|
+
"target_total_value_fx_portfolio": wb_serializers.decorator(
|
|
366
|
+
decorator_type="text", position="left", value="{{portfolio_currency}}"
|
|
367
|
+
),
|
|
368
|
+
}
|
|
346
369
|
read_only_fields = (
|
|
347
370
|
"transaction_subtype",
|
|
348
371
|
"shares",
|
|
349
|
-
# "underlying_instrument",
|
|
350
|
-
# "_underlying_instrument",
|
|
351
|
-
"shares",
|
|
352
372
|
"effective_shares",
|
|
353
373
|
"target_shares",
|
|
374
|
+
"total_value_fx_portfolio",
|
|
375
|
+
"effective_total_value_fx_portfolio",
|
|
376
|
+
"target_total_value_fx_portfolio",
|
|
354
377
|
)
|
|
355
378
|
fields = (
|
|
356
379
|
"id",
|
|
@@ -375,6 +398,10 @@ class TradeTradeProposalModelSerializer(TradeModelSerializer):
|
|
|
375
398
|
"order",
|
|
376
399
|
"effective_shares",
|
|
377
400
|
"target_shares",
|
|
401
|
+
"total_value_fx_portfolio",
|
|
402
|
+
"effective_total_value_fx_portfolio",
|
|
403
|
+
"target_total_value_fx_portfolio",
|
|
404
|
+
"portfolio_currency",
|
|
378
405
|
)
|
|
379
406
|
|
|
380
407
|
|
|
@@ -66,11 +66,11 @@ class TestTradeProposal:
|
|
|
66
66
|
validated_trading_service = trade_proposal.validated_trading_service
|
|
67
67
|
|
|
68
68
|
# Assert effective and target portfolios are as expected
|
|
69
|
-
assert validated_trading_service.
|
|
69
|
+
assert validated_trading_service._effective_portfolio.to_dict() == {
|
|
70
70
|
a1.underlying_quote.id: a1.weighting,
|
|
71
71
|
a2.underlying_quote.id: a2.weighting,
|
|
72
72
|
}
|
|
73
|
-
assert validated_trading_service.
|
|
73
|
+
assert validated_trading_service._target_portfolio.to_dict() == {
|
|
74
74
|
a1.underlying_quote.id: a1.weighting + t1.weighting,
|
|
75
75
|
a2.underlying_quote.id: a2.weighting + t2.weighting,
|
|
76
76
|
}
|
|
@@ -361,6 +361,8 @@ class TestTradeProposal:
|
|
|
361
361
|
"""
|
|
362
362
|
portfolio = trade_proposal.portfolio
|
|
363
363
|
instrument = instrument_factory.create(currency=portfolio.currency)
|
|
364
|
+
underlying_quote_price = instrument_price_factory.create(instrument=instrument, date=trade_proposal.trade_date)
|
|
365
|
+
mock_fct.return_value = Decimal(1_000_000) # 1 million cash
|
|
364
366
|
trade = trade_factory.create(
|
|
365
367
|
trade_proposal=trade_proposal,
|
|
366
368
|
transaction_date=trade_proposal.trade_date,
|
|
@@ -368,12 +370,12 @@ class TestTradeProposal:
|
|
|
368
370
|
underlying_instrument=instrument,
|
|
369
371
|
)
|
|
370
372
|
trade.refresh_from_db()
|
|
371
|
-
underlying_quote_price = instrument_price_factory.create(instrument=instrument, date=trade.transaction_date)
|
|
372
|
-
mock_fct.return_value = Decimal(1_000_000) # 1 million cash
|
|
373
373
|
|
|
374
374
|
# Assert estimated shares are correctly calculated
|
|
375
375
|
assert (
|
|
376
|
-
trade_proposal.get_estimated_shares(
|
|
376
|
+
trade_proposal.get_estimated_shares(
|
|
377
|
+
trade.weighting, trade.underlying_instrument, underlying_quote_price.net_value
|
|
378
|
+
)
|
|
377
379
|
== Decimal(1_000_000) * trade.weighting / underlying_quote_price.net_value
|
|
378
380
|
)
|
|
379
381
|
|
|
@@ -38,30 +38,24 @@ class TestModelPortfolioRebalancing:
|
|
|
38
38
|
def model(self, portfolio, weekday):
|
|
39
39
|
from wbportfolio.rebalancing.models import ModelPortfolioRebalancing
|
|
40
40
|
|
|
41
|
+
PortfolioPortfolioThroughModel.objects.create(
|
|
42
|
+
portfolio=portfolio,
|
|
43
|
+
dependency_portfolio=PortfolioFactory.create(),
|
|
44
|
+
type=PortfolioPortfolioThroughModel.Type.MODEL,
|
|
45
|
+
)
|
|
41
46
|
return ModelPortfolioRebalancing(portfolio, (weekday + BDay(1)).date(), weekday)
|
|
42
47
|
|
|
43
48
|
def test_is_valid(self, portfolio, weekday, model, asset_position_factory, instrument_price_factory):
|
|
44
49
|
assert not model.is_valid()
|
|
45
50
|
asset_position_factory.create(portfolio=model.portfolio, date=model.last_effective_date)
|
|
46
51
|
assert not model.is_valid()
|
|
47
|
-
|
|
48
|
-
PortfolioPortfolioThroughModel.objects.create(
|
|
49
|
-
portfolio=model.portfolio,
|
|
50
|
-
dependency_portfolio=model_portfolio,
|
|
51
|
-
type=PortfolioPortfolioThroughModel.Type.MODEL,
|
|
52
|
-
)
|
|
52
|
+
|
|
53
53
|
a = asset_position_factory.create(portfolio=model.model_portfolio, date=model.last_effective_date)
|
|
54
54
|
assert not model.is_valid()
|
|
55
55
|
instrument_price_factory.create(instrument=a.underlying_quote, date=model.trade_date)
|
|
56
56
|
assert model.is_valid()
|
|
57
57
|
|
|
58
58
|
def test_get_target_portfolio(self, portfolio, weekday, model, asset_position_factory):
|
|
59
|
-
model_portfolio = PortfolioFactory.create()
|
|
60
|
-
PortfolioPortfolioThroughModel.objects.create(
|
|
61
|
-
portfolio=model.portfolio,
|
|
62
|
-
dependency_portfolio=model_portfolio,
|
|
63
|
-
type=PortfolioPortfolioThroughModel.Type.MODEL,
|
|
64
|
-
)
|
|
65
59
|
asset_position_factory(portfolio=portfolio, date=model.last_effective_date) # noise
|
|
66
60
|
asset_position_factory(portfolio=portfolio, date=model.last_effective_date) # noise
|
|
67
61
|
a1 = asset_position_factory(weighting=0.8, portfolio=portfolio.model_portfolio, date=model.last_effective_date)
|
|
@@ -91,7 +85,7 @@ class TestCompositeRebalancing:
|
|
|
91
85
|
transaction_date=model.last_effective_date,
|
|
92
86
|
transaction_subtype=Trade.Type.BUY,
|
|
93
87
|
trade_proposal=trade_proposal,
|
|
94
|
-
weighting=0.7,
|
|
88
|
+
weighting=Decimal(0.7),
|
|
95
89
|
status=Trade.Status.EXECUTED,
|
|
96
90
|
)
|
|
97
91
|
TradeFactory.create(
|
|
@@ -99,7 +93,7 @@ class TestCompositeRebalancing:
|
|
|
99
93
|
transaction_date=model.last_effective_date,
|
|
100
94
|
transaction_subtype=Trade.Type.BUY,
|
|
101
95
|
trade_proposal=trade_proposal,
|
|
102
|
-
weighting=0.3,
|
|
96
|
+
weighting=Decimal(0.3),
|
|
103
97
|
status=Trade.Status.EXECUTED,
|
|
104
98
|
)
|
|
105
99
|
assert not model.is_valid()
|
|
@@ -117,7 +111,7 @@ class TestCompositeRebalancing:
|
|
|
117
111
|
transaction_date=model.last_effective_date,
|
|
118
112
|
transaction_subtype=Trade.Type.BUY,
|
|
119
113
|
trade_proposal=trade_proposal,
|
|
120
|
-
weighting=0.8,
|
|
114
|
+
weighting=Decimal(0.8),
|
|
121
115
|
status=Trade.Status.EXECUTED,
|
|
122
116
|
)
|
|
123
117
|
t2 = TradeFactory.create(
|
|
@@ -125,7 +119,7 @@ class TestCompositeRebalancing:
|
|
|
125
119
|
transaction_date=model.last_effective_date,
|
|
126
120
|
transaction_subtype=Trade.Type.BUY,
|
|
127
121
|
trade_proposal=trade_proposal,
|
|
128
|
-
weighting=0.2,
|
|
122
|
+
weighting=Decimal(0.2),
|
|
129
123
|
status=Trade.Status.EXECUTED,
|
|
130
124
|
)
|
|
131
125
|
target_portfolio = model.get_target_portfolio()
|
wbportfolio/viewsets/assets.py
CHANGED
|
@@ -478,9 +478,7 @@ class AssetPositionUnderlyingInstrumentChartViewSet(UserPortfolioRequestPermissi
|
|
|
478
478
|
filterset_class = AssetPositionUnderlyingInstrumentChartFilter
|
|
479
479
|
|
|
480
480
|
def get_queryset(self):
|
|
481
|
-
return AssetPosition.objects.filter(
|
|
482
|
-
underlying_instrument__in=self.instrument.get_descendants(include_self=True)
|
|
483
|
-
)
|
|
481
|
+
return AssetPosition.objects.filter(underlying_quote__in=self.instrument.get_descendants(include_self=True))
|
|
484
482
|
|
|
485
483
|
def get_plotly(self, queryset):
|
|
486
484
|
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
from wbcore import serializers as wb_serializers
|
|
1
2
|
from wbcore.contrib.icons import WBIcon
|
|
2
3
|
from wbcore.enums import RequestType
|
|
3
4
|
from wbcore.metadata.configs import buttons as bt
|
|
4
5
|
from wbcore.metadata.configs.buttons.view_config import ButtonViewConfig
|
|
6
|
+
from wbcore.metadata.configs.display import create_simple_display
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NormalizeSerializer(wb_serializers.Serializer):
|
|
10
|
+
total_cash_weight = wb_serializers.FloatField(default=0, precision=4, percent=True)
|
|
5
11
|
|
|
6
12
|
|
|
7
13
|
class TradeProposalButtonConfig(ButtonViewConfig):
|
|
@@ -41,10 +47,12 @@ class TradeProposalButtonConfig(ButtonViewConfig):
|
|
|
41
47
|
icon=WBIcon.EDIT.icon,
|
|
42
48
|
label="Normalize Trades",
|
|
43
49
|
description_fields="""
|
|
44
|
-
<p>Make sure all trades normalize to a total target weight of 100%</p>
|
|
50
|
+
<p>Make sure all trades normalize to a total target weight of (100 - {{total_cash_weight}})%</p>
|
|
45
51
|
""",
|
|
46
52
|
action_label="Normalize Trades",
|
|
47
53
|
title="Normalize Trades",
|
|
54
|
+
serializer=NormalizeSerializer,
|
|
55
|
+
instance_display=create_simple_display([["total_cash_weight"]]),
|
|
48
56
|
),
|
|
49
57
|
bt.ActionButton(
|
|
50
58
|
method=RequestType.PATCH,
|
|
@@ -77,11 +77,12 @@ class TradeProposalDisplayConfig(DisplayViewConfig):
|
|
|
77
77
|
layouts={
|
|
78
78
|
default(): Layout(
|
|
79
79
|
grid_template_areas=[
|
|
80
|
-
["status", "status"
|
|
81
|
-
["trade_date", "
|
|
80
|
+
["status", "status"],
|
|
81
|
+
["trade_date", "total_cash_weight"],
|
|
82
|
+
["rebalancing_model", "target_portfolio"]
|
|
82
83
|
if self.view.new_mode
|
|
83
|
-
else ["
|
|
84
|
-
["comment", "comment"
|
|
84
|
+
else ["rebalancing_model", "rebalancing_model"],
|
|
85
|
+
["comment", "comment"],
|
|
85
86
|
],
|
|
86
87
|
),
|
|
87
88
|
},
|
|
@@ -357,6 +357,42 @@ class TradeTradeProposalDisplayConfig(DisplayViewConfig):
|
|
|
357
357
|
],
|
|
358
358
|
)
|
|
359
359
|
)
|
|
360
|
+
fields.append(
|
|
361
|
+
dp.Field(
|
|
362
|
+
label="Total Value",
|
|
363
|
+
open_by_default=False,
|
|
364
|
+
key=None,
|
|
365
|
+
children=[
|
|
366
|
+
dp.Field(
|
|
367
|
+
key="effective_total_value_fx_portfolio",
|
|
368
|
+
label="Effective Total Value",
|
|
369
|
+
show="open",
|
|
370
|
+
width=Unit.PIXEL(150),
|
|
371
|
+
),
|
|
372
|
+
dp.Field(
|
|
373
|
+
key="target_total_value_fx_portfolio",
|
|
374
|
+
label="Target Total Value",
|
|
375
|
+
show="open",
|
|
376
|
+
width=Unit.PIXEL(150),
|
|
377
|
+
),
|
|
378
|
+
dp.Field(
|
|
379
|
+
key="total_value_fx_portfolio",
|
|
380
|
+
label="Total Value",
|
|
381
|
+
formatting_rules=[
|
|
382
|
+
dp.FormattingRule(
|
|
383
|
+
style={"color": WBColor.RED_DARK.value, "fontWeight": "bold"},
|
|
384
|
+
condition=("<", 0),
|
|
385
|
+
),
|
|
386
|
+
dp.FormattingRule(
|
|
387
|
+
style={"color": WBColor.GREEN_DARK.value, "fontWeight": "bold"},
|
|
388
|
+
condition=(">", 0),
|
|
389
|
+
),
|
|
390
|
+
],
|
|
391
|
+
width=Unit.PIXEL(150),
|
|
392
|
+
),
|
|
393
|
+
],
|
|
394
|
+
)
|
|
395
|
+
)
|
|
360
396
|
fields.append(
|
|
361
397
|
dp.Field(
|
|
362
398
|
label="Information",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from contextlib import suppress
|
|
2
2
|
from datetime import date
|
|
3
|
+
from decimal import Decimal
|
|
3
4
|
|
|
4
5
|
from django.contrib.messages import info, warning
|
|
5
6
|
from django.shortcuts import get_object_or_404
|
|
@@ -106,8 +107,9 @@ class TradeProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserP
|
|
|
106
107
|
@action(detail=True, methods=["PATCH"])
|
|
107
108
|
def normalize(self, request, pk=None):
|
|
108
109
|
trade_proposal = get_object_or_404(TradeProposal, pk=pk)
|
|
110
|
+
total_cash_weight = Decimal(request.data.get("total_cash_weight", Decimal("0.0")))
|
|
109
111
|
if trade_proposal.status == TradeProposal.Status.DRAFT:
|
|
110
|
-
trade_proposal.normalize_trades()
|
|
112
|
+
trade_proposal.normalize_trades(total_target_weight=Decimal("1.0") - total_cash_weight)
|
|
111
113
|
return Response({"send": True})
|
|
112
114
|
return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)
|
|
113
115
|
|
|
@@ -25,7 +25,6 @@ from wbcore.permissions.permissions import InternalUserPermissionMixin
|
|
|
25
25
|
from wbcore.utils.strings import format_number
|
|
26
26
|
from wbcore.viewsets.mixins import OrderableMixin
|
|
27
27
|
from wbcrm.models import Account
|
|
28
|
-
from wbfdm.models import Cash
|
|
29
28
|
|
|
30
29
|
from wbportfolio.filters import (
|
|
31
30
|
SubscriptionRedemptionFilterSet,
|
|
@@ -395,6 +394,10 @@ class TradeTradeProposalModelViewSet(
|
|
|
395
394
|
def trade_proposal(self):
|
|
396
395
|
return get_object_or_404(TradeProposal, pk=self.kwargs["trade_proposal_id"])
|
|
397
396
|
|
|
397
|
+
@cached_property
|
|
398
|
+
def portfolio_total_asset_value(self):
|
|
399
|
+
return self.trade_proposal.portfolio_total_asset_value
|
|
400
|
+
|
|
398
401
|
def has_import_permission(self, request) -> bool: # allow import only on draft trade proposal
|
|
399
402
|
return super().has_import_permission(request) and self.trade_proposal.status == TradeProposal.Status.DRAFT
|
|
400
403
|
|
|
@@ -409,46 +412,36 @@ class TradeTradeProposalModelViewSet(
|
|
|
409
412
|
def get_aggregates(self, queryset, *args, **kwargs):
|
|
410
413
|
agg = {}
|
|
411
414
|
if queryset.exists():
|
|
412
|
-
cash_target_position = self.trade_proposal.get_estimated_target_cash(
|
|
413
|
-
self.trade_proposal.portfolio.currency
|
|
414
|
-
)
|
|
415
|
-
cash_target_cash_weight, cash_target_cash_shares = (
|
|
416
|
-
cash_target_position.weighting,
|
|
417
|
-
cash_target_position.initial_shares,
|
|
418
|
-
)
|
|
419
|
-
extra_existing_cash_components = Cash.objects.filter(
|
|
420
|
-
id__in=self.trade_proposal.trades.filter(underlying_instrument__is_cash=True).values(
|
|
421
|
-
"underlying_instrument"
|
|
422
|
-
)
|
|
423
|
-
).exclude(currency=self.trade_proposal.portfolio.currency)
|
|
424
|
-
|
|
425
|
-
for cash_component in extra_existing_cash_components:
|
|
426
|
-
extra_cash_position = self.trade_proposal.get_estimated_target_cash(cash_component.currency)
|
|
427
|
-
cash_target_cash_weight += extra_cash_position.weighting
|
|
428
|
-
cash_target_cash_shares += extra_cash_position.initial_shares
|
|
429
415
|
noncash_aggregates = queryset.filter(underlying_instrument__is_cash=False).aggregate(
|
|
430
416
|
sum_target_weight=Sum(F("target_weight")),
|
|
431
417
|
sum_effective_weight=Sum(F("effective_weight")),
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
)
|
|
435
|
-
cash_aggregates = queryset.filter(underlying_instrument__is_cash=True).aggregate(
|
|
436
|
-
sum_effective_weight=Sum(F("effective_weight")),
|
|
437
|
-
sum_effective_shares=Sum(F("effective_shares")),
|
|
418
|
+
sum_target_total_value_fx_portfolio=Sum(F("target_total_value_fx_portfolio")),
|
|
419
|
+
sum_effective_total_value_fx_portfolio=Sum(F("effective_total_value_fx_portfolio")),
|
|
438
420
|
)
|
|
421
|
+
|
|
439
422
|
# weights aggregates
|
|
440
|
-
cash_sum_effective_weight =
|
|
423
|
+
cash_sum_effective_weight = Decimal("1.0") - noncash_aggregates["sum_effective_weight"]
|
|
424
|
+
cash_sum_target_cash_weight = Decimal("1.0") - noncash_aggregates["sum_target_weight"]
|
|
441
425
|
noncash_sum_effective_weight = noncash_aggregates["sum_effective_weight"] or Decimal(0)
|
|
442
426
|
noncash_sum_target_weight = noncash_aggregates["sum_target_weight"] or Decimal(0)
|
|
443
427
|
sum_buy_weight = queryset.filter(weighting__gte=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
|
|
444
428
|
sum_sell_weight = queryset.filter(weighting__lt=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
|
|
445
429
|
|
|
446
430
|
# shares aggregates
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
431
|
+
cash_sum_effective_total_value_fx_portfolio = cash_sum_effective_weight * self.portfolio_total_asset_value
|
|
432
|
+
cash_sum_target_total_value_fx_portfolio = cash_sum_target_cash_weight * self.portfolio_total_asset_value
|
|
433
|
+
noncash_sum_effective_total_value_fx_portfolio = noncash_aggregates[
|
|
434
|
+
"sum_effective_total_value_fx_portfolio"
|
|
435
|
+
] or Decimal(0)
|
|
436
|
+
noncash_sum_target_total_value_fx_portfolio = noncash_aggregates[
|
|
437
|
+
"sum_target_total_value_fx_portfolio"
|
|
438
|
+
] or Decimal(0)
|
|
439
|
+
sum_buy_total_value_fx_portfolio = queryset.filter(total_value_fx_portfolio__gte=0).aggregate(
|
|
440
|
+
s=Sum(F("total_value_fx_portfolio"))
|
|
441
|
+
)["s"] or Decimal(0)
|
|
442
|
+
sum_sell_total_value_fx_portfolio = queryset.filter(total_value_fx_portfolio__lt=0).aggregate(
|
|
443
|
+
s=Sum(F("total_value_fx_portfolio"))
|
|
444
|
+
)["s"] or Decimal(0)
|
|
452
445
|
|
|
453
446
|
agg = {
|
|
454
447
|
"effective_weight": {
|
|
@@ -457,29 +450,38 @@ class TradeTradeProposalModelViewSet(
|
|
|
457
450
|
"Total": format_number(noncash_sum_effective_weight + cash_sum_effective_weight, decimal=6),
|
|
458
451
|
},
|
|
459
452
|
"target_weight": {
|
|
460
|
-
"Cash": format_number(
|
|
453
|
+
"Cash": format_number(cash_sum_target_cash_weight, decimal=6),
|
|
461
454
|
"Non-Cash": format_number(noncash_sum_target_weight, decimal=6),
|
|
462
|
-
"Total": format_number(
|
|
455
|
+
"Total": format_number(cash_sum_target_cash_weight + noncash_sum_target_weight, decimal=6),
|
|
463
456
|
},
|
|
464
|
-
"
|
|
465
|
-
"Cash": format_number(
|
|
466
|
-
"Non-Cash": format_number(
|
|
467
|
-
"Total": format_number(
|
|
457
|
+
"effective_total_value_fx_portfolio": {
|
|
458
|
+
"Cash": format_number(cash_sum_effective_total_value_fx_portfolio, decimal=6),
|
|
459
|
+
"Non-Cash": format_number(noncash_sum_effective_total_value_fx_portfolio, decimal=6),
|
|
460
|
+
"Total": format_number(
|
|
461
|
+
cash_sum_effective_total_value_fx_portfolio + noncash_sum_effective_total_value_fx_portfolio,
|
|
462
|
+
decimal=6,
|
|
463
|
+
),
|
|
468
464
|
},
|
|
469
|
-
"
|
|
470
|
-
"Cash": format_number(
|
|
471
|
-
"Non-Cash": format_number(
|
|
472
|
-
"Total": format_number(
|
|
465
|
+
"target_total_value_fx_portfolio": {
|
|
466
|
+
"Cash": format_number(cash_sum_target_total_value_fx_portfolio, decimal=6),
|
|
467
|
+
"Non-Cash": format_number(noncash_sum_target_total_value_fx_portfolio, decimal=6),
|
|
468
|
+
"Total": format_number(
|
|
469
|
+
cash_sum_target_total_value_fx_portfolio + noncash_sum_target_total_value_fx_portfolio,
|
|
470
|
+
decimal=6,
|
|
471
|
+
),
|
|
473
472
|
},
|
|
474
473
|
"weighting": {
|
|
475
|
-
"Cash Flow": format_number(
|
|
474
|
+
"Cash Flow": format_number(cash_sum_target_cash_weight - cash_sum_effective_weight, decimal=6),
|
|
476
475
|
"Buy": format_number(sum_buy_weight, decimal=6),
|
|
477
476
|
"Sell": format_number(sum_sell_weight, decimal=6),
|
|
478
477
|
},
|
|
479
|
-
"
|
|
480
|
-
"Cash Flow": format_number(
|
|
481
|
-
|
|
482
|
-
|
|
478
|
+
"total_value_fx_portfolio": {
|
|
479
|
+
"Cash Flow": format_number(
|
|
480
|
+
cash_sum_target_total_value_fx_portfolio - cash_sum_effective_total_value_fx_portfolio,
|
|
481
|
+
decimal=6,
|
|
482
|
+
),
|
|
483
|
+
"Buy": format_number(sum_buy_total_value_fx_portfolio, decimal=6),
|
|
484
|
+
"Sell": format_number(sum_sell_total_value_fx_portfolio, decimal=6),
|
|
483
485
|
},
|
|
484
486
|
}
|
|
485
487
|
|
|
@@ -509,6 +511,7 @@ class TradeTradeProposalModelViewSet(
|
|
|
509
511
|
qs = super().get_queryset().filter(trade_proposal=self.kwargs["trade_proposal_id"]).annotate_base_info()
|
|
510
512
|
else:
|
|
511
513
|
qs = TradeProposal.objects.none()
|
|
514
|
+
|
|
512
515
|
return qs.annotate(
|
|
513
516
|
underlying_instrument_isin=F("underlying_instrument__isin"),
|
|
514
517
|
underlying_instrument_ticker=F("underlying_instrument__ticker"),
|
|
@@ -520,4 +523,7 @@ class TradeTradeProposalModelViewSet(
|
|
|
520
523
|
),
|
|
521
524
|
default=F("underlying_instrument__instrument_type__short_name"),
|
|
522
525
|
),
|
|
526
|
+
effective_total_value_fx_portfolio=F("effective_weight") * Value(self.portfolio_total_asset_value),
|
|
527
|
+
target_total_value_fx_portfolio=F("target_weight") * Value(self.portfolio_total_asset_value),
|
|
528
|
+
portfolio_currency=F("portfolio__currency__symbol"),
|
|
523
529
|
)
|
|
@@ -250,11 +250,11 @@ wbportfolio/migrations/0077_remove_transaction_currency_and_more.py,sha256=Yf4a3
|
|
|
250
250
|
wbportfolio/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
251
251
|
wbportfolio/models/__init__.py,sha256=HSpa5xwh_MHQaBpNrq9E0CbdEE5Iq-pDLIsPzZ-TRTg,904
|
|
252
252
|
wbportfolio/models/adjustments.py,sha256=osXWkJZOiansPWYPyHtl7Z121zDWi7u1YMtrBQtbHVo,10272
|
|
253
|
-
wbportfolio/models/asset.py,sha256=
|
|
253
|
+
wbportfolio/models/asset.py,sha256=WwbLjpGHzz17yJxrkuTP4MbErqcTJMlKkDrQI92p5II,45490
|
|
254
254
|
wbportfolio/models/custodians.py,sha256=owTiS2Vm5CRKzh9M_P9GOVg-s-ndQ9UvRmw3yZP7cw0,3815
|
|
255
255
|
wbportfolio/models/exceptions.py,sha256=3ix0tWUO-O6jpz8f07XIwycw2x3JFRoWzjwil8FVA2Q,52
|
|
256
256
|
wbportfolio/models/indexes.py,sha256=gvW4K9U9Bj8BmVCqFYdWiXvDWhjHINRON8XhNsZUiQY,639
|
|
257
|
-
wbportfolio/models/portfolio.py,sha256=
|
|
257
|
+
wbportfolio/models/portfolio.py,sha256=yQ8cqAZLOMwsiejC9RzOLoqLDV5q5ZWYWMOc2BlcHdw,56281
|
|
258
258
|
wbportfolio/models/portfolio_cash_flow.py,sha256=uElG7IJUBY8qvtrXftOoskX6EA-dKgEG1JJdvHeWV7g,7336
|
|
259
259
|
wbportfolio/models/portfolio_cash_targets.py,sha256=WmgG-etPisZsh2yaFQpz7EkpvAudKBEzqPsO715w52U,1498
|
|
260
260
|
wbportfolio/models/portfolio_relationship.py,sha256=ZGECiPZiLdlk4uSamOrEfuzO0hduK6OMKJLUSnh5_kc,5190
|
|
@@ -281,16 +281,16 @@ wbportfolio/models/transactions/claim.py,sha256=SF2FlwG6SRVmA_hT0NbXah5-fYejccWK
|
|
|
281
281
|
wbportfolio/models/transactions/dividends.py,sha256=mmOdGWR35yndUMoCuG24Y6BdtxDhSk2gMQ-8LVguqzg,1890
|
|
282
282
|
wbportfolio/models/transactions/fees.py,sha256=wJtlzbBCAq1UHvv0wqWTE2BEjCF5RMtoaSDS3kODFRo,7112
|
|
283
283
|
wbportfolio/models/transactions/rebalancing.py,sha256=obzgewWKOD4kJbCoF5fhtfDk502QkbrjPKh8T9KDGew,7355
|
|
284
|
-
wbportfolio/models/transactions/trade_proposals.py,sha256=
|
|
285
|
-
wbportfolio/models/transactions/trades.py,sha256=
|
|
284
|
+
wbportfolio/models/transactions/trade_proposals.py,sha256=Jd7rfiE42Qtqp9VXQ448OF9m_DKoFR8ISjSh-1TDi8o,31592
|
|
285
|
+
wbportfolio/models/transactions/trades.py,sha256=og8R4Sg7LCRQsuX09uYjZXq9q4VsUb0j8W8iCTyx0pQ,31571
|
|
286
286
|
wbportfolio/models/transactions/transactions.py,sha256=XTcUeMUfkf5XTSZaR2UAyGqCVkOhQYk03_vzHLIgf8Q,3807
|
|
287
287
|
wbportfolio/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
288
|
-
wbportfolio/pms/typing.py,sha256=
|
|
288
|
+
wbportfolio/pms/typing.py,sha256=qcqEs94HA7qNB631LErV7FVQ4ytYBLetuPDcilrnlW0,6798
|
|
289
289
|
wbportfolio/pms/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
290
290
|
wbportfolio/pms/analytics/portfolio.py,sha256=vE0KA6Z037bUdmBTkYuBqXElt80nuYObNzY_kWvxEZY,1360
|
|
291
291
|
wbportfolio/pms/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
292
292
|
wbportfolio/pms/trading/__init__.py,sha256=R_yLKc54sCak8A1cW0O1Aszrcv5KV8mC_3h17Hr20e4,36
|
|
293
|
-
wbportfolio/pms/trading/handler.py,sha256=
|
|
293
|
+
wbportfolio/pms/trading/handler.py,sha256=ORvtwAG2VX1GvNvNE2HnDvS-4UNrgBwCMPydnfphtY0,5992
|
|
294
294
|
wbportfolio/rebalancing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
295
295
|
wbportfolio/rebalancing/base.py,sha256=NwTGZtBm1f35gj5Jp6iTyyFvDT1GSIztN990cKBvYzQ,637
|
|
296
296
|
wbportfolio/rebalancing/decorators.py,sha256=JhQ2vkcIuGBTGvNmpkQerdw2-vLq-RAb0KAPjOrETkw,515
|
|
@@ -298,7 +298,7 @@ wbportfolio/rebalancing/models/__init__.py,sha256=AQjG7Tu5vlmhqncVoYOjpBKU2UIvgo
|
|
|
298
298
|
wbportfolio/rebalancing/models/composite.py,sha256=XEgK3oMurrE_d_l5uN0stBKRrtvnKQzRWyXNXuBYfmc,1818
|
|
299
299
|
wbportfolio/rebalancing/models/equally_weighted.py,sha256=FCpSKOs49ckNYVgoYIiHB0BqPT9OeCMuFoet4Ixbp-Y,1210
|
|
300
300
|
wbportfolio/rebalancing/models/market_capitalization_weighted.py,sha256=6ZsR8iJg6l89CsxHqoxJSXlTaj8Pmb8_bFPrXhnxaRs,5295
|
|
301
|
-
wbportfolio/rebalancing/models/model_portfolio.py,sha256=
|
|
301
|
+
wbportfolio/rebalancing/models/model_portfolio.py,sha256=Dk3tw9u3WG1jCP3V2-R05GS4-DmDBBtxH4h6p7pRe4g,1393
|
|
302
302
|
wbportfolio/reports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
303
303
|
wbportfolio/reports/monthly_position_report.py,sha256=e7BzjDd6eseUOwLwQJXKvWErQ58YnCsznHU2VtR6izM,2981
|
|
304
304
|
wbportfolio/risk_management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -346,8 +346,8 @@ wbportfolio/serializers/transactions/__init__.py,sha256=oAfidhjjCKP0exeHbzJgGuBd
|
|
|
346
346
|
wbportfolio/serializers/transactions/claim.py,sha256=kC4E2RZRrpd9i8tGfoiV-gpWDk3ikR5F1Wf0v_IGIvw,11599
|
|
347
347
|
wbportfolio/serializers/transactions/dividends.py,sha256=ADXf9cXe8rq55lC_a8vIzViGLmQ-yDXkgR54k2m-N0w,1814
|
|
348
348
|
wbportfolio/serializers/transactions/fees.py,sha256=3mBzs6vdfST9roeQB-bmLJhipY7i5jBtAXjoTTE-GOg,2388
|
|
349
|
-
wbportfolio/serializers/transactions/trade_proposals.py,sha256=
|
|
350
|
-
wbportfolio/serializers/transactions/trades.py,sha256=
|
|
349
|
+
wbportfolio/serializers/transactions/trade_proposals.py,sha256=ufYBYiSttz5KAlAaaXbnf98oUFT_qNfxF_VUM4ClXE8,4072
|
|
350
|
+
wbportfolio/serializers/transactions/trades.py,sha256=JtL2jrvIjBVsmB2N5ngw_1UyzK7BbxGK5yuNTCRSfkU,16815
|
|
351
351
|
wbportfolio/static/wbportfolio/css/macro_review.css,sha256=FAVVO8nModxwPXcTKpcfzVxBGPZGJVK1Xn-0dkSfGyc,233
|
|
352
352
|
wbportfolio/static/wbportfolio/markdown/documentation/account_holding_reconciliation.md,sha256=MabxOvOne8s5gl6osDoow6-3ghaXLAYg9THWpvy6G5I,921
|
|
353
353
|
wbportfolio/static/wbportfolio/markdown/documentation/aggregate_asset_position_liquidity.md,sha256=HEgXB7uqmqfty-GBCCXYxrAN-teqmxWuqDLK_liKWVc,1090
|
|
@@ -387,12 +387,12 @@ wbportfolio/tests/models/transactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
387
387
|
wbportfolio/tests/models/transactions/test_claim.py,sha256=NG3BKB-FVcIDgHSJHCjImxgMM3ISVUMl24xUPmEcPec,5570
|
|
388
388
|
wbportfolio/tests/models/transactions/test_fees.py,sha256=tAp18x2wCNQr11LUnLtHNbBDbbX0v1DZnmW7i-cEi5Q,2423
|
|
389
389
|
wbportfolio/tests/models/transactions/test_rebalancing.py,sha256=fZ5tx6kByEGXD6nhapYdvk9HOjYlmjhU2w6KlQJ6QE4,4061
|
|
390
|
-
wbportfolio/tests/models/transactions/test_trade_proposals.py,sha256=
|
|
390
|
+
wbportfolio/tests/models/transactions/test_trade_proposals.py,sha256=ERGDbniHctEnIG5yFLkrZX2Jw1jEew5rWceVKuKskto,18997
|
|
391
391
|
wbportfolio/tests/models/transactions/test_trades.py,sha256=vqvOqUY_uXvBp8YOKR0Wq9ycA2oeeEBhO3dzV7sbXEU,9863
|
|
392
392
|
wbportfolio/tests/pms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
393
393
|
wbportfolio/tests/pms/test_analytics.py,sha256=fAuY1zcXibttFpBh2GhKVyzdYfi1kz_b7SPa9xZQXY0,1086
|
|
394
394
|
wbportfolio/tests/rebalancing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
395
|
-
wbportfolio/tests/rebalancing/test_models.py,sha256=
|
|
395
|
+
wbportfolio/tests/rebalancing/test_models.py,sha256=4rSb05xKH881ft12G0B8ZSeFgJvutKcW_7vFgVQa0DI,7813
|
|
396
396
|
wbportfolio/tests/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
397
397
|
wbportfolio/tests/serializers/test_claims.py,sha256=vQrg73xQXRFEgvx3KI9ivFre_wpBFzdO0p0J13PkvdY,582
|
|
398
398
|
wbportfolio/tests/viewsets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -403,7 +403,7 @@ wbportfolio/tests/viewsets/transactions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
|
|
|
403
403
|
wbportfolio/tests/viewsets/transactions/test_claims.py,sha256=QEZfMAW07dyoZ63t2umSwGOqvaTULfYfbN_F4ZoSAcw,6368
|
|
404
404
|
wbportfolio/viewsets/__init__.py,sha256=3kUaQ66ybvROwejd3bEcSt4XKzfOlPDaeoStMvlz7qY,2294
|
|
405
405
|
wbportfolio/viewsets/adjustments.py,sha256=ugbX4aFRCaD4Yj1hxL-VIPaNI7GF_wt0FrkN6mq1YjU,1524
|
|
406
|
-
wbportfolio/viewsets/assets.py,sha256=
|
|
406
|
+
wbportfolio/viewsets/assets.py,sha256=nMMVh7P531KZ-zpFogIMFKbfvRD3KdG2oIAAHQ42lpc,24634
|
|
407
407
|
wbportfolio/viewsets/assets_and_net_new_money_progression.py,sha256=Jl4vEQP4N2OFL5IGBXoKcj-0qaPviU0I8npvQLw4Io0,4464
|
|
408
408
|
wbportfolio/viewsets/custodians.py,sha256=CTFqkqVP1R3AV7lhdvcdICxB5DfwDYCyikNSI5kbYEo,2322
|
|
409
409
|
wbportfolio/viewsets/esg.py,sha256=27MxxdXQH3Cq_1UEYmcrF7htUOg6i81fUpbVQXAAKJI,6985
|
|
@@ -436,7 +436,7 @@ wbportfolio/viewsets/configs/buttons/products.py,sha256=bHOs2ftfaLbIbJ7E8yFqQqSu
|
|
|
436
436
|
wbportfolio/viewsets/configs/buttons/reconciliations.py,sha256=lw4r22GHpqKPUF1MrB6P9dOkL-FHe5iiBJ0--f8D74E,3145
|
|
437
437
|
wbportfolio/viewsets/configs/buttons/registers.py,sha256=aS89TsYHql83k-NHojOrLDqtBpnpsUUO8x63PiMfrXM,445
|
|
438
438
|
wbportfolio/viewsets/configs/buttons/signals.py,sha256=6sKBQI_eDvZuZR5bUUwvur5R67A3oChAGxPfayWUelE,2739
|
|
439
|
-
wbportfolio/viewsets/configs/buttons/trade_proposals.py,sha256=
|
|
439
|
+
wbportfolio/viewsets/configs/buttons/trade_proposals.py,sha256=Os5jT-aL0jMNkdm2sJpGRZvY4OzreR6Ah7eHex_6zB4,3383
|
|
440
440
|
wbportfolio/viewsets/configs/buttons/trades.py,sha256=bPOqqwdgadSUbU9l5aSirqr5UAMFe2AiFNsXLRsKwn8,2535
|
|
441
441
|
wbportfolio/viewsets/configs/display/__init__.py,sha256=jJqSCCAfw_vjbcsIkzKr39LdAZA810LUBfSH1EA7MAI,2086
|
|
442
442
|
wbportfolio/viewsets/configs/display/adjustments.py,sha256=jIOEc23OCYBguLaZRlZxC916kocYT35ZV9Jsiocs9nk,3334
|
|
@@ -456,8 +456,8 @@ wbportfolio/viewsets/configs/display/rebalancing.py,sha256=yw9X1Nf2-V_KP_mCX4pVK
|
|
|
456
456
|
wbportfolio/viewsets/configs/display/reconciliations.py,sha256=YvMAuwmpX0HExvGsuf5UvcRQxe4eMo1iyNJX68GGC_k,6021
|
|
457
457
|
wbportfolio/viewsets/configs/display/registers.py,sha256=1np75exIk5rfct6UkVN_RnfJ9ozvIkcWJgFV4_4rJns,3182
|
|
458
458
|
wbportfolio/viewsets/configs/display/roles.py,sha256=SFUyCdxSlHZ3NsMrJmpVBSlg-XKGaEFteV89nyLMMAQ,1815
|
|
459
|
-
wbportfolio/viewsets/configs/display/trade_proposals.py,sha256=
|
|
460
|
-
wbportfolio/viewsets/configs/display/trades.py,sha256=
|
|
459
|
+
wbportfolio/viewsets/configs/display/trade_proposals.py,sha256=gM5CuUmNcVtwJHYE9Myg-RR_WmAc6VDJztwUqQ1uZ7g,4335
|
|
460
|
+
wbportfolio/viewsets/configs/display/trades.py,sha256=ZYZ2ceE4Hyw2SpQjW00VDnzGz7Wlu3jjPoK_J2GZLyk,19181
|
|
461
461
|
wbportfolio/viewsets/configs/endpoints/__init__.py,sha256=KR3AsSxl71VAVkYBRVowgs3PZB8vaKa34WHHUvC-2MY,2807
|
|
462
462
|
wbportfolio/viewsets/configs/endpoints/adjustments.py,sha256=KRZLqv4ZeB27d-_s7Qlqj9LI3I9WFJkjc6Mw8agDueE,606
|
|
463
463
|
wbportfolio/viewsets/configs/endpoints/assets.py,sha256=B8W6ATQQfT292TllE4sUDYv5wHUCo99HEqymJuMtFAc,1909
|
|
@@ -517,9 +517,9 @@ wbportfolio/viewsets/transactions/claim.py,sha256=Pb1WftoO-w-ZSTbLRhmQubhy7hgd68
|
|
|
517
517
|
wbportfolio/viewsets/transactions/fees.py,sha256=WT2bWWfgozz4_rpyTKX7dgBBTXD-gu0nlsd2Nk2Zh1Q,7028
|
|
518
518
|
wbportfolio/viewsets/transactions/mixins.py,sha256=WipvJoi5hylkpD0y9VATe30WAcwIHUIroVkK10FYw7k,636
|
|
519
519
|
wbportfolio/viewsets/transactions/rebalancing.py,sha256=6rIrdK0rtKL1afJ-tYfAGdQVTN2MH1kG_yCeVkmyK8k,1263
|
|
520
|
-
wbportfolio/viewsets/transactions/trade_proposals.py,sha256=
|
|
521
|
-
wbportfolio/viewsets/transactions/trades.py,sha256=
|
|
522
|
-
wbportfolio-1.52.
|
|
523
|
-
wbportfolio-1.52.
|
|
524
|
-
wbportfolio-1.52.
|
|
525
|
-
wbportfolio-1.52.
|
|
520
|
+
wbportfolio/viewsets/transactions/trade_proposals.py,sha256=zdb9yqyAE1-hcMmxxN3CYePWoPVWIW3WP3v8agErDeY,6210
|
|
521
|
+
wbportfolio/viewsets/transactions/trades.py,sha256=DcfiJReBnnR6oBgNKZyBYWhXurtxJG_DCzxq5WCionA,22025
|
|
522
|
+
wbportfolio-1.52.6.dist-info/METADATA,sha256=9YI1D7f4Omg3dI2yJs8v_cUOHJ4cdzujpm3l7QfGh38,702
|
|
523
|
+
wbportfolio-1.52.6.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
524
|
+
wbportfolio-1.52.6.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
525
|
+
wbportfolio-1.52.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|