wbfdm 2.2.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wbfdm might be problematic. Click here for more details.
- wbfdm/__init__.py +2 -0
- wbfdm/admin/__init__.py +42 -0
- wbfdm/admin/classifications.py +39 -0
- wbfdm/admin/esg.py +23 -0
- wbfdm/admin/exchanges.py +53 -0
- wbfdm/admin/instrument_lists.py +23 -0
- wbfdm/admin/instrument_prices.py +62 -0
- wbfdm/admin/instrument_requests.py +33 -0
- wbfdm/admin/instruments.py +117 -0
- wbfdm/admin/instruments_relationships.py +25 -0
- wbfdm/admin/options.py +101 -0
- wbfdm/analysis/__init__.py +2 -0
- wbfdm/analysis/esg/__init__.py +0 -0
- wbfdm/analysis/esg/enums.py +82 -0
- wbfdm/analysis/esg/esg_analysis.py +217 -0
- wbfdm/analysis/esg/utils.py +13 -0
- wbfdm/analysis/financial_analysis/__init__.py +1 -0
- wbfdm/analysis/financial_analysis/financial_metric_analysis.py +88 -0
- wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +125 -0
- wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +271 -0
- wbfdm/analysis/financial_analysis/statement_with_estimates.py +558 -0
- wbfdm/analysis/financial_analysis/utils.py +316 -0
- wbfdm/analysis/technical_analysis/__init__.py +1 -0
- wbfdm/analysis/technical_analysis/technical_analysis.py +138 -0
- wbfdm/analysis/technical_analysis/traces.py +165 -0
- wbfdm/analysis/utils.py +32 -0
- wbfdm/apps.py +14 -0
- wbfdm/contrib/__init__.py +0 -0
- wbfdm/contrib/dsws/__init__.py +0 -0
- wbfdm/contrib/dsws/client.py +285 -0
- wbfdm/contrib/internal/__init__.py +0 -0
- wbfdm/contrib/internal/dataloaders/__init__.py +0 -0
- wbfdm/contrib/internal/dataloaders/market_data.py +87 -0
- wbfdm/contrib/metric/__init__.py +0 -0
- wbfdm/contrib/metric/admin/__init__.py +2 -0
- wbfdm/contrib/metric/admin/instruments.py +12 -0
- wbfdm/contrib/metric/admin/metrics.py +43 -0
- wbfdm/contrib/metric/apps.py +10 -0
- wbfdm/contrib/metric/backends/__init__.py +2 -0
- wbfdm/contrib/metric/backends/base.py +159 -0
- wbfdm/contrib/metric/backends/performances.py +265 -0
- wbfdm/contrib/metric/backends/statistics.py +182 -0
- wbfdm/contrib/metric/decorators.py +14 -0
- wbfdm/contrib/metric/dispatch.py +23 -0
- wbfdm/contrib/metric/dto.py +88 -0
- wbfdm/contrib/metric/exceptions.py +6 -0
- wbfdm/contrib/metric/factories.py +33 -0
- wbfdm/contrib/metric/filters.py +28 -0
- wbfdm/contrib/metric/migrations/0001_initial.py +88 -0
- wbfdm/contrib/metric/migrations/0002_remove_instrumentmetric_unique_instrument_metric_and_more.py +26 -0
- wbfdm/contrib/metric/migrations/__init__.py +0 -0
- wbfdm/contrib/metric/models.py +180 -0
- wbfdm/contrib/metric/orchestrators.py +94 -0
- wbfdm/contrib/metric/registry.py +80 -0
- wbfdm/contrib/metric/serializers.py +44 -0
- wbfdm/contrib/metric/tasks.py +27 -0
- wbfdm/contrib/metric/tests/__init__.py +0 -0
- wbfdm/contrib/metric/tests/backends/__init__.py +0 -0
- wbfdm/contrib/metric/tests/backends/test_performances.py +152 -0
- wbfdm/contrib/metric/tests/backends/test_statistics.py +48 -0
- wbfdm/contrib/metric/tests/conftest.py +92 -0
- wbfdm/contrib/metric/tests/test_dto.py +73 -0
- wbfdm/contrib/metric/tests/test_models.py +72 -0
- wbfdm/contrib/metric/tests/test_tasks.py +24 -0
- wbfdm/contrib/metric/tests/test_viewsets.py +79 -0
- wbfdm/contrib/metric/urls.py +19 -0
- wbfdm/contrib/metric/viewsets/__init__.py +1 -0
- wbfdm/contrib/metric/viewsets/configs/__init__.py +1 -0
- wbfdm/contrib/metric/viewsets/configs/display.py +92 -0
- wbfdm/contrib/metric/viewsets/configs/menus.py +11 -0
- wbfdm/contrib/metric/viewsets/configs/utils.py +137 -0
- wbfdm/contrib/metric/viewsets/mixins.py +245 -0
- wbfdm/contrib/metric/viewsets/viewsets.py +40 -0
- wbfdm/contrib/msci/__init__.py +0 -0
- wbfdm/contrib/msci/client.py +92 -0
- wbfdm/contrib/msci/dataloaders/__init__.py +0 -0
- wbfdm/contrib/msci/dataloaders/esg.py +87 -0
- wbfdm/contrib/msci/dataloaders/esg_controversies.py +81 -0
- wbfdm/contrib/msci/sync.py +58 -0
- wbfdm/contrib/msci/tests/__init__.py +0 -0
- wbfdm/contrib/msci/tests/conftest.py +1 -0
- wbfdm/contrib/msci/tests/test_client.py +70 -0
- wbfdm/contrib/qa/__init__.py +0 -0
- wbfdm/contrib/qa/apps.py +22 -0
- wbfdm/contrib/qa/database_routers.py +25 -0
- wbfdm/contrib/qa/dataloaders/__init__.py +0 -0
- wbfdm/contrib/qa/dataloaders/adjustments.py +56 -0
- wbfdm/contrib/qa/dataloaders/corporate_actions.py +59 -0
- wbfdm/contrib/qa/dataloaders/financials.py +83 -0
- wbfdm/contrib/qa/dataloaders/market_data.py +117 -0
- wbfdm/contrib/qa/dataloaders/officers.py +59 -0
- wbfdm/contrib/qa/dataloaders/reporting_dates.py +67 -0
- wbfdm/contrib/qa/dataloaders/statements.py +267 -0
- wbfdm/contrib/qa/tasks.py +0 -0
- wbfdm/dataloaders/__init__.py +0 -0
- wbfdm/dataloaders/cache.py +129 -0
- wbfdm/dataloaders/protocols.py +112 -0
- wbfdm/dataloaders/proxies.py +201 -0
- wbfdm/dataloaders/types.py +209 -0
- wbfdm/dynamic_preferences_registry.py +45 -0
- wbfdm/enums.py +657 -0
- wbfdm/factories/__init__.py +13 -0
- wbfdm/factories/classifications.py +56 -0
- wbfdm/factories/controversies.py +27 -0
- wbfdm/factories/exchanges.py +21 -0
- wbfdm/factories/instrument_list.py +22 -0
- wbfdm/factories/instrument_prices.py +79 -0
- wbfdm/factories/instruments.py +63 -0
- wbfdm/factories/instruments_relationships.py +31 -0
- wbfdm/factories/options.py +66 -0
- wbfdm/figures/__init__.py +1 -0
- wbfdm/figures/financials/__init__.py +1 -0
- wbfdm/figures/financials/financial_analysis_charts.py +469 -0
- wbfdm/figures/financials/financials_charts.py +711 -0
- wbfdm/filters/__init__.py +31 -0
- wbfdm/filters/classifications.py +100 -0
- wbfdm/filters/exchanges.py +22 -0
- wbfdm/filters/financials.py +95 -0
- wbfdm/filters/financials_analysis.py +119 -0
- wbfdm/filters/instrument_prices.py +112 -0
- wbfdm/filters/instruments.py +198 -0
- wbfdm/filters/utils.py +44 -0
- wbfdm/import_export/__init__.py +0 -0
- wbfdm/import_export/backends/__init__.py +0 -0
- wbfdm/import_export/backends/cbinsights/__init__.py +2 -0
- wbfdm/import_export/backends/cbinsights/deals.py +44 -0
- wbfdm/import_export/backends/cbinsights/equities.py +41 -0
- wbfdm/import_export/backends/cbinsights/mixin.py +15 -0
- wbfdm/import_export/backends/cbinsights/utils/__init__.py +0 -0
- wbfdm/import_export/backends/cbinsights/utils/classifications.py +4150 -0
- wbfdm/import_export/backends/cbinsights/utils/client.py +217 -0
- wbfdm/import_export/backends/refinitiv/__init__.py +5 -0
- wbfdm/import_export/backends/refinitiv/daily_fundamental.py +36 -0
- wbfdm/import_export/backends/refinitiv/fiscal_period.py +63 -0
- wbfdm/import_export/backends/refinitiv/forecast.py +178 -0
- wbfdm/import_export/backends/refinitiv/fundamental.py +103 -0
- wbfdm/import_export/backends/refinitiv/geographic_segment.py +32 -0
- wbfdm/import_export/backends/refinitiv/instrument.py +55 -0
- wbfdm/import_export/backends/refinitiv/instrument_price.py +77 -0
- wbfdm/import_export/backends/refinitiv/mixin.py +29 -0
- wbfdm/import_export/backends/refinitiv/utils/__init__.py +1 -0
- wbfdm/import_export/backends/refinitiv/utils/controller.py +182 -0
- wbfdm/import_export/handlers/__init__.py +0 -0
- wbfdm/import_export/handlers/instrument.py +253 -0
- wbfdm/import_export/handlers/instrument_list.py +101 -0
- wbfdm/import_export/handlers/instrument_price.py +71 -0
- wbfdm/import_export/handlers/option.py +54 -0
- wbfdm/import_export/handlers/private_equities.py +49 -0
- wbfdm/import_export/parsers/__init__.py +0 -0
- wbfdm/import_export/parsers/cbinsights/__init__.py +0 -0
- wbfdm/import_export/parsers/cbinsights/deals.py +39 -0
- wbfdm/import_export/parsers/cbinsights/equities.py +56 -0
- wbfdm/import_export/parsers/cbinsights/fundamentals.py +45 -0
- wbfdm/import_export/parsers/refinitiv/__init__.py +0 -0
- wbfdm/import_export/parsers/refinitiv/daily_fundamental.py +7 -0
- wbfdm/import_export/parsers/refinitiv/forecast.py +7 -0
- wbfdm/import_export/parsers/refinitiv/fundamental.py +9 -0
- wbfdm/import_export/parsers/refinitiv/geographic_segment.py +7 -0
- wbfdm/import_export/parsers/refinitiv/instrument.py +75 -0
- wbfdm/import_export/parsers/refinitiv/instrument_price.py +26 -0
- wbfdm/import_export/parsers/refinitiv/utils.py +96 -0
- wbfdm/import_export/resources/__init__.py +0 -0
- wbfdm/import_export/resources/classification.py +23 -0
- wbfdm/import_export/resources/instrument_prices.py +33 -0
- wbfdm/import_export/resources/instruments.py +176 -0
- wbfdm/jinja2.py +7 -0
- wbfdm/management/__init__.py +30 -0
- wbfdm/menu.py +11 -0
- wbfdm/migrations/0001_initial.py +71 -0
- wbfdm/migrations/0002_rename_statements_instrumentlookup_financials_and_more.py +144 -0
- wbfdm/migrations/0003_instrument_estimate_backend_and_more.py +34 -0
- wbfdm/migrations/0004_rename_financials_instrumentlookup_statements_and_more.py +86 -0
- wbfdm/migrations/0005_instrument_corporate_action_backend.py +29 -0
- wbfdm/migrations/0006_instrument_officer_backend.py +29 -0
- wbfdm/migrations/0007_instrument_country_instrument_currency_and_more.py +117 -0
- wbfdm/migrations/0008_controversy.py +75 -0
- wbfdm/migrations/0009_alter_controversy_flag_alter_controversy_initiated_and_more.py +85 -0
- wbfdm/migrations/0010_classification_classificationgroup_deal_exchange_and_more.py +1299 -0
- wbfdm/migrations/0011_delete_instrumentlookup_instrument_corporate_actions_and_more.py +169 -0
- wbfdm/migrations/0012_instrumentprice_created_instrumentprice_modified.py +564 -0
- wbfdm/migrations/0013_instrument_is_investable_universe_and_more.py +199 -0
- wbfdm/migrations/0014_alter_controversy_instrument.py +22 -0
- wbfdm/migrations/0015_instrument_instrument_investible_index.py +16 -0
- wbfdm/migrations/0016_instrumenttype_name_repr.py +18 -0
- wbfdm/migrations/0017_instrument_instrument_security_index.py +16 -0
- wbfdm/migrations/0018_instrument_instrument_level_index.py +20 -0
- wbfdm/migrations/0019_alter_controversy_source.py +17 -0
- wbfdm/migrations/0020_optionaggregate_option_and_more.py +249 -0
- wbfdm/migrations/0021_delete_instrumentdailystatistics.py +15 -0
- wbfdm/migrations/0022_instrument_cusip_option_open_interest_20d_and_more.py +91 -0
- wbfdm/migrations/0023_instrument_unique_ric_instrument_unique_rmc_and_more.py +53 -0
- wbfdm/migrations/0024_option_open_interest_10d_option_volume_10d_and_more.py +36 -0
- wbfdm/migrations/0025_instrument_is_primary_and_more.py +29 -0
- wbfdm/migrations/0026_instrument_is_cash_equivalent.py +30 -0
- wbfdm/migrations/0027_remove_instrument_unique_ric_and_more.py +100 -0
- wbfdm/migrations/__init__.py +0 -0
- wbfdm/models/__init__.py +4 -0
- wbfdm/models/esg/__init__.py +1 -0
- wbfdm/models/esg/controversies.py +81 -0
- wbfdm/models/exchanges/__init__.py +1 -0
- wbfdm/models/exchanges/exchanges.py +223 -0
- wbfdm/models/fields.py +117 -0
- wbfdm/models/fk_fields.py +403 -0
- wbfdm/models/indicators.py +0 -0
- wbfdm/models/instruments/__init__.py +19 -0
- wbfdm/models/instruments/classifications.py +265 -0
- wbfdm/models/instruments/instrument_lists.py +120 -0
- wbfdm/models/instruments/instrument_prices.py +540 -0
- wbfdm/models/instruments/instrument_relationships.py +251 -0
- wbfdm/models/instruments/instrument_requests.py +196 -0
- wbfdm/models/instruments/instruments.py +991 -0
- wbfdm/models/instruments/llm/__init__.py +1 -0
- wbfdm/models/instruments/llm/create_instrument_news_relationships.py +78 -0
- wbfdm/models/instruments/mixin/__init__.py +0 -0
- wbfdm/models/instruments/mixin/financials_computed.py +804 -0
- wbfdm/models/instruments/mixin/financials_serializer_fields.py +1407 -0
- wbfdm/models/instruments/mixin/instruments.py +294 -0
- wbfdm/models/instruments/options.py +225 -0
- wbfdm/models/instruments/private_equities.py +59 -0
- wbfdm/models/instruments/querysets.py +73 -0
- wbfdm/models/instruments/utils.py +41 -0
- wbfdm/preferences.py +21 -0
- wbfdm/serializers/__init__.py +4 -0
- wbfdm/serializers/esg.py +36 -0
- wbfdm/serializers/exchanges.py +39 -0
- wbfdm/serializers/instruments/__init__.py +37 -0
- wbfdm/serializers/instruments/classifications.py +139 -0
- wbfdm/serializers/instruments/instrument_lists.py +61 -0
- wbfdm/serializers/instruments/instrument_prices.py +73 -0
- wbfdm/serializers/instruments/instrument_relationships.py +170 -0
- wbfdm/serializers/instruments/instrument_requests.py +61 -0
- wbfdm/serializers/instruments/instruments.py +274 -0
- wbfdm/serializers/instruments/mixins.py +104 -0
- wbfdm/serializers/officers.py +20 -0
- wbfdm/signals.py +7 -0
- wbfdm/sync/__init__.py +0 -0
- wbfdm/sync/abstract.py +31 -0
- wbfdm/sync/runner.py +22 -0
- wbfdm/tasks.py +69 -0
- wbfdm/tests/__init__.py +0 -0
- wbfdm/tests/analysis/__init__.py +0 -0
- wbfdm/tests/analysis/financial_analysis/__init__.py +0 -0
- wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +392 -0
- wbfdm/tests/analysis/financial_analysis/test_utils.py +322 -0
- wbfdm/tests/analysis/test_esg.py +159 -0
- wbfdm/tests/conftest.py +92 -0
- wbfdm/tests/dataloaders/__init__.py +0 -0
- wbfdm/tests/dataloaders/test_cache.py +73 -0
- wbfdm/tests/models/__init__.py +0 -0
- wbfdm/tests/models/test_classifications.py +99 -0
- wbfdm/tests/models/test_exchanges.py +7 -0
- wbfdm/tests/models/test_instrument_list.py +117 -0
- wbfdm/tests/models/test_instrument_prices.py +306 -0
- wbfdm/tests/models/test_instruments.py +202 -0
- wbfdm/tests/models/test_merge.py +99 -0
- wbfdm/tests/models/test_options.py +69 -0
- wbfdm/tests/test_tasks.py +6 -0
- wbfdm/tests/tests.py +10 -0
- wbfdm/urls.py +222 -0
- wbfdm/utils.py +54 -0
- wbfdm/viewsets/__init__.py +10 -0
- wbfdm/viewsets/configs/__init__.py +5 -0
- wbfdm/viewsets/configs/buttons/__init__.py +8 -0
- wbfdm/viewsets/configs/buttons/classifications.py +23 -0
- wbfdm/viewsets/configs/buttons/exchanges.py +9 -0
- wbfdm/viewsets/configs/buttons/instrument_prices.py +49 -0
- wbfdm/viewsets/configs/buttons/instruments.py +283 -0
- wbfdm/viewsets/configs/display/__init__.py +22 -0
- wbfdm/viewsets/configs/display/classifications.py +138 -0
- wbfdm/viewsets/configs/display/esg.py +75 -0
- wbfdm/viewsets/configs/display/exchanges.py +42 -0
- wbfdm/viewsets/configs/display/instrument_lists.py +137 -0
- wbfdm/viewsets/configs/display/instrument_prices.py +199 -0
- wbfdm/viewsets/configs/display/instrument_requests.py +116 -0
- wbfdm/viewsets/configs/display/instruments.py +618 -0
- wbfdm/viewsets/configs/display/instruments_relationships.py +65 -0
- wbfdm/viewsets/configs/display/monthly_performances.py +72 -0
- wbfdm/viewsets/configs/display/officers.py +16 -0
- wbfdm/viewsets/configs/display/prices.py +21 -0
- wbfdm/viewsets/configs/display/statement_with_estimates.py +101 -0
- wbfdm/viewsets/configs/display/statements.py +48 -0
- wbfdm/viewsets/configs/endpoints/__init__.py +41 -0
- wbfdm/viewsets/configs/endpoints/classifications.py +87 -0
- wbfdm/viewsets/configs/endpoints/esg.py +20 -0
- wbfdm/viewsets/configs/endpoints/exchanges.py +6 -0
- wbfdm/viewsets/configs/endpoints/financials_analysis.py +65 -0
- wbfdm/viewsets/configs/endpoints/instrument_lists.py +38 -0
- wbfdm/viewsets/configs/endpoints/instrument_prices.py +51 -0
- wbfdm/viewsets/configs/endpoints/instrument_requests.py +20 -0
- wbfdm/viewsets/configs/endpoints/instruments.py +13 -0
- wbfdm/viewsets/configs/endpoints/instruments_relationships.py +31 -0
- wbfdm/viewsets/configs/endpoints/statements.py +6 -0
- wbfdm/viewsets/configs/menus/__init__.py +9 -0
- wbfdm/viewsets/configs/menus/classifications.py +19 -0
- wbfdm/viewsets/configs/menus/exchanges.py +10 -0
- wbfdm/viewsets/configs/menus/instrument_lists.py +10 -0
- wbfdm/viewsets/configs/menus/instruments.py +20 -0
- wbfdm/viewsets/configs/menus/instruments_relationships.py +33 -0
- wbfdm/viewsets/configs/titles/__init__.py +42 -0
- wbfdm/viewsets/configs/titles/classifications.py +79 -0
- wbfdm/viewsets/configs/titles/esg.py +11 -0
- wbfdm/viewsets/configs/titles/exchanges.py +12 -0
- wbfdm/viewsets/configs/titles/financial_ratio_analysis.py +6 -0
- wbfdm/viewsets/configs/titles/financials_analysis.py +50 -0
- wbfdm/viewsets/configs/titles/instrument_prices.py +50 -0
- wbfdm/viewsets/configs/titles/instrument_requests.py +16 -0
- wbfdm/viewsets/configs/titles/instruments.py +31 -0
- wbfdm/viewsets/configs/titles/instruments_relationships.py +21 -0
- wbfdm/viewsets/configs/titles/market_data.py +13 -0
- wbfdm/viewsets/configs/titles/prices.py +15 -0
- wbfdm/viewsets/configs/titles/statement_with_estimates.py +10 -0
- wbfdm/viewsets/esg.py +72 -0
- wbfdm/viewsets/exchanges.py +63 -0
- wbfdm/viewsets/financial_analysis/__init__.py +3 -0
- wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +85 -0
- wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +85 -0
- wbfdm/viewsets/financial_analysis/statement_with_estimates.py +145 -0
- wbfdm/viewsets/instruments/__init__.py +80 -0
- wbfdm/viewsets/instruments/classifications.py +279 -0
- wbfdm/viewsets/instruments/financials_analysis.py +614 -0
- wbfdm/viewsets/instruments/instrument_lists.py +77 -0
- wbfdm/viewsets/instruments/instrument_prices.py +542 -0
- wbfdm/viewsets/instruments/instrument_requests.py +51 -0
- wbfdm/viewsets/instruments/instruments.py +106 -0
- wbfdm/viewsets/instruments/instruments_relationships.py +235 -0
- wbfdm/viewsets/instruments/utils.py +27 -0
- wbfdm/viewsets/market_data.py +172 -0
- wbfdm/viewsets/mixins.py +9 -0
- wbfdm/viewsets/officers.py +27 -0
- wbfdm/viewsets/prices.py +62 -0
- wbfdm/viewsets/statements/__init__.py +1 -0
- wbfdm/viewsets/statements/statements.py +100 -0
- wbfdm/viewsets/technical_analysis/__init__.py +1 -0
- wbfdm/viewsets/technical_analysis/monthly_performances.py +93 -0
- wbfdm-2.2.1.dist-info/METADATA +15 -0
- wbfdm-2.2.1.dist-info/RECORD +337 -0
- wbfdm-2.2.1.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from wbcore import serializers as wb_serializers
|
|
2
|
+
from wbcore.contrib.directory.serializers import PersonRepresentationSerializer
|
|
3
|
+
from wbfdm.models.instruments import InstrumentRequest
|
|
4
|
+
|
|
5
|
+
from .instruments import InstrumentModelSerializer, InstrumentRepresentationSerializer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InstrumentRequestRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
9
|
+
_detail = wb_serializers.HyperlinkField(reverse_name="wbfdm:instrumentrequest-detail")
|
|
10
|
+
|
|
11
|
+
class Meta:
|
|
12
|
+
model = InstrumentRequest
|
|
13
|
+
fields = ("id", "status", "_detail")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InstrumentRequestModelSerializer(wb_serializers.ModelSerializer):
|
|
17
|
+
_requester = PersonRepresentationSerializer(source="requester")
|
|
18
|
+
_handler = PersonRepresentationSerializer(source="handler")
|
|
19
|
+
_created_instrument = InstrumentRepresentationSerializer(source="created_instrument")
|
|
20
|
+
notes = wb_serializers.CharField(required=False)
|
|
21
|
+
|
|
22
|
+
def validate(self, data):
|
|
23
|
+
if (not self.instance or not self.instance.requester) and (request := self.context.get("request")):
|
|
24
|
+
data["requester"] = request.user.profile
|
|
25
|
+
return super().validate(data)
|
|
26
|
+
|
|
27
|
+
class Meta:
|
|
28
|
+
model = InstrumentRequest
|
|
29
|
+
fields = (
|
|
30
|
+
"id",
|
|
31
|
+
"status",
|
|
32
|
+
"requester",
|
|
33
|
+
"_requester",
|
|
34
|
+
"_handler",
|
|
35
|
+
"handler",
|
|
36
|
+
"_created_instrument",
|
|
37
|
+
"created_instrument",
|
|
38
|
+
"notes",
|
|
39
|
+
"created",
|
|
40
|
+
"_additional_resources",
|
|
41
|
+
)
|
|
42
|
+
flatten_fields = {
|
|
43
|
+
"instrument_data": wb_serializers.JSONTableField(
|
|
44
|
+
serializer_class=InstrumentModelSerializer,
|
|
45
|
+
required=False,
|
|
46
|
+
flatten_field_names=[
|
|
47
|
+
"name",
|
|
48
|
+
"name_repr",
|
|
49
|
+
"instrument_type",
|
|
50
|
+
"refinitiv_identifier_code",
|
|
51
|
+
"refinitiv_mnemonic_code",
|
|
52
|
+
"isin",
|
|
53
|
+
"ticker",
|
|
54
|
+
"is_cash",
|
|
55
|
+
"currency",
|
|
56
|
+
"country",
|
|
57
|
+
"tags",
|
|
58
|
+
"classifications",
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Type
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth import get_user_model
|
|
4
|
+
from rest_framework.reverse import reverse
|
|
5
|
+
from wbcore import serializers
|
|
6
|
+
from wbcore.contrib.currency.models import Currency
|
|
7
|
+
from wbcore.contrib.currency.serializers import CurrencyRepresentationSerializer
|
|
8
|
+
from wbcore.contrib.geography.models import Geography
|
|
9
|
+
from wbcore.contrib.geography.serializers import CountryRepresentationSerializer
|
|
10
|
+
from wbcore.contrib.tags.serializers import TagRepresentationSerializer
|
|
11
|
+
from wbfdm.models import Instrument, InstrumentType
|
|
12
|
+
|
|
13
|
+
from ..exchanges import ExchangeRepresentationSerializer
|
|
14
|
+
from .classifications import ClassificationRepresentationSerializer
|
|
15
|
+
from .mixins import InstrumentAdditionalResourcesMixin
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
User = Type[get_user_model()]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InstrumentTypeRepresentationSerializer(serializers.RepresentationSerializer):
|
|
22
|
+
class Meta:
|
|
23
|
+
model = InstrumentType
|
|
24
|
+
fields = ("id", "name", "key")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InstrumentRepresentationSerializer(serializers.RepresentationSerializer):
|
|
28
|
+
_detail = serializers.HyperlinkField(reverse_name="wbfdm:instrument-detail")
|
|
29
|
+
|
|
30
|
+
def get_filter_params(self, request):
|
|
31
|
+
filter_params = {}
|
|
32
|
+
if (view := request.parser_context.get("view", None)) and (
|
|
33
|
+
classification_id := view.kwargs.get("classification_id", None)
|
|
34
|
+
):
|
|
35
|
+
filter_params["classifications_neq"] = classification_id
|
|
36
|
+
|
|
37
|
+
return filter_params
|
|
38
|
+
|
|
39
|
+
class Meta:
|
|
40
|
+
model = Instrument
|
|
41
|
+
fields = ("id", "name", "ticker", "isin", "computed_str", "_detail")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ClassifiableInstrumentRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
45
|
+
def get_filter_params(self, request):
|
|
46
|
+
filter_params = super().get_filter_params(request)
|
|
47
|
+
filter_params["instrument_type__is_classifiable"] = True
|
|
48
|
+
filter_params["level"] = 0
|
|
49
|
+
return filter_params
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SecurityRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
53
|
+
def get_filter_params(self, request):
|
|
54
|
+
filter_params = super().get_filter_params(request)
|
|
55
|
+
filter_params["is_security"] = True
|
|
56
|
+
return filter_params
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class InvestableUniverseRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
60
|
+
def get_filter_params(self, request):
|
|
61
|
+
filter_params = super().get_filter_params(request)
|
|
62
|
+
filter_params["is_investable_universe"] = True
|
|
63
|
+
return filter_params
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class InvestableInstrumentRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
67
|
+
def get_filter_params(self, request):
|
|
68
|
+
filter_params = super().get_filter_params(request)
|
|
69
|
+
filter_params["is_investable"] = True
|
|
70
|
+
return filter_params
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PrimaryInvestableInstrumentRepresentationSerializer(InvestableInstrumentRepresentationSerializer):
|
|
74
|
+
def get_filter_params(self, request):
|
|
75
|
+
filter_params = super().get_filter_params(request)
|
|
76
|
+
filter_params["is_primary"] = True
|
|
77
|
+
return filter_params
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ManagedInstrumentRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
81
|
+
def get_filter_params(self, request):
|
|
82
|
+
filter_params = super().get_filter_params(request)
|
|
83
|
+
filter_params["is_managed"] = True
|
|
84
|
+
return filter_params
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class EquityRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
88
|
+
def get_filter_params(self, request):
|
|
89
|
+
filter_params = super().get_filter_params(request)
|
|
90
|
+
filter_params["investment_type__key"] = "equity"
|
|
91
|
+
return filter_params
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ProductRepresentationSerializer(InstrumentRepresentationSerializer):
|
|
95
|
+
def get_filter_params(self, request):
|
|
96
|
+
filter_params = super().get_filter_params(request)
|
|
97
|
+
filter_params["investment_type__key"] = "product"
|
|
98
|
+
return filter_params
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class InstrumentModelListSerializer(
|
|
102
|
+
serializers.ModelSerializer,
|
|
103
|
+
):
|
|
104
|
+
_currency = CurrencyRepresentationSerializer(source="currency")
|
|
105
|
+
currency = serializers.PrimaryKeyRelatedField(queryset=Currency.objects.all())
|
|
106
|
+
currency_symbol = serializers.CharField(read_only=True)
|
|
107
|
+
_exchange = ExchangeRepresentationSerializer(source="exchange")
|
|
108
|
+
country = serializers.PrimaryKeyRelatedField(queryset=Geography.countries.all())
|
|
109
|
+
_country = CountryRepresentationSerializer(source="country")
|
|
110
|
+
instrument_type = serializers.PrimaryKeyRelatedField(queryset=InstrumentType.objects.all())
|
|
111
|
+
_instrument_type = InstrumentTypeRepresentationSerializer(source="instrument_type")
|
|
112
|
+
_tags = TagRepresentationSerializer(many=True, source="tags")
|
|
113
|
+
|
|
114
|
+
_classifications = ClassificationRepresentationSerializer(
|
|
115
|
+
source="classifications", many=True, label_key="{{ name }}"
|
|
116
|
+
)
|
|
117
|
+
_parent = InstrumentRepresentationSerializer(source="parent")
|
|
118
|
+
is_active = serializers.BooleanField(default=True, read_only=True)
|
|
119
|
+
is_cash = serializers.BooleanField(default=False, read_only=True)
|
|
120
|
+
_group_key = serializers.CharField(read_only=True)
|
|
121
|
+
|
|
122
|
+
class Meta:
|
|
123
|
+
model = Instrument
|
|
124
|
+
read_only_fields = (
|
|
125
|
+
"name",
|
|
126
|
+
"exchange",
|
|
127
|
+
"computed_str",
|
|
128
|
+
"instrument_type",
|
|
129
|
+
"old_isins",
|
|
130
|
+
"inception_date",
|
|
131
|
+
"delisted_date",
|
|
132
|
+
"country",
|
|
133
|
+
"isin",
|
|
134
|
+
"refinitiv_identifier_code",
|
|
135
|
+
"ticker",
|
|
136
|
+
"is_active",
|
|
137
|
+
"parent",
|
|
138
|
+
"is_primary",
|
|
139
|
+
"is_investable_universe",
|
|
140
|
+
"is_security",
|
|
141
|
+
"is_managed",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
fields = (
|
|
145
|
+
"id",
|
|
146
|
+
"name",
|
|
147
|
+
"name_repr",
|
|
148
|
+
"description",
|
|
149
|
+
"instrument_type",
|
|
150
|
+
"_instrument_type",
|
|
151
|
+
"inception_date",
|
|
152
|
+
"delisted_date",
|
|
153
|
+
"country",
|
|
154
|
+
"_country",
|
|
155
|
+
"currency",
|
|
156
|
+
"_currency",
|
|
157
|
+
"currency_symbol",
|
|
158
|
+
"isin",
|
|
159
|
+
"refinitiv_identifier_code",
|
|
160
|
+
"ticker",
|
|
161
|
+
"_classifications",
|
|
162
|
+
"classifications",
|
|
163
|
+
"_tags",
|
|
164
|
+
"tags",
|
|
165
|
+
"is_active",
|
|
166
|
+
"is_cash",
|
|
167
|
+
"_parent",
|
|
168
|
+
"parent",
|
|
169
|
+
"_additional_resources",
|
|
170
|
+
"_buttons",
|
|
171
|
+
"_group_key",
|
|
172
|
+
"exchange",
|
|
173
|
+
"_exchange",
|
|
174
|
+
"is_primary",
|
|
175
|
+
"is_investable_universe",
|
|
176
|
+
"is_security",
|
|
177
|
+
"is_managed",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class InstrumentModelSerializer(InstrumentAdditionalResourcesMixin, InstrumentModelListSerializer):
|
|
182
|
+
_related_instruments = SecurityRepresentationSerializer(source="related_instruments", many=True)
|
|
183
|
+
|
|
184
|
+
@serializers.register_resource()
|
|
185
|
+
def load_resources(self, instance, request, user, **kwargs):
|
|
186
|
+
res = {
|
|
187
|
+
"prices": reverse("wbfdm:prices-list", args=[instance.id], request=request),
|
|
188
|
+
"financial-statistics": reverse(
|
|
189
|
+
"wbfdm:instrument-financialstatistics-list", args=[instance.id], request=request
|
|
190
|
+
),
|
|
191
|
+
}
|
|
192
|
+
if not instance.is_managed:
|
|
193
|
+
res.update(
|
|
194
|
+
{
|
|
195
|
+
"swe-income-statement": reverse(
|
|
196
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "income"], request=request
|
|
197
|
+
),
|
|
198
|
+
"swe-balance-sheet": reverse(
|
|
199
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "balancesheet"], request=request
|
|
200
|
+
),
|
|
201
|
+
"swe-cashflow-statement": reverse(
|
|
202
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "cashflow"], request=request
|
|
203
|
+
),
|
|
204
|
+
"swe-ratios": reverse(
|
|
205
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "ratios"], request=request
|
|
206
|
+
),
|
|
207
|
+
"swe-margins": reverse(
|
|
208
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "margins"], request=request
|
|
209
|
+
),
|
|
210
|
+
"swe-summary": reverse(
|
|
211
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "summary"], request=request
|
|
212
|
+
),
|
|
213
|
+
"swe-cashflow-ratios": reverse(
|
|
214
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "cashflow-ratios"], request=request
|
|
215
|
+
),
|
|
216
|
+
"swe-asset-turnover-ratios": reverse(
|
|
217
|
+
"wbfdm:statementwithestimates-list",
|
|
218
|
+
args=[instance.id, "asset-turnover-ratios"],
|
|
219
|
+
request=request,
|
|
220
|
+
),
|
|
221
|
+
"swe-credit": reverse(
|
|
222
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "credit"], request=request
|
|
223
|
+
),
|
|
224
|
+
"swe-long-term-solvency": reverse(
|
|
225
|
+
"wbfdm:statementwithestimates-list", args=[instance.id, "long-term-solvency"], request=request
|
|
226
|
+
),
|
|
227
|
+
"swe-short-term-liquidity": reverse(
|
|
228
|
+
"wbfdm:statementwithestimates-list",
|
|
229
|
+
args=[instance.id, "short-term-liquidity"],
|
|
230
|
+
request=request,
|
|
231
|
+
),
|
|
232
|
+
"valuation_ratios-new": reverse(
|
|
233
|
+
"wbfdm:valuation_ratios-list", args=[instance.id], request=request
|
|
234
|
+
),
|
|
235
|
+
"income-statement": reverse(
|
|
236
|
+
"wbfdm:statement-list", args=[instance.id, "income-statement"], request=request
|
|
237
|
+
),
|
|
238
|
+
"balance-sheet": reverse(
|
|
239
|
+
"wbfdm:statement-list", args=[instance.id, "balance-sheet"], request=request
|
|
240
|
+
),
|
|
241
|
+
"cash-flow-statement": reverse(
|
|
242
|
+
"wbfdm:statement-list", args=[instance.id, "cash-flow-statement"], request=request
|
|
243
|
+
),
|
|
244
|
+
"officers": reverse(viewname="wbfdm:officers-list", args=[instance.id], request=request),
|
|
245
|
+
"controversies": reverse(viewname="wbfdm:controversies-list", args=[instance.id], request=request),
|
|
246
|
+
"pai": reverse(viewname="wbfdm:pai-list", args=[instance.id], request=request),
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
return res
|
|
250
|
+
|
|
251
|
+
@serializers.register_only_instance_resource()
|
|
252
|
+
def register_children(self, instance, request, user, **kwargs):
|
|
253
|
+
return {"children": reverse("wbfdm:instrument-children-list", args=[instance.id], request=request)}
|
|
254
|
+
|
|
255
|
+
@serializers.register_resource()
|
|
256
|
+
def register_market_data(self, instance, request, user, **kwargs):
|
|
257
|
+
return {
|
|
258
|
+
"market_data": reverse("wbfdm:market_data-list", args=[instance.id], request=request),
|
|
259
|
+
"cumulativereturn": f'{reverse("wbfdm:market_data-list", args=[instance.id], request=request)}?chart_type=ret',
|
|
260
|
+
"drawdown": f'{reverse("wbfdm:market_data-list", args=[instance.id], request=request)}?chart_type=drawdown',
|
|
261
|
+
"performance_summary": reverse("wbfdm:performance_summary-list", args=[instance.id], request=request),
|
|
262
|
+
"monthly_performances": reverse("wbfdm:monthly_performances-list", args=[instance.id], request=request),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
class Meta(InstrumentModelListSerializer.Meta):
|
|
266
|
+
fields = InstrumentModelListSerializer.Meta.fields + (
|
|
267
|
+
"refinitiv_mnemonic_code",
|
|
268
|
+
"identifier",
|
|
269
|
+
"base_color",
|
|
270
|
+
"old_isins",
|
|
271
|
+
"is_cash",
|
|
272
|
+
"related_instruments",
|
|
273
|
+
"_related_instruments",
|
|
274
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from rest_framework.reverse import reverse
|
|
2
|
+
from wbcore import serializers as wb_serializers
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InstrumentAdditionalResourcesMixin:
|
|
6
|
+
@wb_serializers.register_only_instance_resource()
|
|
7
|
+
def instrument_resources(self, instance, request, user, **kwargs):
|
|
8
|
+
additional_resources = dict()
|
|
9
|
+
|
|
10
|
+
if instance.prices.exists():
|
|
11
|
+
additional_resources["instrumentpricestatisticchart"] = reverse(
|
|
12
|
+
"wbfdm:instrument-pricestatisticchart-list",
|
|
13
|
+
args=[instance.id],
|
|
14
|
+
request=request,
|
|
15
|
+
)
|
|
16
|
+
additional_resources["instrumentprices"] = reverse(
|
|
17
|
+
"wbfdm:instrument-price-list",
|
|
18
|
+
args=[instance.id],
|
|
19
|
+
request=request,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
additional_resources["distributionreturnschart"] = reverse(
|
|
23
|
+
"wbfdm:instrument-distributionreturnschart-list",
|
|
24
|
+
args=[instance.id],
|
|
25
|
+
request=request,
|
|
26
|
+
)
|
|
27
|
+
additional_resources["bestandworstreturns"] = reverse(
|
|
28
|
+
"wbfdm:instrument-bestandworstreturns-list",
|
|
29
|
+
args=[instance.id],
|
|
30
|
+
request=request,
|
|
31
|
+
)
|
|
32
|
+
additional_resources[
|
|
33
|
+
"price_and_volume"
|
|
34
|
+
] = f'{reverse("wbfdm:market_data-list", args=[instance.id], request=request)}?chart_type=close&indicators=sma_50,sma_100&volume=true'
|
|
35
|
+
|
|
36
|
+
additional_resources["classifications_list"] = reverse(
|
|
37
|
+
"wbfdm:instrument-classification-list",
|
|
38
|
+
args=[instance.get_root().id],
|
|
39
|
+
request=request,
|
|
40
|
+
)
|
|
41
|
+
additional_resources["instrument_lists"] = reverse(
|
|
42
|
+
"wbfdm:instrument-instrumentlistthrough-list", args=[instance.id], request=request
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return additional_resources
|
|
46
|
+
|
|
47
|
+
@wb_serializers.register_only_instance_resource()
|
|
48
|
+
def related_instruments(self, instance, request, user, **kwargs):
|
|
49
|
+
return {
|
|
50
|
+
"related_instruments": reverse(
|
|
51
|
+
"wbfdm:instrument-relatedinstrument-list", args=[instance.id], request=request
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# @wb_serializers.register_only_instance_resource()
|
|
56
|
+
# def deprecated_financial_resources(self, instance, request, user, **kwargs):
|
|
57
|
+
# additional_resources = dict()
|
|
58
|
+
# additional_resources["summary_table"] = reverse(
|
|
59
|
+
# "wbfdm:instrument-summarytablechart-list",
|
|
60
|
+
# args=[instance.id],
|
|
61
|
+
# request=request,
|
|
62
|
+
# )
|
|
63
|
+
# additional_resources["financials_graph"] = reverse(
|
|
64
|
+
# "wbfdm:instrument-financialsgraphchart-list",
|
|
65
|
+
# args=[instance.id],
|
|
66
|
+
# request=request,
|
|
67
|
+
# )
|
|
68
|
+
# additional_resources["profitability_ratios"] = reverse(
|
|
69
|
+
# "wbfdm:instrument-profitabilityratioschart-list",
|
|
70
|
+
# args=[instance.id],
|
|
71
|
+
# request=request,
|
|
72
|
+
# )
|
|
73
|
+
# additional_resources["cash_flow_analysis_table"] = reverse(
|
|
74
|
+
# "wbfdm:instrument-cashflowanalysistablechart-list",
|
|
75
|
+
# args=[instance.id],
|
|
76
|
+
# request=request,
|
|
77
|
+
# )
|
|
78
|
+
# additional_resources["cash_flow_analysis_chart"] = reverse(
|
|
79
|
+
# "wbfdm:instrument-cashflowanalysisbarchart-list",
|
|
80
|
+
# args=[instance.id],
|
|
81
|
+
# request=request,
|
|
82
|
+
# )
|
|
83
|
+
# additional_resources["net_debt_and_ebitda_chart"] = reverse(
|
|
84
|
+
# "wbfdm:instrument-netdebtandebitdachart-list",
|
|
85
|
+
# args=[instance.id],
|
|
86
|
+
# request=request,
|
|
87
|
+
# )
|
|
88
|
+
# earnings_base_url = reverse(
|
|
89
|
+
# "wbfdm:instrument-earningschart-list",
|
|
90
|
+
# args=[instance.id],
|
|
91
|
+
# request=request,
|
|
92
|
+
# )
|
|
93
|
+
# additional_resources["earnings_chart"] = earnings_base_url
|
|
94
|
+
# additional_resources["earnings_chart_ttm"] = f"{earnings_base_url}?period=TTM"
|
|
95
|
+
# additional_resources["earnings_chart_ntm"] = f"{earnings_base_url}?period=FTM"
|
|
96
|
+
# valuation_ratios_base_url = reverse(
|
|
97
|
+
# "wbfdm:instrument-valuationratios-list",
|
|
98
|
+
# args=[instance.id],
|
|
99
|
+
# request=request,
|
|
100
|
+
# )
|
|
101
|
+
# additional_resources["valuation_ratios-old"] = valuation_ratios_base_url
|
|
102
|
+
# additional_resources["valuation_ratios_ranges"] = f"{valuation_ratios_base_url}?ranges=true"
|
|
103
|
+
# additional_resources["valuation_ratios_related"] = f"{valuation_ratios_base_url}?vs_related=true"
|
|
104
|
+
# return additional_resources
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from wbcore import serializers
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OfficerSerializer(serializers.Serializer):
|
|
5
|
+
id = serializers.PrimaryKeyCharField()
|
|
6
|
+
position = serializers.CharField()
|
|
7
|
+
name = serializers.CharField()
|
|
8
|
+
age = serializers.IntegerField()
|
|
9
|
+
sex = serializers.CharField()
|
|
10
|
+
start = serializers.DateField()
|
|
11
|
+
|
|
12
|
+
class Meta:
|
|
13
|
+
fields = (
|
|
14
|
+
"id",
|
|
15
|
+
"position",
|
|
16
|
+
"name",
|
|
17
|
+
"age",
|
|
18
|
+
"sex",
|
|
19
|
+
"start",
|
|
20
|
+
)
|
wbfdm/signals.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from django.db.models.signals import ModelSignal
|
|
2
|
+
|
|
3
|
+
# this signal is triggered by the investable universe manager in order to gather what instrument are considered within the investable universe.
|
|
4
|
+
add_instrument_to_investable_universe = ModelSignal(use_caching=False)
|
|
5
|
+
|
|
6
|
+
# this signal is triggered whenever prices are stored in the system and action needs to be considered
|
|
7
|
+
instrument_price_imported = ModelSignal(use_caching=False)
|
wbfdm/sync/__init__.py
ADDED
|
File without changes
|
wbfdm/sync/abstract.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Generic, TypeVar
|
|
3
|
+
|
|
4
|
+
T = TypeVar("T")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Sync(Generic[T], ABC):
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def update(self):
|
|
10
|
+
"""Updates all items"""
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def update_or_create_item(self, external_id: int) -> T:
|
|
15
|
+
"""Updates or creates a single item from an external id and returns it"""
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def update_item(self, item: T) -> T:
|
|
20
|
+
"""Updates a single item and returns it"""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_item(self, external_id: int) -> dict:
|
|
25
|
+
"""Retrieves a dictionairy representation of the item"""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def trigger_partial_update(self):
|
|
30
|
+
"""Figures out if an update is necessary and then tries to update the entire database in the most efficiant way"""
|
|
31
|
+
...
|
wbfdm/sync/runner.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.utils.module_loading import import_string
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def initialize_instruments():
|
|
6
|
+
for sync in map(import_string, getattr(settings, "INSTRUMENT_SYNC", [])):
|
|
7
|
+
sync().update()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def initialize_exchanges():
|
|
11
|
+
for sync in map(import_string, getattr(settings, "EXCHANGE_SYNC", [])):
|
|
12
|
+
sync().update()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def synchronize_instruments():
|
|
16
|
+
for sync in map(import_string, getattr(settings, "INSTRUMENT_SYNC", [])):
|
|
17
|
+
sync().trigger_partial_update()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def synchronize_exchanges():
|
|
21
|
+
for sync in map(import_string, getattr(settings, "EXCHANGE_SYNC", [])):
|
|
22
|
+
sync().trigger_partial_update()
|
wbfdm/tasks.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from celery import shared_task
|
|
4
|
+
from django.db import transaction
|
|
5
|
+
from django.db.models import Q
|
|
6
|
+
from pandas.tseries.offsets import BDay
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
from wbfdm.models import Instrument
|
|
9
|
+
from wbfdm.sync.runner import ( # noqa: F401
|
|
10
|
+
initialize_exchanges,
|
|
11
|
+
initialize_instruments,
|
|
12
|
+
synchronize_exchanges,
|
|
13
|
+
synchronize_instruments,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@shared_task(queue="portfolio")
|
|
18
|
+
def update_of_investable_universe_data(start: date | None = None, end: date | None = None, clear: bool = False):
|
|
19
|
+
"""
|
|
20
|
+
Update the investable universe data on a daily basis.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
- start (date | None): The start date for updating data. If None, defaults to three business days before 'end'.
|
|
24
|
+
- end (date | None): The end date for updating data. If None, defaults to the current date.
|
|
25
|
+
|
|
26
|
+
Notes:
|
|
27
|
+
- The function resets the investable universe by marking all instruments as not in the investable universe.
|
|
28
|
+
- It then updates all instruments marked as part of the investable universe.
|
|
29
|
+
- If 'end' is not provided, it defaults to the current date.
|
|
30
|
+
- If 'start' is not provided, it defaults to three business days before 'end'.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
None
|
|
34
|
+
"""
|
|
35
|
+
if not end:
|
|
36
|
+
end = (
|
|
37
|
+
date.today() - BDay(1)
|
|
38
|
+
).date() # we don't import today price in case the dataloader returns duplicates (e.g. DSWS)
|
|
39
|
+
if not start:
|
|
40
|
+
start = (end - BDay(3)).date() # override three last day by default
|
|
41
|
+
|
|
42
|
+
Instrument.investable_universe.update(
|
|
43
|
+
is_investable_universe=True
|
|
44
|
+
) # ensure all the investable universe is marked as such
|
|
45
|
+
|
|
46
|
+
instruments = Instrument.active_objects.filter(is_investable_universe=True, delisted_date__isnull=True).exclude(
|
|
47
|
+
Q(is_managed=True)
|
|
48
|
+
| Q(dl_parameters__market_data__path="wbfdm.contrib.internal.dataloaders.market_data.MarketDataDataloader")
|
|
49
|
+
) # we exclude product and index managed to avoid circular import
|
|
50
|
+
for instrument in tqdm(instruments, total=instruments.count()):
|
|
51
|
+
instrument.import_prices(start=start, end=end, clear=clear)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@shared_task(queue="portfolio")
|
|
55
|
+
def synchronize_instruments_as_task():
|
|
56
|
+
synchronize_instruments()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@shared_task(queue="portfolio")
|
|
60
|
+
def synchronize_exchanges_as_task():
|
|
61
|
+
synchronize_exchanges()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@shared_task(queue="portfolio")
|
|
65
|
+
def full_synchronization_as_task():
|
|
66
|
+
initialize_exchanges()
|
|
67
|
+
initialize_instruments()
|
|
68
|
+
with transaction.atomic():
|
|
69
|
+
Instrument.objects.rebuild() # rebuild MPTT tree
|
wbfdm/tests/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|