wbportfolio 1.52.0__py2.py3-none-any.whl → 1.59.4__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/admin/__init__.py +3 -1
- wbportfolio/admin/indexes.py +1 -1
- wbportfolio/admin/orders/__init__.py +2 -0
- wbportfolio/admin/orders/order_proposals.py +16 -0
- wbportfolio/admin/orders/orders.py +32 -0
- wbportfolio/admin/portfolio.py +11 -5
- wbportfolio/admin/product_groups.py +1 -1
- wbportfolio/admin/products.py +2 -1
- wbportfolio/admin/{transactions/rebalancing.py → rebalancing.py} +1 -1
- wbportfolio/admin/transactions/__init__.py +0 -2
- wbportfolio/admin/transactions/dividends.py +40 -4
- wbportfolio/admin/transactions/fees.py +24 -14
- wbportfolio/admin/transactions/trades.py +34 -27
- wbportfolio/analysis/claims.py +5 -6
- wbportfolio/api_clients/ubs.py +162 -0
- wbportfolio/constants.py +1 -0
- wbportfolio/contrib/company_portfolio/configs/display.py +22 -10
- wbportfolio/contrib/company_portfolio/configs/previews.py +3 -3
- wbportfolio/contrib/company_portfolio/filters.py +10 -10
- wbportfolio/contrib/company_portfolio/models.py +69 -39
- wbportfolio/contrib/company_portfolio/scripts.py +7 -2
- wbportfolio/contrib/company_portfolio/serializers.py +32 -22
- wbportfolio/contrib/company_portfolio/tasks.py +12 -1
- wbportfolio/contrib/company_portfolio/tests/conftest.py +2 -2
- wbportfolio/defaults/fees/default.py +7 -15
- wbportfolio/factories/__init__.py +2 -2
- wbportfolio/factories/assets.py +1 -1
- wbportfolio/factories/dividends.py +8 -3
- wbportfolio/factories/fees.py +8 -4
- wbportfolio/factories/orders/__init__.py +2 -0
- wbportfolio/factories/orders/order_proposals.py +21 -0
- wbportfolio/factories/orders/orders.py +34 -0
- wbportfolio/factories/portfolios.py +2 -1
- wbportfolio/factories/product_groups.py +3 -3
- wbportfolio/factories/products.py +3 -3
- wbportfolio/factories/rebalancing.py +1 -1
- wbportfolio/factories/trades.py +12 -16
- wbportfolio/filters/assets.py +18 -4
- wbportfolio/filters/orders/__init__.py +2 -0
- wbportfolio/filters/orders/order_proposals.py +55 -0
- wbportfolio/filters/orders/orders.py +11 -0
- wbportfolio/filters/portfolios.py +38 -1
- wbportfolio/filters/positions.py +0 -1
- wbportfolio/filters/transactions/__init__.py +1 -2
- wbportfolio/filters/transactions/fees.py +5 -12
- wbportfolio/filters/transactions/trades.py +16 -8
- wbportfolio/filters/transactions/utils.py +42 -0
- wbportfolio/import_export/backends/ubs/__init__.py +1 -0
- wbportfolio/import_export/backends/ubs/asset_position.py +6 -7
- wbportfolio/import_export/backends/ubs/fees.py +10 -20
- wbportfolio/import_export/backends/ubs/instrument_price.py +6 -6
- wbportfolio/import_export/backends/ubs/trade.py +48 -0
- wbportfolio/import_export/backends/utils.py +0 -17
- wbportfolio/import_export/handlers/asset_position.py +22 -10
- wbportfolio/import_export/handlers/dividend.py +8 -8
- wbportfolio/import_export/handlers/fees.py +13 -23
- wbportfolio/import_export/handlers/orders.py +71 -0
- wbportfolio/import_export/handlers/trade.py +53 -77
- wbportfolio/import_export/parsers/default_mapping.py +1 -1
- wbportfolio/import_export/parsers/jpmorgan/customer_trade.py +2 -2
- wbportfolio/import_export/parsers/jpmorgan/fees.py +4 -4
- wbportfolio/import_export/parsers/jpmorgan/strategy.py +59 -85
- wbportfolio/import_export/parsers/jpmorgan/valuation.py +2 -2
- wbportfolio/import_export/parsers/leonteq/customer_trade.py +5 -5
- wbportfolio/import_export/parsers/leonteq/fees.py +11 -7
- wbportfolio/import_export/parsers/leonteq/trade.py +2 -6
- wbportfolio/import_export/parsers/natixis/d1_fees.py +2 -2
- wbportfolio/import_export/parsers/natixis/dividend.py +4 -9
- wbportfolio/import_export/parsers/natixis/equity.py +22 -4
- wbportfolio/import_export/parsers/natixis/fees.py +7 -9
- wbportfolio/import_export/parsers/natixis/utils.py +13 -19
- wbportfolio/import_export/parsers/sg_lux/customer_trade_pending_slk.py +1 -1
- wbportfolio/import_export/parsers/sg_lux/equity.py +10 -10
- wbportfolio/import_export/parsers/sg_lux/fees.py +2 -2
- wbportfolio/import_export/parsers/sg_lux/perf_fees.py +2 -2
- wbportfolio/import_export/parsers/sg_lux/sylk.py +12 -11
- wbportfolio/import_export/parsers/sg_lux/utils.py +2 -2
- wbportfolio/import_export/parsers/sg_lux/valuation.py +4 -2
- wbportfolio/import_export/parsers/societe_generale/strategy.py +5 -5
- wbportfolio/import_export/parsers/tellco/customer_trade.py +2 -1
- wbportfolio/import_export/parsers/tellco/valuation.py +4 -3
- wbportfolio/import_export/parsers/ubs/api/fees.py +2 -2
- wbportfolio/import_export/parsers/ubs/api/trade.py +39 -0
- wbportfolio/import_export/parsers/ubs/customer_trade.py +7 -5
- wbportfolio/import_export/parsers/ubs/equity.py +3 -2
- wbportfolio/import_export/parsers/ubs/valuation.py +2 -1
- wbportfolio/import_export/parsers/vontobel/customer_trade.py +2 -3
- wbportfolio/import_export/parsers/vontobel/historical_customer_trade.py +0 -1
- wbportfolio/import_export/parsers/vontobel/management_fees.py +12 -20
- wbportfolio/import_export/parsers/vontobel/performance_fees.py +5 -8
- wbportfolio/import_export/parsers/vontobel/valuation_api.py +4 -1
- wbportfolio/import_export/resources/trades.py +3 -3
- wbportfolio/import_export/utils.py +3 -1
- wbportfolio/jinja2/wbportfolio/sql/aum_nnm.sql +2 -2
- wbportfolio/metric/backends/base.py +2 -2
- wbportfolio/migrations/0059_fees_unique_fees.py +1 -1
- wbportfolio/migrations/0077_remove_transaction_currency_and_more.py +622 -0
- wbportfolio/migrations/0078_trade_drift_factor.py +26 -0
- wbportfolio/migrations/0079_alter_trade_drift_factor.py +19 -0
- wbportfolio/migrations/0080_alter_trade_drift_factor_alter_trade_weighting.py +19 -0
- wbportfolio/migrations/0081_alter_trade_drift_factor.py +19 -0
- wbportfolio/migrations/0082_remove_tradeproposal_creator_and_more.py +93 -0
- wbportfolio/migrations/0083_order_alter_trade_options_and_more.py +181 -0
- wbportfolio/migrations/0084_orderproposal_min_order_value.py +25 -0
- wbportfolio/migrations/0085_order_desired_target_weight.py +26 -0
- wbportfolio/migrations/0086_orderproposal_total_cash_weight.py +19 -0
- wbportfolio/migrations/0087_product_order_routing_custodian_adapter.py +94 -0
- wbportfolio/migrations/0088_orderproposal_total_effective_portfolio_contribution.py +19 -0
- wbportfolio/migrations/0089_orderproposal_min_weighting.py +71 -0
- wbportfolio/migrations/0090_dividendtransaction_price_fx_portfolio_and_more.py +44 -0
- wbportfolio/migrations/0091_remove_order_execution_confirmed_and_more.py +32 -0
- wbportfolio/migrations/0092_order_quantization_error_alter_orderproposal_status.py +49 -0
- wbportfolio/migrations/0093_remove_portfolioportfoliothroughmodel_unique_primary_and_more.py +35 -0
- wbportfolio/models/__init__.py +2 -0
- wbportfolio/models/adjustments.py +1 -1
- wbportfolio/models/asset.py +28 -170
- wbportfolio/models/builder.py +323 -0
- wbportfolio/models/custodians.py +3 -3
- wbportfolio/models/exceptions.py +1 -1
- wbportfolio/models/graphs/portfolio.py +1 -1
- wbportfolio/models/graphs/utils.py +11 -11
- wbportfolio/models/mixins/instruments.py +7 -0
- wbportfolio/models/mixins/liquidity_stress_test.py +4 -4
- wbportfolio/models/orders/__init__.py +2 -0
- wbportfolio/models/orders/order_proposals.py +1414 -0
- wbportfolio/models/orders/orders.py +410 -0
- wbportfolio/models/portfolio.py +311 -289
- wbportfolio/models/portfolio_relationship.py +6 -0
- wbportfolio/models/products.py +12 -0
- wbportfolio/models/{transactions/rebalancing.py → rebalancing.py} +40 -27
- wbportfolio/models/roles.py +4 -10
- wbportfolio/models/transactions/__init__.py +0 -4
- wbportfolio/models/transactions/claim.py +7 -6
- wbportfolio/models/transactions/dividends.py +42 -5
- wbportfolio/models/transactions/fees.py +55 -22
- wbportfolio/models/transactions/trades.py +121 -442
- wbportfolio/models/transactions/transactions.py +78 -158
- wbportfolio/models/utils.py +100 -1
- wbportfolio/order_routing/__init__.py +35 -0
- wbportfolio/order_routing/adapters/__init__.py +65 -0
- wbportfolio/order_routing/adapters/ubs.py +195 -0
- wbportfolio/order_routing/router.py +33 -0
- wbportfolio/order_routing/tests/__init__.py +0 -0
- wbportfolio/order_routing/tests/test_router.py +110 -0
- wbportfolio/permissions.py +7 -0
- wbportfolio/pms/analytics/portfolio.py +17 -9
- wbportfolio/pms/analytics/utils.py +9 -0
- wbportfolio/pms/trading/__init__.py +0 -1
- wbportfolio/pms/trading/optimizer.py +61 -0
- wbportfolio/pms/typing.py +198 -63
- wbportfolio/rebalancing/base.py +12 -1
- wbportfolio/rebalancing/decorators.py +1 -1
- wbportfolio/rebalancing/models/composite.py +4 -8
- wbportfolio/rebalancing/models/equally_weighted.py +13 -11
- wbportfolio/rebalancing/models/market_capitalization_weighted.py +21 -14
- wbportfolio/rebalancing/models/model_portfolio.py +14 -18
- wbportfolio/risk_management/backends/__init__.py +1 -0
- wbportfolio/risk_management/backends/controversy_portfolio.py +2 -2
- wbportfolio/risk_management/backends/esg_aggregation_portfolio.py +64 -0
- wbportfolio/risk_management/backends/exposure_portfolio.py +4 -4
- wbportfolio/risk_management/backends/instrument_list_portfolio.py +3 -3
- wbportfolio/risk_management/tests/test_esg_aggregation_portfolio.py +49 -0
- wbportfolio/risk_management/tests/test_exposure_portfolio.py +1 -1
- wbportfolio/risk_management/tests/test_stop_loss_instrument.py +2 -2
- wbportfolio/risk_management/tests/test_stop_loss_portfolio.py +1 -1
- wbportfolio/serializers/__init__.py +1 -0
- wbportfolio/serializers/orders/__init__.py +2 -0
- wbportfolio/serializers/orders/order_proposals.py +115 -0
- wbportfolio/serializers/orders/orders.py +283 -0
- wbportfolio/serializers/portfolios.py +7 -7
- wbportfolio/serializers/positions.py +2 -2
- wbportfolio/serializers/rebalancing.py +1 -1
- wbportfolio/serializers/signals.py +9 -12
- wbportfolio/serializers/transactions/__init__.py +1 -10
- wbportfolio/serializers/transactions/claim.py +2 -2
- wbportfolio/serializers/transactions/dividends.py +37 -9
- wbportfolio/serializers/transactions/fees.py +39 -10
- wbportfolio/serializers/transactions/trades.py +55 -157
- wbportfolio/tasks.py +43 -5
- wbportfolio/tests/analysis/__init__.py +0 -0
- wbportfolio/tests/analysis/test_claims.py +85 -0
- wbportfolio/tests/conftest.py +12 -12
- wbportfolio/tests/models/orders/__init__.py +0 -0
- wbportfolio/tests/models/orders/test_order_proposals.py +1046 -0
- wbportfolio/tests/models/test_assets.py +7 -3
- wbportfolio/tests/models/test_imports.py +9 -13
- wbportfolio/tests/models/test_portfolios.py +102 -95
- wbportfolio/tests/models/test_products.py +11 -0
- wbportfolio/tests/models/test_splits.py +1 -6
- wbportfolio/tests/models/test_utils.py +140 -0
- wbportfolio/tests/models/transactions/test_fees.py +7 -13
- wbportfolio/tests/models/transactions/test_rebalancing.py +5 -5
- wbportfolio/tests/models/transactions/test_trades.py +0 -20
- wbportfolio/tests/pms/test_analytics.py +22 -3
- wbportfolio/tests/rebalancing/test_models.py +51 -57
- wbportfolio/tests/signals.py +10 -20
- wbportfolio/tests/tests.py +3 -1
- wbportfolio/tests/viewsets/test_products.py +1 -0
- wbportfolio/urls.py +10 -13
- wbportfolio/viewsets/__init__.py +9 -4
- wbportfolio/viewsets/assets.py +3 -204
- wbportfolio/viewsets/charts/__init__.py +6 -1
- wbportfolio/viewsets/charts/assets.py +344 -154
- wbportfolio/viewsets/configs/buttons/__init__.py +2 -2
- wbportfolio/viewsets/configs/buttons/assets.py +1 -1
- wbportfolio/viewsets/configs/buttons/mixins.py +4 -4
- wbportfolio/viewsets/configs/buttons/portfolios.py +45 -1
- wbportfolio/viewsets/configs/buttons/products.py +32 -2
- wbportfolio/viewsets/configs/display/__init__.py +2 -5
- wbportfolio/viewsets/configs/display/assets.py +6 -19
- wbportfolio/viewsets/configs/display/fees.py +3 -3
- wbportfolio/viewsets/configs/display/portfolios.py +5 -5
- wbportfolio/viewsets/configs/display/products.py +1 -1
- wbportfolio/viewsets/configs/display/rebalancing.py +2 -2
- wbportfolio/viewsets/configs/display/reconciliations.py +4 -4
- wbportfolio/viewsets/configs/display/trades.py +1 -189
- wbportfolio/viewsets/configs/endpoints/__init__.py +3 -7
- wbportfolio/viewsets/configs/endpoints/fees.py +2 -2
- wbportfolio/viewsets/configs/endpoints/trades.py +0 -41
- wbportfolio/viewsets/configs/menu/__init__.py +1 -1
- wbportfolio/viewsets/configs/menu/orders.py +11 -0
- wbportfolio/viewsets/configs/titles/__init__.py +2 -3
- wbportfolio/viewsets/configs/titles/fees.py +4 -8
- wbportfolio/viewsets/esg.py +3 -5
- wbportfolio/viewsets/mixins.py +5 -1
- wbportfolio/viewsets/orders/__init__.py +6 -0
- wbportfolio/viewsets/orders/configs/__init__.py +4 -0
- wbportfolio/viewsets/orders/configs/buttons/__init__.py +2 -0
- wbportfolio/viewsets/orders/configs/buttons/order_proposals.py +188 -0
- wbportfolio/viewsets/orders/configs/buttons/orders.py +113 -0
- wbportfolio/viewsets/orders/configs/displays/__init__.py +2 -0
- wbportfolio/viewsets/orders/configs/displays/order_proposals.py +157 -0
- wbportfolio/viewsets/orders/configs/displays/orders.py +232 -0
- wbportfolio/viewsets/orders/configs/endpoints/__init__.py +2 -0
- wbportfolio/viewsets/orders/configs/endpoints/order_proposals.py +21 -0
- wbportfolio/viewsets/orders/configs/endpoints/orders.py +28 -0
- wbportfolio/viewsets/orders/configs/titles/__init__.py +0 -0
- wbportfolio/viewsets/orders/configs/titles/orders.py +0 -0
- wbportfolio/viewsets/orders/order_proposals.py +252 -0
- wbportfolio/viewsets/orders/orders.py +277 -0
- wbportfolio/viewsets/portfolios.py +36 -12
- wbportfolio/viewsets/positions.py +3 -2
- wbportfolio/viewsets/products.py +6 -6
- wbportfolio/viewsets/{transactions/rebalancing.py → rebalancing.py} +2 -2
- wbportfolio/viewsets/transactions/__init__.py +3 -14
- wbportfolio/viewsets/transactions/fees.py +22 -22
- wbportfolio/viewsets/transactions/trades.py +1 -180
- {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/METADATA +3 -1
- {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/RECORD +252 -203
- {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/WHEEL +1 -1
- wbportfolio/admin/transactions/transactions.py +0 -38
- wbportfolio/factories/transactions.py +0 -22
- wbportfolio/fdm/tasks.py +0 -13
- wbportfolio/filters/transactions/transactions.py +0 -99
- wbportfolio/models/transactions/expiry.py +0 -7
- wbportfolio/models/transactions/trade_proposals.py +0 -704
- wbportfolio/pms/trading/handler.py +0 -161
- wbportfolio/serializers/transactions/expiry.py +0 -18
- wbportfolio/serializers/transactions/trade_proposals.py +0 -76
- wbportfolio/serializers/transactions/transactions.py +0 -85
- wbportfolio/tests/models/transactions/test_trade_proposals.py +0 -410
- wbportfolio/viewsets/configs/buttons/trade_proposals.py +0 -66
- wbportfolio/viewsets/configs/display/trade_proposals.py +0 -100
- wbportfolio/viewsets/configs/display/transactions.py +0 -55
- wbportfolio/viewsets/configs/endpoints/trade_proposals.py +0 -18
- wbportfolio/viewsets/configs/endpoints/transactions.py +0 -14
- wbportfolio/viewsets/configs/menu/transactions.py +0 -9
- wbportfolio/viewsets/configs/titles/transactions.py +0 -9
- wbportfolio/viewsets/signals.py +0 -43
- wbportfolio/viewsets/transactions/trade_proposals.py +0 -139
- wbportfolio/viewsets/transactions/transactions.py +0 -122
- /wbportfolio/{fdm → api_clients}/__init__.py +0 -0
- {wbportfolio-1.52.0.dist-info → wbportfolio-1.59.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,6 @@ from decimal import Decimal
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import plotly.graph_objects as go
|
|
5
|
-
from django.contrib.messages import error, warning
|
|
6
5
|
from django.db.models import (
|
|
7
6
|
BooleanField,
|
|
8
7
|
Case,
|
|
@@ -16,16 +15,12 @@ from django.db.models import (
|
|
|
16
15
|
When,
|
|
17
16
|
)
|
|
18
17
|
from django.db.models.functions import Coalesce
|
|
19
|
-
from django.shortcuts import get_object_or_404
|
|
20
|
-
from django.utils.functional import cached_property
|
|
21
18
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
22
19
|
from wbcore import viewsets
|
|
23
20
|
from wbcore.contrib.currency.models import CurrencyFXRates
|
|
24
21
|
from wbcore.permissions.permissions import InternalUserPermissionMixin
|
|
25
22
|
from wbcore.utils.strings import format_number
|
|
26
|
-
from wbcore.viewsets.mixins import OrderableMixin
|
|
27
23
|
from wbcrm.models import Account
|
|
28
|
-
from wbfdm.models import Cash
|
|
29
24
|
|
|
30
25
|
from wbportfolio.filters import (
|
|
31
26
|
SubscriptionRedemptionFilterSet,
|
|
@@ -34,15 +29,11 @@ from wbportfolio.filters import (
|
|
|
34
29
|
TradeInstrumentFilterSet,
|
|
35
30
|
TradePortfolioFilter,
|
|
36
31
|
)
|
|
37
|
-
from wbportfolio.
|
|
38
|
-
from wbportfolio.models import Trade, TradeProposal
|
|
32
|
+
from wbportfolio.models import Trade
|
|
39
33
|
from wbportfolio.models.transactions.claim import Claim
|
|
40
34
|
from wbportfolio.serializers import (
|
|
41
|
-
ReadOnlyTradeTradeProposalModelSerializer,
|
|
42
35
|
TradeModelSerializer,
|
|
43
|
-
TradeProposalRepresentationSerializer,
|
|
44
36
|
TradeRepresentationSerializer,
|
|
45
|
-
TradeTradeProposalModelSerializer,
|
|
46
37
|
)
|
|
47
38
|
from wbportfolio.viewsets.configs.titles.trades import (
|
|
48
39
|
CustomerDistributionInstrumentTitleConfig,
|
|
@@ -64,18 +55,10 @@ from ..configs import (
|
|
|
64
55
|
TradePortfolioEndpointConfig,
|
|
65
56
|
TradePortfolioTitleConfig,
|
|
66
57
|
TradeTitleConfig,
|
|
67
|
-
TradeTradeProposalDisplayConfig,
|
|
68
|
-
TradeTradeProposalEndpointConfig,
|
|
69
58
|
)
|
|
70
59
|
from ..mixins import UserPortfolioRequestPermissionMixin
|
|
71
60
|
|
|
72
61
|
|
|
73
|
-
class TradeProposalRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
|
|
74
|
-
IDENTIFIER = "wbportfolio:trade"
|
|
75
|
-
queryset = TradeProposal.objects.all()
|
|
76
|
-
serializer_class = TradeProposalRepresentationSerializer
|
|
77
|
-
|
|
78
|
-
|
|
79
62
|
class TradeRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
|
|
80
63
|
filterset_class = TradeFilter
|
|
81
64
|
|
|
@@ -357,165 +340,3 @@ class CustomerDistributionInstrumentChartViewSet(UserPortfolioRequestPermissionM
|
|
|
357
340
|
)
|
|
358
341
|
.filter(account_name__isnull=False)
|
|
359
342
|
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
class TradeTradeProposalModelViewSet(
|
|
363
|
-
UserPortfolioRequestPermissionMixin, InternalUserPermissionMixin, OrderableMixin, viewsets.ModelViewSet
|
|
364
|
-
):
|
|
365
|
-
IMPORT_ALLOWED = True
|
|
366
|
-
ordering = (
|
|
367
|
-
"trade_proposal",
|
|
368
|
-
"order",
|
|
369
|
-
)
|
|
370
|
-
ordering_fields = (
|
|
371
|
-
"underlying_instrument__name",
|
|
372
|
-
"underlying_instrument_isin",
|
|
373
|
-
"underlying_instrument_ticker",
|
|
374
|
-
"underlying_instrument_refinitiv_identifier_code",
|
|
375
|
-
"underlying_instrument_instrument_type",
|
|
376
|
-
"target_weight",
|
|
377
|
-
"effective_weight",
|
|
378
|
-
"effective_shares",
|
|
379
|
-
"target_shares",
|
|
380
|
-
"shares",
|
|
381
|
-
"weighting",
|
|
382
|
-
)
|
|
383
|
-
IDENTIFIER = "wbportfolio:tradeproposal"
|
|
384
|
-
search_fields = ("underlying_instrument__name",)
|
|
385
|
-
filterset_fields = {"status": ["exact"]}
|
|
386
|
-
queryset = Trade.objects.all()
|
|
387
|
-
|
|
388
|
-
display_config_class = TradeTradeProposalDisplayConfig
|
|
389
|
-
endpoint_config_class = TradeTradeProposalEndpointConfig
|
|
390
|
-
serializer_class = TradeTradeProposalModelSerializer
|
|
391
|
-
|
|
392
|
-
@cached_property
|
|
393
|
-
def trade_proposal(self):
|
|
394
|
-
return get_object_or_404(TradeProposal, pk=self.kwargs["trade_proposal_id"])
|
|
395
|
-
|
|
396
|
-
def has_import_permission(self, request) -> bool: # allow import only on draft trade proposal
|
|
397
|
-
return super().has_import_permission(request) and self.trade_proposal.status == TradeProposal.Status.DRAFT
|
|
398
|
-
|
|
399
|
-
def get_import_resource_kwargs(self):
|
|
400
|
-
resource_kwargs = super().get_import_resource_kwargs()
|
|
401
|
-
resource_kwargs["columns_mapping"] = {"underlying_instrument": "underlying_instrument__isin"}
|
|
402
|
-
return resource_kwargs
|
|
403
|
-
|
|
404
|
-
def get_resource_class(self):
|
|
405
|
-
return TradeProposalTradeResource
|
|
406
|
-
|
|
407
|
-
def get_aggregates(self, queryset, *args, **kwargs):
|
|
408
|
-
agg = {}
|
|
409
|
-
if queryset.exists():
|
|
410
|
-
cash_target_position = self.trade_proposal.get_estimated_target_cash(
|
|
411
|
-
self.trade_proposal.portfolio.currency
|
|
412
|
-
)
|
|
413
|
-
cash_target_cash_weight, cash_target_cash_shares = (
|
|
414
|
-
cash_target_position.weighting,
|
|
415
|
-
cash_target_position.initial_shares,
|
|
416
|
-
)
|
|
417
|
-
extra_existing_cash_components = Cash.objects.filter(
|
|
418
|
-
id__in=self.trade_proposal.trades.filter(underlying_instrument__is_cash=True).values(
|
|
419
|
-
"underlying_instrument"
|
|
420
|
-
)
|
|
421
|
-
).exclude(currency=self.trade_proposal.portfolio.currency)
|
|
422
|
-
|
|
423
|
-
for cash_component in extra_existing_cash_components:
|
|
424
|
-
extra_cash_position = self.trade_proposal.get_estimated_target_cash(cash_component.currency)
|
|
425
|
-
cash_target_cash_weight += extra_cash_position.weighting
|
|
426
|
-
cash_target_cash_shares += extra_cash_position.initial_shares
|
|
427
|
-
noncash_aggregates = queryset.filter(underlying_instrument__is_cash=False).aggregate(
|
|
428
|
-
sum_target_weight=Sum(F("target_weight")),
|
|
429
|
-
sum_effective_weight=Sum(F("effective_weight")),
|
|
430
|
-
sum_target_shares=Sum(F("target_shares")),
|
|
431
|
-
sum_effective_shares=Sum(F("effective_shares")),
|
|
432
|
-
)
|
|
433
|
-
cash_aggregates = queryset.filter(underlying_instrument__is_cash=True).aggregate(
|
|
434
|
-
sum_effective_weight=Sum(F("effective_weight")),
|
|
435
|
-
sum_effective_shares=Sum(F("effective_shares")),
|
|
436
|
-
)
|
|
437
|
-
# weights aggregates
|
|
438
|
-
cash_sum_effective_weight = cash_aggregates["sum_effective_weight"] or Decimal(0)
|
|
439
|
-
noncash_sum_effective_weight = noncash_aggregates["sum_effective_weight"] or Decimal(0)
|
|
440
|
-
noncash_sum_target_weight = noncash_aggregates["sum_target_weight"] or Decimal(0)
|
|
441
|
-
sum_buy_weight = queryset.filter(weighting__gte=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
|
|
442
|
-
sum_sell_weight = queryset.filter(weighting__lt=0).aggregate(s=Sum(F("weighting")))["s"] or Decimal(0)
|
|
443
|
-
|
|
444
|
-
# shares aggregates
|
|
445
|
-
cash_sum_effective_shares = cash_aggregates["sum_effective_shares"] or Decimal(0)
|
|
446
|
-
noncash_sum_effective_shares = noncash_aggregates["sum_effective_shares"] or Decimal(0)
|
|
447
|
-
noncash_sum_target_shares = noncash_aggregates["sum_target_shares"] or Decimal(0)
|
|
448
|
-
sum_buy_shares = queryset.filter(shares__gte=0).aggregate(s=Sum(F("shares")))["s"] or Decimal(0)
|
|
449
|
-
sum_sell_shares = queryset.filter(shares__lt=0).aggregate(s=Sum(F("shares")))["s"] or Decimal(0)
|
|
450
|
-
|
|
451
|
-
agg = {
|
|
452
|
-
"effective_weight": {
|
|
453
|
-
"Cash": format_number(cash_sum_effective_weight, decimal=6),
|
|
454
|
-
"Non-Cash": format_number(noncash_sum_effective_weight, decimal=6),
|
|
455
|
-
"Total": format_number(noncash_sum_effective_weight + cash_sum_effective_weight, decimal=6),
|
|
456
|
-
},
|
|
457
|
-
"target_weight": {
|
|
458
|
-
"Cash": format_number(cash_target_cash_weight, decimal=6),
|
|
459
|
-
"Non-Cash": format_number(noncash_sum_target_weight, decimal=6),
|
|
460
|
-
"Total": format_number(cash_target_cash_weight + noncash_sum_target_weight, decimal=6),
|
|
461
|
-
},
|
|
462
|
-
"effective_shares": {
|
|
463
|
-
"Cash": format_number(cash_sum_effective_shares, decimal=6),
|
|
464
|
-
"Non-Cash": format_number(noncash_sum_effective_shares, decimal=6),
|
|
465
|
-
"Total": format_number(cash_sum_effective_shares + noncash_sum_effective_shares, decimal=6),
|
|
466
|
-
},
|
|
467
|
-
"target_shares": {
|
|
468
|
-
"Cash": format_number(cash_target_cash_shares, decimal=6),
|
|
469
|
-
"Non-Cash": format_number(noncash_sum_target_shares, decimal=6),
|
|
470
|
-
"Total": format_number(cash_target_cash_shares + noncash_sum_target_shares, decimal=6),
|
|
471
|
-
},
|
|
472
|
-
"weighting": {
|
|
473
|
-
"Cash Flow": format_number(cash_sum_effective_weight - cash_target_cash_weight, decimal=6),
|
|
474
|
-
"Buy": format_number(sum_buy_weight, decimal=6),
|
|
475
|
-
"Sell": format_number(sum_sell_weight, decimal=6),
|
|
476
|
-
},
|
|
477
|
-
"shares": {
|
|
478
|
-
"Cash Flow": format_number(cash_sum_effective_shares - cash_target_cash_shares, decimal=6),
|
|
479
|
-
"Buy": format_number(sum_buy_shares, decimal=6),
|
|
480
|
-
"Sell": format_number(sum_sell_shares, decimal=6),
|
|
481
|
-
},
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return agg
|
|
485
|
-
|
|
486
|
-
def get_serializer_class(self):
|
|
487
|
-
if self.trade_proposal.status != TradeProposal.Status.DRAFT:
|
|
488
|
-
return ReadOnlyTradeTradeProposalModelSerializer
|
|
489
|
-
return TradeTradeProposalModelSerializer
|
|
490
|
-
|
|
491
|
-
def add_messages(self, request, queryset=None, paginated_queryset=None, instance=None, initial=False):
|
|
492
|
-
if queryset is not None and queryset.exists() and self.trade_proposal.status != TradeProposal.Status.APPROVED:
|
|
493
|
-
total_target_weight = queryset.aggregate(c=Sum(F("target_weight")))["c"] or Decimal(0)
|
|
494
|
-
if round(total_target_weight, 3) != 1:
|
|
495
|
-
warning(
|
|
496
|
-
request,
|
|
497
|
-
"The total target weight does not equal 1. To avoid automatic cash allocation, please adjust the trade weights to sum up to 1. Otherwise, a cash component will be added when this trade proposal is submitted.",
|
|
498
|
-
)
|
|
499
|
-
if queryset.filter(status=Trade.Status.FAILED):
|
|
500
|
-
error(
|
|
501
|
-
request,
|
|
502
|
-
"Some trades failed preparation. To resolve this, please revert the trade proposal to draft, review and correct the trades, and then resubmit.",
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
def get_queryset(self):
|
|
506
|
-
if self.is_portfolio_manager:
|
|
507
|
-
qs = super().get_queryset().filter(trade_proposal=self.kwargs["trade_proposal_id"]).annotate_base_info()
|
|
508
|
-
else:
|
|
509
|
-
qs = TradeProposal.objects.none()
|
|
510
|
-
return qs.annotate(
|
|
511
|
-
underlying_instrument_isin=F("underlying_instrument__isin"),
|
|
512
|
-
underlying_instrument_ticker=F("underlying_instrument__ticker"),
|
|
513
|
-
underlying_instrument_refinitiv_identifier_code=F("underlying_instrument__refinitiv_identifier_code"),
|
|
514
|
-
underlying_instrument_instrument_type=Case(
|
|
515
|
-
When(
|
|
516
|
-
underlying_instrument__parent__is_security=True,
|
|
517
|
-
then=F("underlying_instrument__parent__instrument_type__short_name"),
|
|
518
|
-
),
|
|
519
|
-
default=F("underlying_instrument__instrument_type__short_name"),
|
|
520
|
-
),
|
|
521
|
-
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wbportfolio
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.59.4
|
|
4
4
|
Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Dist: cryptography==3.4.*
|
|
@@ -19,6 +19,8 @@ Requires-Dist: wbcompliance
|
|
|
19
19
|
Requires-Dist: wbcore
|
|
20
20
|
Requires-Dist: wbcrm
|
|
21
21
|
Requires-Dist: wbfdm
|
|
22
|
+
Requires-Dist: wbmailing
|
|
22
23
|
Requires-Dist: wbnews
|
|
24
|
+
Requires-Dist: wbreport
|
|
23
25
|
Requires-Dist: xlrd==2.*
|
|
24
26
|
Requires-Dist: xlsxwriter==3.*
|