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,67 @@
|
|
|
1
|
+
from typing import Iterator
|
|
2
|
+
|
|
3
|
+
from django.db import connections
|
|
4
|
+
from jinjasql import JinjaSql
|
|
5
|
+
from wbcore.contrib.dataloader.dataloaders import Dataloader
|
|
6
|
+
from wbcore.contrib.dataloader.utils import dictfetchall
|
|
7
|
+
from wbfdm.dataloaders.protocols import ReportDateProtocol
|
|
8
|
+
from wbfdm.dataloaders.types import ReportDateDataDict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IbesReportingDateDataloader(ReportDateProtocol, Dataloader):
|
|
12
|
+
def reporting_dates(self, only_next: bool = True) -> Iterator[ReportDateDataDict]:
|
|
13
|
+
lookup = {k: v for k, v in self.entities.values_list("dl_parameters__reporting_dates__parameters", "id")}
|
|
14
|
+
|
|
15
|
+
sql = """
|
|
16
|
+
with next_events as (
|
|
17
|
+
select
|
|
18
|
+
*,
|
|
19
|
+
row_number() over (partition by EstPermID, PerType order by PerEndDate) as rn
|
|
20
|
+
from TREExpectedRptDate
|
|
21
|
+
where StartDate > getdate()
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
select
|
|
25
|
+
rp.EstPermID as external_id,
|
|
26
|
+
'qa-ibes' as source,
|
|
27
|
+
convert(date, rp.PerEndDate) as per_end_date,
|
|
28
|
+
convert(date, rp.StartDate) as start_date,
|
|
29
|
+
convert(date, rp.EndDate) as end_date,
|
|
30
|
+
iif(rp.PerType=4, 0, 1) as interim,
|
|
31
|
+
case
|
|
32
|
+
when rp.MarketPhase = 'AMC' then 'after_market'
|
|
33
|
+
when rp.MarketPhase = 'BMO' then 'before_market'
|
|
34
|
+
else null
|
|
35
|
+
end as market_phase,
|
|
36
|
+
lower(rp.Status) as status
|
|
37
|
+
{% if only_next %}
|
|
38
|
+
from next_events as rp
|
|
39
|
+
{% else %}
|
|
40
|
+
from TREExpectedRptDate as rp
|
|
41
|
+
{% endif %}
|
|
42
|
+
where rp.EstPermID in (
|
|
43
|
+
{% for instrument in instruments %}
|
|
44
|
+
{{instrument}} {% if not loop.last %}, {% endif %}
|
|
45
|
+
{% endfor %}
|
|
46
|
+
)
|
|
47
|
+
{% if only_next %}
|
|
48
|
+
AND rp.rn = 1
|
|
49
|
+
{% endif %}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
query, bind_params = JinjaSql(param_style="format").prepare_query(
|
|
53
|
+
sql,
|
|
54
|
+
{
|
|
55
|
+
"instruments": lookup.keys(),
|
|
56
|
+
"only_next": only_next,
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
with connections["qa"].cursor() as cursor:
|
|
60
|
+
cursor.execute(
|
|
61
|
+
query,
|
|
62
|
+
bind_params,
|
|
63
|
+
)
|
|
64
|
+
for row in dictfetchall(cursor):
|
|
65
|
+
row["instrument_id"] = lookup[row["external_id"]]
|
|
66
|
+
row["interim"] = bool(row["interim"])
|
|
67
|
+
yield row
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Iterator
|
|
4
|
+
|
|
5
|
+
from django.db import connections
|
|
6
|
+
from jinjasql import JinjaSql # type: ignore
|
|
7
|
+
from wbcore.contrib.dataloader.dataloaders import Dataloader
|
|
8
|
+
from wbcore.contrib.dataloader.utils import dictfetchall
|
|
9
|
+
from wbfdm.dataloaders.protocols import StatementsProtocol
|
|
10
|
+
from wbfdm.dataloaders.types import StatementDataDict
|
|
11
|
+
from wbfdm.enums import DataType, Financial, PeriodType, StatementType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RKDStatementType(Enum):
|
|
15
|
+
INCOME_STATEMENT = 1
|
|
16
|
+
CASHFLOW_STATEMENT = 2
|
|
17
|
+
BALANCE_SHEET = 3
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RKDFinancial(Enum):
|
|
21
|
+
EMPLOYEES = "METL"
|
|
22
|
+
CASH_AND_SHORT_TERM_INVESTMENT = "SCSI"
|
|
23
|
+
DILUTED_WEIGHTED_AVG_SHARES = "SDWS"
|
|
24
|
+
TOTAL_DEBT = "STLD"
|
|
25
|
+
NET_DEBT = "SNTD"
|
|
26
|
+
STOCK_COMPENSATION = "VSCP"
|
|
27
|
+
TANGIBLE_BOOK_VALUE_PER_SHARE = "STBP"
|
|
28
|
+
REVENUE = "RTLR"
|
|
29
|
+
SHARES_OUTSTANDING = "QTCO"
|
|
30
|
+
EPS = "SDAI"
|
|
31
|
+
CASH_FLOW_FROM_OPERATIONS = "OTLO"
|
|
32
|
+
CAPEX = "SCEX"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
reported_sql = """
|
|
36
|
+
SELECT
|
|
37
|
+
CONCAT(val.Code, '-', CONVERT(DATE, val.PerEndDt), '-', fil.InterimNo, '-', val.LineID) as id,
|
|
38
|
+
val.LineID as external_ordering,
|
|
39
|
+
itemsub.Desc_ as external_description,
|
|
40
|
+
itemsub.StdCOA as external_code,
|
|
41
|
+
val.Code as external_identifier,
|
|
42
|
+
CONVERT(DATE, val.PerEndDt) as period_end_date,
|
|
43
|
+
per.Fyr as year,
|
|
44
|
+
fil.InterimNo as interim,
|
|
45
|
+
CASE
|
|
46
|
+
when fil.PerTypeCode = 1 THEN 'Y'
|
|
47
|
+
when fil.PerTypeCode = 2 THEN 'Q'
|
|
48
|
+
when fil.PerTypeCode = 3 THEN 'S'
|
|
49
|
+
when fil.PerTypeCode = 4 THEN 'T'
|
|
50
|
+
when fil.PerTypeCode = 5 THEN 'Q'
|
|
51
|
+
when fil.PerTypeCode = 6 THEN 'Y'
|
|
52
|
+
END as period_type,
|
|
53
|
+
CASE
|
|
54
|
+
WHEN fil.UnitsConvToCode = 'T' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e3
|
|
55
|
+
WHEN fil.UnitsConvToCode = 'M' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e6
|
|
56
|
+
WHEN fil.UnitsConvToCode = 'B' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e9
|
|
57
|
+
ELSE val.Value_
|
|
58
|
+
END as value,
|
|
59
|
+
'qa-rkd' as source,
|
|
60
|
+
CASE
|
|
61
|
+
WHEN item.IsCurrency = 1 THEN code.Desc_
|
|
62
|
+
ELSE NULL
|
|
63
|
+
END AS currency
|
|
64
|
+
|
|
65
|
+
FROM RKDFndCSFFinVal AS val
|
|
66
|
+
|
|
67
|
+
JOIN RKDFndCSFStmt AS stmt ON
|
|
68
|
+
val.Code = stmt.Code
|
|
69
|
+
AND val.PerEndDt = stmt.PerEndDt
|
|
70
|
+
AND val.PerTypeCode = stmt.PerTypeCode
|
|
71
|
+
AND val.StmtDt = stmt.StmtDt
|
|
72
|
+
AND val.StmtTypeCode = stmt.StmtTypeCode
|
|
73
|
+
AND stmt.CompStmtCode = 1
|
|
74
|
+
|
|
75
|
+
JOIN RKDFndCSFPerFiling AS fil ON
|
|
76
|
+
stmt.Code = fil.Code
|
|
77
|
+
AND stmt.PerEndDt = fil.PerEndDt
|
|
78
|
+
AND stmt.PerTypeCode = fil.PerTypeCode
|
|
79
|
+
AND stmt.StmtDt = fil.StmtDt
|
|
80
|
+
|
|
81
|
+
LEFT JOIN RKDFNDCSFITEMSUB AS itemsub ON
|
|
82
|
+
val.Code = itemsub.Code
|
|
83
|
+
AND val.PerTypeCode = itemsub.PerTypeCode
|
|
84
|
+
AND val.StmtTypeCode = itemsub.StmtTypeCode
|
|
85
|
+
AND val.LineID = itemsub.LineID
|
|
86
|
+
|
|
87
|
+
LEFT JOIN RKDFndCSFItem AS item ON
|
|
88
|
+
itemsub.Item = item.Item
|
|
89
|
+
|
|
90
|
+
LEFT JOIN RKDFndCode AS code ON
|
|
91
|
+
fil.CurrConvToCode = code.Code
|
|
92
|
+
AND code.Type_ = 58
|
|
93
|
+
|
|
94
|
+
LEFT JOIN RKDFndCsfPeriod AS per ON
|
|
95
|
+
per.Code = fil.Code
|
|
96
|
+
AND per.PerEndDt = fil.PerEndDt
|
|
97
|
+
AND per.PerTypeCode = (
|
|
98
|
+
CASE
|
|
99
|
+
WHEN fil.PerTypeCode = 1 THEN 1
|
|
100
|
+
WHEN fil.PerTypeCode in (2,3,4,5) THEN 5
|
|
101
|
+
END
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
WHERE
|
|
105
|
+
val.Code in (
|
|
106
|
+
{% for instrument in instruments %}
|
|
107
|
+
{{instrument}} {% if not loop.last %}, {% endif %}
|
|
108
|
+
{% endfor %})
|
|
109
|
+
AND val.StmtTypeCode = {{ statement_type }}
|
|
110
|
+
{% if from_year %}AND per.Fyr >= {{ from_year }} {% endif %}
|
|
111
|
+
{% if to_year %}AND per.Fyr <= {{ to_year }} {% endif %}
|
|
112
|
+
{% if from_date %}AND val.PerEndDt >= {{ from_date }} {% endif %}
|
|
113
|
+
{% if to_date %}AND val.PerEndDt <= {{ to_date }} {% endif %}
|
|
114
|
+
{% if period_type == 'interim' %}AND fil.InterimNo > 0{% elif period_type == 'annual' %}AND (fil.InterimNo = 0 OR fil.InterimNo IS NULL){% endif %}
|
|
115
|
+
ORDER BY val.LineID
|
|
116
|
+
"""
|
|
117
|
+
standardized_sql = """
|
|
118
|
+
SELECT
|
|
119
|
+
CONCAT(val.Code, '-', CONVERT(DATE, val.PerEndDt), '-', fil.InterimNo, '-', item.LineID) as id,
|
|
120
|
+
item.LineID as external_ordering,
|
|
121
|
+
item.Desc_ as external_description,
|
|
122
|
+
item.COA as external_code,
|
|
123
|
+
val.Code as external_identifier,
|
|
124
|
+
CONVERT(DATE, val.PerEndDt) as period_end_date,
|
|
125
|
+
per.Fyr as year,
|
|
126
|
+
CASE
|
|
127
|
+
when fil.PerTypeCode = 1 THEN 'Y'
|
|
128
|
+
when fil.PerTypeCode = 2 THEN 'Q'
|
|
129
|
+
when fil.PerTypeCode = 3 THEN 'S'
|
|
130
|
+
when fil.PerTypeCode = 4 THEN 'T'
|
|
131
|
+
when fil.PerTypeCode = 5 THEN 'Q'
|
|
132
|
+
when fil.PerTypeCode = 6 THEN 'Y'
|
|
133
|
+
END as period_type,
|
|
134
|
+
fil.InterimNo as interim,
|
|
135
|
+
CASE
|
|
136
|
+
WHEN fil.UnitsConvToCode = 'T' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e3
|
|
137
|
+
WHEN fil.UnitsConvToCode = 'M' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e6
|
|
138
|
+
WHEN fil.UnitsConvToCode = 'B' AND item.ItemPrecision in (1,2) THEN val.Value_ * 1e9
|
|
139
|
+
ELSE val.Value_
|
|
140
|
+
END as value,
|
|
141
|
+
'qa-rkd' as source,
|
|
142
|
+
CASE
|
|
143
|
+
WHEN item.IsCurrency = 1 THEN code.Desc_
|
|
144
|
+
ELSE NULL
|
|
145
|
+
END AS currency
|
|
146
|
+
|
|
147
|
+
FROM RKDFndStdFinVal AS val
|
|
148
|
+
|
|
149
|
+
LEFT JOIN RKDFndStdPeriod AS per ON
|
|
150
|
+
per.Code = val.Code
|
|
151
|
+
AND per.PerEndDt = val.PerEndDt
|
|
152
|
+
AND per.PerTypeCode = (
|
|
153
|
+
CASE
|
|
154
|
+
WHEN val.PerTypeCode = 1 THEN 1
|
|
155
|
+
WHEN val.PerTypeCode in (2,3,4,5) THEN 5
|
|
156
|
+
END
|
|
157
|
+
)
|
|
158
|
+
AND per.PerEnddt = (
|
|
159
|
+
SELECT TOP 1 per2.PerEndDt
|
|
160
|
+
FROM RKDFndStdPeriod AS per2
|
|
161
|
+
WHERE per.Code = per2.Code
|
|
162
|
+
AND per.PerTypeCode = per2.PerTypeCode
|
|
163
|
+
AND per.Fyr = per2.Fyr
|
|
164
|
+
AND (
|
|
165
|
+
per2.InterimNo = per.InterimNo
|
|
166
|
+
OR (
|
|
167
|
+
per2.InterimNo IS NULL AND per.InterimNo IS NULL
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
ORDER BY per2.PerEndDt DESC
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
JOIN RKDFndStdStmt AS stmt ON
|
|
174
|
+
val.Code = stmt.Code
|
|
175
|
+
AND val.PerEndDt = stmt.PerEndDt
|
|
176
|
+
AND val.PerTypeCode = stmt.PerTypeCode
|
|
177
|
+
AND val.StmtDt = stmt.StmtDt
|
|
178
|
+
AND val.StmtTypeCode = stmt.StmtTypeCode
|
|
179
|
+
AND stmt.CompStmtCode = 1
|
|
180
|
+
AND stmt.StmtDt = (
|
|
181
|
+
select top 1 stmt2.StmtDt
|
|
182
|
+
from RKDFndStdStmt as stmt2
|
|
183
|
+
where stmt2.Code = stmt.Code
|
|
184
|
+
and stmt2.PerEndDt = stmt.PerEndDt
|
|
185
|
+
and stmt2.PerTypeCode = stmt.PerTypeCode
|
|
186
|
+
and stmt2.StmtTypeCode = stmt.StmtTypeCode
|
|
187
|
+
and stmt2.CompStmtCode = stmt.CompStmtCode
|
|
188
|
+
order by stmt2.StmtDt desc
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
JOIN RKDFndStdPerFiling AS fil ON
|
|
192
|
+
stmt.Code = fil.Code
|
|
193
|
+
AND stmt.PerEndDt = fil.PerEndDt
|
|
194
|
+
AND stmt.PerTypeCode = fil.PerTypeCode
|
|
195
|
+
AND stmt.StmtDt = fil.StmtDt
|
|
196
|
+
|
|
197
|
+
LEFT JOIN RKDFndStdItem AS item ON
|
|
198
|
+
val.Item = item.Item
|
|
199
|
+
|
|
200
|
+
LEFT JOIN RKDFndCode AS code ON
|
|
201
|
+
fil.CurrConvToCode = code.Code
|
|
202
|
+
AND code.Type_ = 58
|
|
203
|
+
|
|
204
|
+
WHERE
|
|
205
|
+
val.Code in (
|
|
206
|
+
{% for instrument in instruments %}
|
|
207
|
+
{{instrument}} {% if not loop.last %}, {% endif %}
|
|
208
|
+
{% endfor %})
|
|
209
|
+
{% if statement_type %} AND val.StmtTypeCode = {{ statement_type }}{% endif %}
|
|
210
|
+
{% if from_year %}AND per.Fyr >= {{ from_year }} {% endif %}
|
|
211
|
+
{% if to_year %}AND per.Fyr <= {{ to_year }} {% endif %}
|
|
212
|
+
{% if from_date %}AND val.PerEndDt >= {{ from_date }} {% endif %}
|
|
213
|
+
{% if to_date %}AND val.PerEndDt <= {{ to_date }} {% endif %}
|
|
214
|
+
{% if period_type == 'interim' %}AND fil.InterimNo > 0{% elif period_type == 'annual' %}AND (fil.InterimNo = 0 OR fil.InterimNo IS NULL){% endif %}
|
|
215
|
+
{% if external_codes %}
|
|
216
|
+
AND item.COA in (
|
|
217
|
+
{% for external_code in external_codes %}
|
|
218
|
+
{{ external_code }} {% if not loop.last %}, {% endif %}
|
|
219
|
+
{% endfor %}
|
|
220
|
+
)
|
|
221
|
+
{% endif %}
|
|
222
|
+
ORDER BY item.LineID
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class RKDStatementsDataloader(StatementsProtocol, Dataloader):
|
|
227
|
+
def statements(
|
|
228
|
+
self,
|
|
229
|
+
statement_type: StatementType | None = None,
|
|
230
|
+
from_date: date | None = None,
|
|
231
|
+
to_date: date | None = None,
|
|
232
|
+
from_year: int | None = None,
|
|
233
|
+
to_year: int | None = None,
|
|
234
|
+
period_type: PeriodType = PeriodType.ALL,
|
|
235
|
+
data_type: DataType = DataType.STANDARDIZED,
|
|
236
|
+
financials: list[Financial] | None = None,
|
|
237
|
+
target_currency: str | None = None,
|
|
238
|
+
) -> Iterator[StatementDataDict]:
|
|
239
|
+
lookup = {k: v for k, v in self.entities.values_list("dl_parameters__statements__parameters", "id")}
|
|
240
|
+
sql = reported_sql if data_type is DataType.REPORTED else standardized_sql
|
|
241
|
+
query, bind_params = JinjaSql(param_style="format").prepare_query(
|
|
242
|
+
sql,
|
|
243
|
+
{
|
|
244
|
+
"instruments": lookup.keys(),
|
|
245
|
+
"statement_type": RKDStatementType[statement_type.name].value if statement_type else None,
|
|
246
|
+
"from_year": from_year,
|
|
247
|
+
"to_year": to_year,
|
|
248
|
+
"from_date": from_date,
|
|
249
|
+
"to_date": to_date,
|
|
250
|
+
"period_type": period_type.value,
|
|
251
|
+
"external_codes": [RKDFinancial[fin.name].value for fin in financials or []],
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
with connections["qa"].cursor() as cursor:
|
|
255
|
+
cursor.execute(
|
|
256
|
+
query,
|
|
257
|
+
bind_params,
|
|
258
|
+
)
|
|
259
|
+
for row in dictfetchall(cursor):
|
|
260
|
+
if row["interim"] is None:
|
|
261
|
+
row["interim"] = 0
|
|
262
|
+
# sometime we get None for the year. We default to the period end date year then
|
|
263
|
+
row["year"] = int(row["year"] or row["period_end_date"].year)
|
|
264
|
+
row["instrument_id"] = lookup[row["external_identifier"]]
|
|
265
|
+
if financials:
|
|
266
|
+
row["financial"] = Financial[RKDFinancial(row["external_code"]).name].value
|
|
267
|
+
yield row
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from typing import Any, Generic, Iterable, TypedDict, TypeVar, cast
|
|
3
|
+
|
|
4
|
+
from django.core.cache import cache
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T", bound=TypedDict)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Cache(Generic[T]):
|
|
10
|
+
"""
|
|
11
|
+
A Cache Class to handle 3-Dimensional data (identifier-key-value)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
identifier_key: str = "instrument_id",
|
|
17
|
+
symbol_key: str = "factor_code",
|
|
18
|
+
value_key: str = "value",
|
|
19
|
+
timeout: int | None = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Constructor of the Cache Class
|
|
23
|
+
Args:
|
|
24
|
+
identifier_key: The lookup identifier field. Default to `instrument_id`
|
|
25
|
+
symbol_key: The symbol lookup field. Default to `factor_code`
|
|
26
|
+
value_key: The value lookup field. Default to `value`
|
|
27
|
+
timeout: The cache timeout configuration value. Default to None (never expired)
|
|
28
|
+
"""
|
|
29
|
+
self.identifier_key = identifier_key
|
|
30
|
+
self.symbol_key = symbol_key
|
|
31
|
+
self.value_key = value_key
|
|
32
|
+
self.timeout = timeout
|
|
33
|
+
self.id_symbol_pairs: list[tuple[str, str]] = []
|
|
34
|
+
self.missing_keys = []
|
|
35
|
+
self.missing_ids = set()
|
|
36
|
+
self.missing_symbols = set()
|
|
37
|
+
|
|
38
|
+
def _get_cache_key(self, id: str, symbol: str) -> str:
|
|
39
|
+
"""
|
|
40
|
+
Generate the key used in the caching layer based on the given ID and symbol.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
id (str): The ID to be used in the cache key.
|
|
44
|
+
symbol (str): The symbol to be used in the cache key.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
str: A cache key in the format "id_symbol" where both the ID and symbol
|
|
48
|
+
are in lowercase.
|
|
49
|
+
"""
|
|
50
|
+
return f"{str(id).lower()}_{symbol.lower()}"
|
|
51
|
+
|
|
52
|
+
def _deserialize_cache(self, id: str, symbol: str, value: Any) -> T:
|
|
53
|
+
"""
|
|
54
|
+
Data retreived from cache is deserialized using this method. We expect a key value format which is converted into a dictionary with keys as identifier, symbol and value
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
id: identifier
|
|
58
|
+
symbol: symbol
|
|
59
|
+
value: value
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
a dictionary with keys as identifier, symbol and value
|
|
63
|
+
"""
|
|
64
|
+
res = {self.identifier_key: int(id), self.symbol_key: symbol, self.value_key: value}
|
|
65
|
+
return cast(T, res)
|
|
66
|
+
|
|
67
|
+
def initialize(self, ids: list[int], symbols: list[str]):
|
|
68
|
+
"""
|
|
69
|
+
Initialize the instance with a list of IDs and symbols.
|
|
70
|
+
|
|
71
|
+
This method takes a list of identifiers and a list of symbols and creates a list of
|
|
72
|
+
tuples containing all possible pairs of identifiers (converted to strings) and symbols.
|
|
73
|
+
It also initializes sets for symbol and identifiers not present in cache
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
ids (list[int]): A list of integer IDs.
|
|
77
|
+
symbols (list[str]): A list of symbol strings.
|
|
78
|
+
"""
|
|
79
|
+
self.id_symbol_pairs = list(map(lambda x: (str(x[0]), x[1]), itertools.product(ids, symbols)))
|
|
80
|
+
self.missing_ids = set()
|
|
81
|
+
self.missing_symbols = set()
|
|
82
|
+
self.missing_keys = list()
|
|
83
|
+
|
|
84
|
+
def fetch_from_cache(self) -> Iterable[T]:
|
|
85
|
+
"""
|
|
86
|
+
Fetch values already stored in the cache.
|
|
87
|
+
|
|
88
|
+
This method iterates over all identifiers and symbols pair and return the cached value if it exists or mark this pair as missing
|
|
89
|
+
|
|
90
|
+
Yields:
|
|
91
|
+
T: Deserialized cached values.
|
|
92
|
+
"""
|
|
93
|
+
sentinel = object()
|
|
94
|
+
for id, symbol in self.id_symbol_pairs:
|
|
95
|
+
key = self._get_cache_key(id, symbol)
|
|
96
|
+
cached_value = cache.get(key, sentinel)
|
|
97
|
+
if cached_value is not None and cached_value is not sentinel:
|
|
98
|
+
yield self._deserialize_cache(id, symbol, cached_value)
|
|
99
|
+
elif (
|
|
100
|
+
cached_value is sentinel
|
|
101
|
+
): # otherwise, it's literal None and then it means the cache contains "None" for that key
|
|
102
|
+
self.missing_symbols.add(symbol)
|
|
103
|
+
self.missing_ids.add(id)
|
|
104
|
+
self.missing_keys.append(key)
|
|
105
|
+
|
|
106
|
+
def write(self, row: T) -> T:
|
|
107
|
+
"""
|
|
108
|
+
Write given row into the cache
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
row: a dictionary typed object
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
a dictionary typed object
|
|
115
|
+
"""
|
|
116
|
+
if (key := row.get(self.identifier_key)) and (symbol := row.get(self.symbol_key)):
|
|
117
|
+
value = row.get(self.value_key)
|
|
118
|
+
key = self._get_cache_key(str(key), str(symbol))
|
|
119
|
+
cache.set(key, value, timeout=self.timeout)
|
|
120
|
+
if key in self.missing_keys: # mark row's key are "handled"
|
|
121
|
+
self.missing_keys.remove(key)
|
|
122
|
+
return row
|
|
123
|
+
|
|
124
|
+
def close(self):
|
|
125
|
+
"""
|
|
126
|
+
Close cache stream by fixing missing keys not handled as None value in the cache (it shows that the value was actually fetched but didn't returned any value)
|
|
127
|
+
"""
|
|
128
|
+
for key in self.missing_keys:
|
|
129
|
+
cache.set(key, None, timeout=self.timeout)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Iterator, Protocol
|
|
3
|
+
|
|
4
|
+
from wbfdm.dataloaders.types import (
|
|
5
|
+
AdjustmentDataDict,
|
|
6
|
+
CorporateActionDataDict,
|
|
7
|
+
ESGControversyDataDict,
|
|
8
|
+
ESGDataDict,
|
|
9
|
+
FinancialDataDict,
|
|
10
|
+
MarketDataDict,
|
|
11
|
+
OfficerDataDict,
|
|
12
|
+
ReportDateDataDict,
|
|
13
|
+
StatementDataDict,
|
|
14
|
+
)
|
|
15
|
+
from wbfdm.enums import (
|
|
16
|
+
ESG,
|
|
17
|
+
CalendarType,
|
|
18
|
+
DataType,
|
|
19
|
+
EstimateType,
|
|
20
|
+
Financial,
|
|
21
|
+
Frequency,
|
|
22
|
+
MarketData,
|
|
23
|
+
PeriodType,
|
|
24
|
+
SeriesType,
|
|
25
|
+
StatementType,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ReportDateProtocol(Protocol):
|
|
30
|
+
def reporting_dates(self, only_next: bool = True) -> Iterator[ReportDateDataDict]:
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AdjustmentsProtocol(Protocol):
|
|
35
|
+
def adjustments(self, from_date: date, to_date: date) -> Iterator[AdjustmentDataDict]:
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MarketDataProtocol(Protocol):
|
|
40
|
+
def market_data(
|
|
41
|
+
self,
|
|
42
|
+
values: list[MarketData] | None = [MarketData.CLOSE],
|
|
43
|
+
from_date: date | None = None,
|
|
44
|
+
to_date: date | None = None,
|
|
45
|
+
exact_date: date | None = None,
|
|
46
|
+
frequency: Frequency = Frequency.DAILY,
|
|
47
|
+
target_currency: str | None = None,
|
|
48
|
+
) -> Iterator[MarketDataDict]:
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CorporateActionsProtocol(Protocol):
|
|
53
|
+
def corporate_actions(
|
|
54
|
+
self, from_date: date | None = None, to_date: date | None = None
|
|
55
|
+
) -> Iterator[CorporateActionDataDict]:
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class OfficersProtocol(Protocol):
|
|
60
|
+
def officers(self) -> Iterator[OfficerDataDict]:
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class StatementsProtocol(Protocol):
|
|
65
|
+
def statements(
|
|
66
|
+
self,
|
|
67
|
+
statement_type: StatementType | None = None,
|
|
68
|
+
from_date: date | None = None,
|
|
69
|
+
to_date: date | None = None,
|
|
70
|
+
from_year: int | None = None,
|
|
71
|
+
to_year: int | None = None,
|
|
72
|
+
period_type: PeriodType = PeriodType.ALL,
|
|
73
|
+
data_type: DataType = DataType.STANDARDIZED,
|
|
74
|
+
financials: list[Financial] | None = None,
|
|
75
|
+
target_currency: str | None = None,
|
|
76
|
+
) -> Iterator[StatementDataDict]:
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class FinancialsProtocol(Protocol):
|
|
81
|
+
def financials(
|
|
82
|
+
self,
|
|
83
|
+
values: list[Financial],
|
|
84
|
+
from_date: date | None = None,
|
|
85
|
+
to_date: date | None = None,
|
|
86
|
+
from_year: int | None = None,
|
|
87
|
+
to_year: int | None = None,
|
|
88
|
+
from_index: int | None = None,
|
|
89
|
+
to_index: int | None = None,
|
|
90
|
+
from_valid: date | None = None,
|
|
91
|
+
to_valid: date | None = None,
|
|
92
|
+
period_type: PeriodType = PeriodType.ANNUAL,
|
|
93
|
+
calendar_type: CalendarType = CalendarType.FISCAL,
|
|
94
|
+
series_type: SeriesType = SeriesType.COMPLETE,
|
|
95
|
+
data_type: DataType = DataType.STANDARDIZED,
|
|
96
|
+
estimate_type: EstimateType = EstimateType.VALID,
|
|
97
|
+
target_currency: str | None = None,
|
|
98
|
+
) -> Iterator[FinancialDataDict]:
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ESGControversyProtocol(Protocol):
|
|
103
|
+
def esg_controversies(self) -> Iterator[ESGControversyDataDict]:
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class ESGProtocol(Protocol):
|
|
108
|
+
def esg(
|
|
109
|
+
self,
|
|
110
|
+
values: list[ESG],
|
|
111
|
+
) -> Iterator[ESGDataDict]:
|
|
112
|
+
...
|