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
wbportfolio/admin/__init__.py
CHANGED
|
@@ -8,5 +8,7 @@ from .product_groups import ProductGroupAdmin
|
|
|
8
8
|
from .portfolio import PortfolioModelAdmin
|
|
9
9
|
from .registers import RegisterModelAdmin
|
|
10
10
|
from .roles import PortfolioRoleAdmin
|
|
11
|
-
from .transactions import DividendAdmin, FeesAdmin, TradeAdmin
|
|
11
|
+
from .transactions import DividendAdmin, FeesAdmin, TradeAdmin
|
|
12
12
|
from .reconciliations import AccountReconciliationAdmin
|
|
13
|
+
from .orders import OrderProposalAdmin
|
|
14
|
+
from .rebalancing import RebalancingModelAdmin, RebalancerAdmin
|
wbportfolio/admin/indexes.py
CHANGED
|
@@ -10,6 +10,6 @@ class IndexAdmin(InstrumentModelAdmin):
|
|
|
10
10
|
("Instrument Information", InstrumentModelAdmin.fieldsets[0][1]),
|
|
11
11
|
(
|
|
12
12
|
"Index Information",
|
|
13
|
-
{"fields": (("net_asset_value_computation_method_path",),)},
|
|
13
|
+
{"fields": (("net_asset_value_computation_method_path", "order_routing_custodian_adapter"),)},
|
|
14
14
|
),
|
|
15
15
|
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from wbportfolio.models import OrderProposal
|
|
4
|
+
|
|
5
|
+
from .orders import OrderTabularInline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@admin.register(OrderProposal)
|
|
9
|
+
class OrderProposalAdmin(admin.ModelAdmin):
|
|
10
|
+
search_fields = ["portfolio__name", "comment"]
|
|
11
|
+
|
|
12
|
+
list_display = ("portfolio", "rebalancing_model", "trade_date", "status")
|
|
13
|
+
autocomplete_fields = ["portfolio", "rebalancing_model"]
|
|
14
|
+
inlines = [OrderTabularInline]
|
|
15
|
+
|
|
16
|
+
raw_id_fields = ["portfolio", "creator", "approver"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from wbportfolio.models.orders import Order
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OrderTabularInline(admin.TabularInline):
|
|
7
|
+
model = Order
|
|
8
|
+
fk_name = "order_proposal"
|
|
9
|
+
|
|
10
|
+
readonly_fields = [
|
|
11
|
+
"_effective_weight",
|
|
12
|
+
"_target_weight",
|
|
13
|
+
"_effective_shares",
|
|
14
|
+
"_target_shares",
|
|
15
|
+
"total_value",
|
|
16
|
+
"total_value_gross",
|
|
17
|
+
"total_value_fx_portfolio",
|
|
18
|
+
"total_value_gross_fx_portfolio",
|
|
19
|
+
"created",
|
|
20
|
+
"updated",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
fields = [
|
|
24
|
+
"underlying_instrument",
|
|
25
|
+
"_effective_weight",
|
|
26
|
+
"_target_weight",
|
|
27
|
+
"weighting",
|
|
28
|
+
"shares",
|
|
29
|
+
"daily_return",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
raw_id_fields = ["underlying_instrument"]
|
wbportfolio/admin/portfolio.py
CHANGED
|
@@ -10,10 +10,6 @@ from wbportfolio.models import (
|
|
|
10
10
|
PortfolioSwingPricing,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
from .portfolio_relationships import (
|
|
14
|
-
PortfolioInstrumentPreferredClassificationThroughInlineModelAdmin,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
13
|
|
|
18
14
|
@admin.register(PortfolioBankAccountThroughModel)
|
|
19
15
|
class PortfolioBankAccountThroughModelModelAdmin(admin.ModelAdmin):
|
|
@@ -64,6 +60,16 @@ class PortfolioModelAdmin(admin.ModelAdmin):
|
|
|
64
60
|
)
|
|
65
61
|
},
|
|
66
62
|
),
|
|
63
|
+
(
|
|
64
|
+
"OMS",
|
|
65
|
+
{
|
|
66
|
+
"fields": (
|
|
67
|
+
"default_order_proposal_min_order_value",
|
|
68
|
+
"default_order_proposal_min_weighting",
|
|
69
|
+
"default_order_proposal_total_cash_weight",
|
|
70
|
+
)
|
|
71
|
+
},
|
|
72
|
+
),
|
|
67
73
|
)
|
|
68
74
|
readonly_fields = ["updated_at"]
|
|
69
75
|
list_display = (
|
|
@@ -78,7 +84,7 @@ class PortfolioModelAdmin(admin.ModelAdmin):
|
|
|
78
84
|
inlines = [
|
|
79
85
|
PortfolioBankAccountThroughModelAdmin,
|
|
80
86
|
InstrumentPortfolioThroughModelAdmin,
|
|
81
|
-
PortfolioInstrumentPreferredClassificationThroughInlineModelAdmin,
|
|
87
|
+
# PortfolioInstrumentPreferredClassificationThroughInlineModelAdmin,
|
|
82
88
|
PortfolioPortfolioThroughModelInlineAdmin,
|
|
83
89
|
]
|
|
84
90
|
raw_id_fields = [
|
|
@@ -30,7 +30,7 @@ class ProductGroupAdmin(InstrumentModelAdmin):
|
|
|
30
30
|
("type", "category", "umbrella"),
|
|
31
31
|
("management_company", "depositary", "transfer_agent", "administrator"),
|
|
32
32
|
("investment_manager", "auditor", "paying_agent"),
|
|
33
|
-
("net_asset_value_computation_method_path", "risk_scale"),
|
|
33
|
+
("net_asset_value_computation_method_path", "order_routing_custodian_adapter", "risk_scale"),
|
|
34
34
|
)
|
|
35
35
|
},
|
|
36
36
|
),
|
wbportfolio/admin/products.py
CHANGED
|
@@ -37,7 +37,7 @@ class ProductAdmin(InstrumentModelAdmin):
|
|
|
37
37
|
{
|
|
38
38
|
"fields": (
|
|
39
39
|
("share_price", "initial_high_water_mark", "bank"),
|
|
40
|
-
("
|
|
40
|
+
("fee_calculation", "net_asset_value_computation_method_path", "order_routing_custodian_adapter"),
|
|
41
41
|
(
|
|
42
42
|
"white_label_customers",
|
|
43
43
|
"default_account",
|
|
@@ -55,6 +55,7 @@ class ProductAdmin(InstrumentModelAdmin):
|
|
|
55
55
|
(
|
|
56
56
|
"jurisdiction",
|
|
57
57
|
"dividend",
|
|
58
|
+
"termsheet",
|
|
58
59
|
),
|
|
59
60
|
("external_webpage", "minimum_subscription", "cut_off_time"),
|
|
60
61
|
)
|
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
2
|
|
|
3
|
-
from wbportfolio.admin.transactions import TransactionModelAdmin
|
|
4
3
|
from wbportfolio.models import DividendTransaction
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
@admin.register(DividendTransaction)
|
|
8
|
-
class DividendAdmin(
|
|
7
|
+
class DividendAdmin(admin.ModelAdmin):
|
|
8
|
+
search_fields = ["portfolio__name", "underlying_instrument__computed_str"]
|
|
9
|
+
list_filter = ("distribution_method", "portfolio")
|
|
10
|
+
list_display = (
|
|
11
|
+
"distribution_method",
|
|
12
|
+
"portfolio",
|
|
13
|
+
"underlying_instrument",
|
|
14
|
+
"value_date",
|
|
15
|
+
"currency",
|
|
16
|
+
"currency_fx_rate",
|
|
17
|
+
"total_value",
|
|
18
|
+
"total_value_fx_portfolio",
|
|
19
|
+
)
|
|
9
20
|
fieldsets = (
|
|
10
|
-
("Transactions Information", TransactionModelAdmin.fieldsets[0][1]),
|
|
11
21
|
(
|
|
12
22
|
"Dividend Information",
|
|
13
|
-
{
|
|
23
|
+
{
|
|
24
|
+
"fields": (
|
|
25
|
+
(
|
|
26
|
+
"portfolio",
|
|
27
|
+
"underlying_instrument",
|
|
28
|
+
"import_source",
|
|
29
|
+
),
|
|
30
|
+
("ex_date", "record_date", "value_date"),
|
|
31
|
+
("currency", "currency_fx_rate"),
|
|
32
|
+
("price", "shares", "retrocession"),
|
|
33
|
+
("total_value", "total_value_gross"),
|
|
34
|
+
("total_value_fx_portfolio", "total_value_gross_fx_portfolio"),
|
|
35
|
+
("created", "updated"),
|
|
36
|
+
("comment",),
|
|
37
|
+
)
|
|
38
|
+
},
|
|
14
39
|
),
|
|
15
40
|
)
|
|
41
|
+
readonly_fields = [
|
|
42
|
+
"total_value",
|
|
43
|
+
"total_value_gross",
|
|
44
|
+
"total_value_fx_portfolio",
|
|
45
|
+
"total_value_gross_fx_portfolio",
|
|
46
|
+
"created",
|
|
47
|
+
"updated",
|
|
48
|
+
]
|
|
49
|
+
autocomplete_fields = ["portfolio", "underlying_instrument", "currency"]
|
|
50
|
+
ordering = ("-value_date",)
|
|
51
|
+
raw_id_fields = ["import_source"]
|
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
|
-
from django.db.models import Prefetch
|
|
3
2
|
|
|
4
|
-
from wbportfolio.
|
|
5
|
-
from wbportfolio.models import Fees, Portfolio
|
|
3
|
+
from wbportfolio.models import Fees
|
|
6
4
|
from wbportfolio.models.transactions.fees import FeeCalculation
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
@admin.register(Fees)
|
|
10
|
-
class FeesAdmin(
|
|
11
|
-
list_filter = [
|
|
12
|
-
list_display = [
|
|
8
|
+
class FeesAdmin(admin.ModelAdmin):
|
|
9
|
+
list_filter = ["product"]
|
|
10
|
+
list_display = [
|
|
11
|
+
"transaction_subtype",
|
|
12
|
+
"fee_date",
|
|
13
|
+
"product",
|
|
14
|
+
"currency",
|
|
15
|
+
"total_value",
|
|
16
|
+
]
|
|
13
17
|
fieldsets = (
|
|
14
|
-
(
|
|
15
|
-
|
|
18
|
+
(
|
|
19
|
+
"Fees Information",
|
|
20
|
+
{
|
|
21
|
+
"fields": (
|
|
22
|
+
("transaction_subtype", "calculated", "fee_date", "product"),
|
|
23
|
+
("currency", "currency_fx_rate"),
|
|
24
|
+
("total_value", "total_value_fx_portfolio"),
|
|
25
|
+
("created", "updated"),
|
|
26
|
+
)
|
|
27
|
+
},
|
|
28
|
+
),
|
|
16
29
|
)
|
|
17
|
-
autocomplete_fields = [
|
|
30
|
+
autocomplete_fields = ["currency", "product"]
|
|
18
31
|
|
|
19
32
|
def get_queryset(self, request):
|
|
20
|
-
return (
|
|
21
|
-
super()
|
|
22
|
-
.get_queryset(request)
|
|
23
|
-
.prefetch_related(Prefetch("portfolio", queryset=Portfolio.objects.all().only("id", "name")))
|
|
24
|
-
)
|
|
33
|
+
return super().get_queryset(request).select_related("product", "currency")
|
|
25
34
|
|
|
26
35
|
def calculate(self, request, queryset):
|
|
27
36
|
for fees in queryset:
|
|
@@ -29,6 +38,7 @@ class FeesAdmin(TransactionModelAdmin):
|
|
|
29
38
|
|
|
30
39
|
calculate.short_description = "Recalculate selected Fees"
|
|
31
40
|
actions = [calculate]
|
|
41
|
+
readonly_fields = ["total_value_fx_portfolio", "created", "updated"]
|
|
32
42
|
|
|
33
43
|
|
|
34
44
|
@admin.register(FeeCalculation)
|
|
@@ -1,50 +1,57 @@
|
|
|
1
1
|
from django.contrib import admin
|
|
2
2
|
|
|
3
|
-
from wbportfolio.
|
|
4
|
-
from wbportfolio.models import Trade, TradeProposal
|
|
3
|
+
from wbportfolio.models import Trade
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
@admin.register(Trade)
|
|
8
|
-
class TradeAdmin(
|
|
9
|
-
search_fields = [
|
|
10
|
-
list_filter = (
|
|
7
|
+
class TradeAdmin(admin.ModelAdmin):
|
|
8
|
+
search_fields = ["portfolio__name", "underlying_instrument__computed_str", "bank"]
|
|
9
|
+
list_filter = ("portfolio", "pending")
|
|
11
10
|
list_display = (
|
|
12
11
|
"transaction_subtype",
|
|
13
|
-
"
|
|
12
|
+
"transaction_date",
|
|
13
|
+
"underlying_instrument",
|
|
14
|
+
"portfolio",
|
|
14
15
|
"shares",
|
|
15
16
|
"price",
|
|
17
|
+
"total_value",
|
|
16
18
|
"pending",
|
|
17
19
|
"marked_for_deletion",
|
|
18
20
|
"exclude_from_history",
|
|
19
|
-
|
|
21
|
+
"import_source",
|
|
20
22
|
)
|
|
23
|
+
|
|
21
24
|
readonly_fields = [
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
25
|
+
"total_value",
|
|
26
|
+
"total_value_gross",
|
|
27
|
+
"total_value_fx_portfolio",
|
|
28
|
+
"total_value_gross_fx_portfolio",
|
|
29
|
+
"created",
|
|
30
|
+
"updated",
|
|
26
31
|
]
|
|
27
32
|
fieldsets = (
|
|
28
|
-
("Transaction Information", TransactionModelAdmin.fieldsets[0][1]),
|
|
29
33
|
(
|
|
30
|
-
"
|
|
34
|
+
"Transaction Information",
|
|
31
35
|
{
|
|
32
36
|
"fields": (
|
|
33
|
-
("transaction_subtype",
|
|
34
|
-
("
|
|
35
|
-
(
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
("transaction_subtype",),
|
|
38
|
+
("pending", "marked_for_deletion", "exclude_from_history"),
|
|
39
|
+
(
|
|
40
|
+
"portfolio",
|
|
41
|
+
"underlying_instrument",
|
|
42
|
+
"import_source",
|
|
43
|
+
),
|
|
44
|
+
("transaction_date", "book_date", "value_date"),
|
|
45
|
+
("price", "currency", "currency_fx_rate"),
|
|
46
|
+
("total_value", "total_value_gross"),
|
|
47
|
+
("total_value_fx_portfolio", "total_value_gross_fx_portfolio"),
|
|
48
|
+
("register", "custodian", "bank", "external_id", "external_id_alternative"),
|
|
49
|
+
("created", "updated"),
|
|
50
|
+
("comment",),
|
|
38
51
|
)
|
|
39
52
|
},
|
|
40
53
|
),
|
|
41
54
|
)
|
|
42
|
-
autocomplete_fields = [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@admin.register(TradeProposal)
|
|
46
|
-
class TradeProposalAdmin(admin.ModelAdmin):
|
|
47
|
-
search_fields = ["portfolio__name", "comment"]
|
|
48
|
-
|
|
49
|
-
list_display = ("portfolio", "rebalancing_model", "trade_date", "status")
|
|
50
|
-
autocomplete_fields = ["portfolio", "rebalancing_model"]
|
|
55
|
+
autocomplete_fields = ["portfolio", "underlying_instrument", "currency", "register", "custodian"]
|
|
56
|
+
ordering = ("-transaction_date",)
|
|
57
|
+
raw_id_fields = ["import_source", "underlying_instrument", "portfolio", "register", "custodian"]
|
wbportfolio/analysis/claims.py
CHANGED
|
@@ -58,13 +58,12 @@ class ConsolidatedTradeSummary:
|
|
|
58
58
|
self.pivot_label = pivot_label
|
|
59
59
|
self.queryset = self.queryset.annotate(
|
|
60
60
|
internal_trade=F("trade__marked_as_internal"),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
),
|
|
61
|
+
valid_date=Greatest("trade__transaction_date", "date"),
|
|
62
|
+
date_considered=ExpressionWrapper(F("valid_date") + 1, output_field=DateField()),
|
|
64
63
|
net_value=InstrumentPrice.subquery_closest_value(
|
|
65
|
-
"net_value", date_name="
|
|
64
|
+
"net_value", date_name="valid_date", instrument_pk_name="product__pk", date_lookup="exact"
|
|
66
65
|
),
|
|
67
|
-
fx_rate=CurrencyFXRates.get_fx_rates_subquery("
|
|
66
|
+
fx_rate=CurrencyFXRates.get_fx_rates_subquery("valid_date", lookup_expr="exact"),
|
|
68
67
|
aum=ExpressionWrapper(F("fx_rate") * F("net_value") * F("shares"), output_field=DecimalField()),
|
|
69
68
|
)
|
|
70
69
|
self.queryset = Account.annotate_root_account_info(self.queryset)
|
|
@@ -158,8 +157,8 @@ class ConsolidatedTradeSummary:
|
|
|
158
157
|
df = df.fillna(0)
|
|
159
158
|
# Prepare dataframe
|
|
160
159
|
df["sum_aum_start"] = df.sum_shares_start * df.price_start
|
|
161
|
-
df["sum_aum_end"] = df.sum_shares_end * df.price_end
|
|
162
160
|
|
|
161
|
+
df["sum_aum_end"] = df.sum_shares_end * df.price_end
|
|
163
162
|
# Sanitize dataframe
|
|
164
163
|
df = df.drop(
|
|
165
164
|
columns=df.columns.difference(["id", "sum_shares_start", "sum_shares_end", "sum_aum_start", "sum_aum_end"])
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from datetime import date, datetime, timedelta
|
|
2
|
+
from json import JSONDecodeError
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
from requests import HTTPError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UBSNeoAPIClient:
|
|
11
|
+
BASE_URL = "https://neo.ubs.com/api"
|
|
12
|
+
SUCCESS_VALUE = "SUCCESS"
|
|
13
|
+
VIRTUAL_AMC_ISIN = "TEST_API_001"
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
initial_jwt_token: str,
|
|
18
|
+
jwt_token_expiry_timestamp: datetime | None = None,
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
:param service_account_id: Identifier for your UBS service account (for reference).
|
|
22
|
+
:param initial_jwt_token: JWT token string initially provided to authenticate API calls.
|
|
23
|
+
:param jwt_token_expiry_timestamp: UNIX timestamp when the token expires - to manage renewal.
|
|
24
|
+
"""
|
|
25
|
+
self.jwt_token = initial_jwt_token
|
|
26
|
+
self.jwt_token_expiry = (
|
|
27
|
+
jwt_token_expiry_timestamp if jwt_token_expiry_timestamp else timezone.now() + timedelta(days=1)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def _is_token_expired(self) -> bool:
|
|
31
|
+
"""Check if the current token is expired or near expiry (e.g. within 5 minutes)."""
|
|
32
|
+
return timezone.now() > self.jwt_token_expiry - timedelta(minutes=5)
|
|
33
|
+
|
|
34
|
+
def _renew_token(self):
|
|
35
|
+
"""
|
|
36
|
+
Placeholder: Implement token renewal logic here.
|
|
37
|
+
This usually involves interacting with UBS Neo application or token service
|
|
38
|
+
before expiry to obtain a new token. This must be customized per your process.
|
|
39
|
+
"""
|
|
40
|
+
raise ValueError("Token has expired. Please go to https://neo.ubs.com/ and renew it.")
|
|
41
|
+
|
|
42
|
+
def _get_headers(self) -> dict[str, str]:
|
|
43
|
+
"""Prepare HTTP headers including Authorization bearer token."""
|
|
44
|
+
if self._is_token_expired():
|
|
45
|
+
self._renew_token()
|
|
46
|
+
return {"Authorization": f"Bearer {self.jwt_token}", "Content-Type": "application/json"}
|
|
47
|
+
|
|
48
|
+
def _get_json(self, response) -> dict:
|
|
49
|
+
try:
|
|
50
|
+
return response.json()
|
|
51
|
+
except JSONDecodeError:
|
|
52
|
+
return dict()
|
|
53
|
+
|
|
54
|
+
def _validate_isin(self, isin: str, test: bool = False) -> str:
|
|
55
|
+
# ensure the given isin can be used for rebalancing (e.g. debug or dev mode)
|
|
56
|
+
if test or settings.DEBUG:
|
|
57
|
+
return self.VIRTUAL_AMC_ISIN
|
|
58
|
+
return isin
|
|
59
|
+
|
|
60
|
+
def _raise_for_status(self, response):
|
|
61
|
+
try:
|
|
62
|
+
response.raise_for_status()
|
|
63
|
+
except HTTPError as e:
|
|
64
|
+
json_response = response.json()
|
|
65
|
+
raise HTTPError(json_response.get("errors", json_response.get("message"))) from e
|
|
66
|
+
|
|
67
|
+
def get_rebalance_service_status(self) -> dict:
|
|
68
|
+
"""Check API connection status."""
|
|
69
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/status"
|
|
70
|
+
response = requests.get(url, headers=self._get_headers(), timeout=10)
|
|
71
|
+
self._raise_for_status(response)
|
|
72
|
+
return self._get_json(response)
|
|
73
|
+
|
|
74
|
+
def get_rebalance_status_for_isin(self, isin: str) -> dict:
|
|
75
|
+
"""Check certificate accessibility and workflow status for given ISIN."""
|
|
76
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/status/{isin}"
|
|
77
|
+
response = requests.get(url, headers=self._get_headers(), timeout=10)
|
|
78
|
+
self._raise_for_status(response)
|
|
79
|
+
return self._get_json(response)
|
|
80
|
+
|
|
81
|
+
def submit_rebalance(self, isin: str, items: list[dict[str, str]], test: bool = False) -> dict:
|
|
82
|
+
"""
|
|
83
|
+
Submit a rebalance request.
|
|
84
|
+
:param isin: Certificate ISIN string.
|
|
85
|
+
:param orders: List of dto representing order instructions.
|
|
86
|
+
:param test: If True, submits to test endpoint to validate syntax only (no persistence).
|
|
87
|
+
"""
|
|
88
|
+
isin = self._validate_isin(isin, test=test)
|
|
89
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/submit/{isin}"
|
|
90
|
+
payload = {"items": items}
|
|
91
|
+
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=60)
|
|
92
|
+
self._raise_for_status(response)
|
|
93
|
+
return self._get_json(response)
|
|
94
|
+
|
|
95
|
+
def save_draft(self, isin: str, items: list[dict[str, str]]) -> dict:
|
|
96
|
+
"""Save a rebalance draft."""
|
|
97
|
+
isin = self._validate_isin(isin)
|
|
98
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/savedraft/{isin}"
|
|
99
|
+
payload = {"items": items}
|
|
100
|
+
response = requests.post(url, json=payload, headers=self._get_headers(), timeout=60)
|
|
101
|
+
self._raise_for_status(response)
|
|
102
|
+
return self._get_json(response)
|
|
103
|
+
|
|
104
|
+
def cancel_rebalance(self, isin: str) -> dict:
|
|
105
|
+
"""Cancel a rebalance request."""
|
|
106
|
+
isin = self._validate_isin(isin)
|
|
107
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/cancel/{isin}"
|
|
108
|
+
response = requests.delete(url, headers=self._get_headers(), timeout=60)
|
|
109
|
+
self._raise_for_status(response)
|
|
110
|
+
return self._get_json(response)
|
|
111
|
+
|
|
112
|
+
def get_current_rebalance_request(self, isin: str) -> dict:
|
|
113
|
+
"""Fetch the current rebalance request for a certificate."""
|
|
114
|
+
url = f"{self.BASE_URL}/ged-amc/external/rebalance/v1/currentRebalanceRequest/{isin}"
|
|
115
|
+
response = requests.get(url, headers=self._get_headers(), timeout=60)
|
|
116
|
+
self._raise_for_status(response)
|
|
117
|
+
return self._get_json(response)
|
|
118
|
+
|
|
119
|
+
def get_rebalance_reports(self, isin: str, from_date: date, to_date: date) -> dict:
|
|
120
|
+
"""Fetch the current rebalance request for a certificate."""
|
|
121
|
+
url = f"{self.BASE_URL}/ged-amc/external/report/v1/rebalance/{isin}"
|
|
122
|
+
response = requests.get(
|
|
123
|
+
url,
|
|
124
|
+
headers=self._get_headers(),
|
|
125
|
+
timeout=60,
|
|
126
|
+
params={"fromDate": from_date.strftime("%Y-%m-%d"), "toDate": to_date.strftime("%Y-%m-%d")},
|
|
127
|
+
)
|
|
128
|
+
self._raise_for_status(response)
|
|
129
|
+
return self._get_json(response)
|
|
130
|
+
|
|
131
|
+
def get_portfolio_at_date(self, isin: str, val_date: date) -> dict:
|
|
132
|
+
url = f"https://neo.ubs.com/api/ged-amc/external/report/v1/valuation/{isin}/{val_date:%Y-%m-%d}"
|
|
133
|
+
response = requests.get(url, headers=self._get_headers(), timeout=10)
|
|
134
|
+
self._raise_for_status(response)
|
|
135
|
+
return self._get_json(response)
|
|
136
|
+
|
|
137
|
+
def get_management_fees(self, isin: str, from_date: date, to_date: date) -> dict:
|
|
138
|
+
url = f"https://neo.ubs.com/api/ged-amc/external/fee/v1/management/{isin}"
|
|
139
|
+
response = requests.get(
|
|
140
|
+
url,
|
|
141
|
+
headers=self._get_headers(),
|
|
142
|
+
params={"fromDate": from_date.strftime("%Y-%m-%d"), "toDate": to_date.strftime("%Y-%m-%d")},
|
|
143
|
+
timeout=30,
|
|
144
|
+
)
|
|
145
|
+
self._raise_for_status(response)
|
|
146
|
+
return self._get_json(response)
|
|
147
|
+
|
|
148
|
+
def get_performance_fees(self, isin: str, from_date: date, to_date: date) -> dict:
|
|
149
|
+
url = f"https://neo.ubs.com/api/ged-amc/external/fee/v1/performance/{isin}"
|
|
150
|
+
response = requests.get(
|
|
151
|
+
url,
|
|
152
|
+
headers=self._get_headers(),
|
|
153
|
+
params={"fromDate": from_date.strftime("%Y-%m-%d"), "toDate": to_date.strftime("%Y-%m-%d")},
|
|
154
|
+
timeout=30,
|
|
155
|
+
)
|
|
156
|
+
self._raise_for_status(response)
|
|
157
|
+
return self._get_json(response)
|
|
158
|
+
|
|
159
|
+
def validate_response(self, response: dict) -> dict:
|
|
160
|
+
if response.get("status", "") == self.SUCCESS_VALUE:
|
|
161
|
+
return response
|
|
162
|
+
return dict()
|
wbportfolio/constants.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
EQUITY_TYPE_KEYS = ["american_depository_receipt", "equity"] # TODO might want to move this into a dynamic preference
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
3
|
from django.utils.translation import gettext as _
|
|
4
|
-
from wbcore.contrib.directory.viewsets.display.entries import CompanyModelDisplay as
|
|
5
|
-
from wbcore.contrib.directory.viewsets.display.entries import PersonModelDisplay as
|
|
4
|
+
from wbcore.contrib.directory.viewsets.display.entries import CompanyModelDisplay as BaseCompanyModelDisplay
|
|
5
|
+
from wbcore.contrib.directory.viewsets.display.entries import PersonModelDisplay as BasePersonModelDisplay
|
|
6
6
|
from wbcore.metadata.configs import display as dp
|
|
7
7
|
from wbcore.metadata.configs.display.instance_display import (
|
|
8
8
|
Inline,
|
|
@@ -62,14 +62,20 @@ AUM_FIELDS = Section(
|
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
class CompanyModelDisplay(
|
|
65
|
+
class CompanyModelDisplay(BaseCompanyModelDisplay):
|
|
66
66
|
def get_list_display(self) -> Optional[dp.ListDisplay]:
|
|
67
67
|
list_display = super().get_list_display()
|
|
68
68
|
list_display.fields = (
|
|
69
69
|
*list_display.fields[:5],
|
|
70
|
-
dp.Field(
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
dp.Field(
|
|
71
|
+
key=None,
|
|
72
|
+
label=_("AUM"),
|
|
73
|
+
children=[
|
|
74
|
+
dp.Field(key="invested_assets_under_management_usd", label="AUM Invested", width=100),
|
|
75
|
+
dp.Field(key="asset_under_management", label="AUM", width=100),
|
|
76
|
+
dp.Field(key="potential", label="Potential", width=100),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
73
79
|
*list_display.fields[5:],
|
|
74
80
|
)
|
|
75
81
|
return list_display
|
|
@@ -134,15 +140,21 @@ class CompanyModelDisplay(CMD):
|
|
|
134
140
|
return instance_display
|
|
135
141
|
|
|
136
142
|
|
|
137
|
-
class PersonModelDisplay(
|
|
143
|
+
class PersonModelDisplay(BasePersonModelDisplay):
|
|
138
144
|
def get_list_display(self) -> Optional[dp.ListDisplay]:
|
|
139
145
|
list_display = super().get_list_display()
|
|
140
146
|
|
|
141
147
|
list_display.fields = (
|
|
142
148
|
*list_display.fields[:7],
|
|
143
|
-
dp.Field(
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
dp.Field(
|
|
150
|
+
key=None,
|
|
151
|
+
label=_("AUM"),
|
|
152
|
+
children=[
|
|
153
|
+
dp.Field(key="invested_assets_under_management_usd", label="AUM Invested", width=100),
|
|
154
|
+
dp.Field(key="asset_under_management", label="AUM", width=100),
|
|
155
|
+
dp.Field(key="potential", label="Potential", width=100),
|
|
156
|
+
],
|
|
157
|
+
),
|
|
146
158
|
*list_display.fields[7:],
|
|
147
159
|
)
|
|
148
160
|
return list_display
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from contextlib import suppress
|
|
2
|
+
|
|
1
3
|
from wbcore.contrib.directory.viewsets.previews import EntryPreviewConfig
|
|
2
4
|
from wbcore.contrib.icons import WBIcon
|
|
3
5
|
from wbcore.metadata.configs import buttons as bt
|
|
@@ -17,12 +19,10 @@ class CompanyPreviewConfig(EntryPreviewConfig):
|
|
|
17
19
|
["asset_under_management", "invested_assets_under_management_usd"],
|
|
18
20
|
[repeat_field(2, "potential")],
|
|
19
21
|
]
|
|
20
|
-
|
|
22
|
+
with suppress(Exception):
|
|
21
23
|
entry = self.view.get_object()
|
|
22
24
|
if entry.profile_image:
|
|
23
25
|
fields.insert(0, [repeat_field(2, "profile_image")])
|
|
24
|
-
except Exception:
|
|
25
|
-
pass
|
|
26
26
|
|
|
27
27
|
return create_simple_display(fields)
|
|
28
28
|
|