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,804 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
# class FundamentalComputedMixin:
|
|
9
|
+
# def _compute_revenue_growth(self) -> float:
|
|
10
|
+
# if (
|
|
11
|
+
# self.revenue is not None
|
|
12
|
+
# and (last_fundamental := self.last_fundamental)
|
|
13
|
+
# and (last_revenue := last_fundamental.revenue)
|
|
14
|
+
# ):
|
|
15
|
+
# return self.revenue / last_revenue - 1
|
|
16
|
+
#
|
|
17
|
+
# def _compute_gross_profit(self) -> float:
|
|
18
|
+
# if self.revenue is not None and self.cost_of_good_sold is not None:
|
|
19
|
+
# return self.revenue - self.cost_of_good_sold
|
|
20
|
+
#
|
|
21
|
+
# def _compute_gross_profit_margin(self) -> float:
|
|
22
|
+
# if (
|
|
23
|
+
# self.gross_profit is not None
|
|
24
|
+
# and self.revenue
|
|
25
|
+
# and self.deprecation_and_amortization_to_sales_ratio is not None
|
|
26
|
+
# ):
|
|
27
|
+
# return (self.gross_profit / self.revenue) + self.deprecation_and_amortization_to_sales_ratio
|
|
28
|
+
#
|
|
29
|
+
# def _compute_ebitda_margin(self) -> float:
|
|
30
|
+
# if self.ebitda is not None and self.revenue:
|
|
31
|
+
# return self.ebitda / self.revenue
|
|
32
|
+
#
|
|
33
|
+
# def _compute_ebit_margin(self) -> float:
|
|
34
|
+
# if self.ebit is not None and self.revenue:
|
|
35
|
+
# return self.ebit / self.revenue
|
|
36
|
+
#
|
|
37
|
+
# def _compute_net_profit_margin(self) -> float:
|
|
38
|
+
# if self.net_profit is not None and self.revenue:
|
|
39
|
+
# return self.net_profit / self.revenue
|
|
40
|
+
#
|
|
41
|
+
# def _compute_cost_of_good_sold(self) -> float:
|
|
42
|
+
# if self.cost_of_good_sold_without_depreciation is not None and self.deprecation_and_amortization is not None:
|
|
43
|
+
# return self.cost_of_good_sold_without_depreciation + self.deprecation_and_amortization
|
|
44
|
+
#
|
|
45
|
+
# def _compute_eps_growth(self) -> float:
|
|
46
|
+
# if self.eps is not None and (last_fundamental := self.last_fundamental) and (last_eps := last_fundamental.eps):
|
|
47
|
+
# return self.eps / last_eps - 1
|
|
48
|
+
#
|
|
49
|
+
# def _compute_sga_to_sales_ratio(self) -> float:
|
|
50
|
+
# if self.sga is not None and self.revenue:
|
|
51
|
+
# return self.sga / self.revenue
|
|
52
|
+
#
|
|
53
|
+
# def _compute_interest_expense_to_sales_ratio(self) -> float:
|
|
54
|
+
# if self.interest_expense is not None and self.revenue:
|
|
55
|
+
# return self.interest_expense / self.revenue
|
|
56
|
+
#
|
|
57
|
+
# def _compute_deprecation_and_amortization_to_sales_ratio(self) -> float:
|
|
58
|
+
# if self.deprecation_and_amortization is not None and self.revenue:
|
|
59
|
+
# return self.deprecation_and_amortization / self.revenue
|
|
60
|
+
#
|
|
61
|
+
# def _compute_free_cash(self) -> float:
|
|
62
|
+
# if self.cash_from_operation is not None and self.capital_expenditures is not None:
|
|
63
|
+
# return self.cash_from_operation - self.capital_expenditures
|
|
64
|
+
#
|
|
65
|
+
# def _compute_free_cash_flow(self):
|
|
66
|
+
# if (
|
|
67
|
+
# self.free_cash is not None
|
|
68
|
+
# and (price := self.last_price)
|
|
69
|
+
# and (outstanding_shares_consolidated := price.outstanding_shares_consolidated)
|
|
70
|
+
# ):
|
|
71
|
+
# return self.free_cash / float(outstanding_shares_consolidated)
|
|
72
|
+
#
|
|
73
|
+
# def _compute_free_cash_flow_growth(self) -> float:
|
|
74
|
+
# if (
|
|
75
|
+
# self.free_cash_flow is not None
|
|
76
|
+
# and (last_fundamental := self.last_fundamental)
|
|
77
|
+
# and (last_free_cash_flow := last_fundamental.free_cash_flow)
|
|
78
|
+
# ):
|
|
79
|
+
# return self.free_cash_flow / last_free_cash_flow - 1
|
|
80
|
+
#
|
|
81
|
+
# def _compute_free_cash_flow_to_sales_ratio(self) -> float:
|
|
82
|
+
# if self.free_cash_flow is not None and self.revenue:
|
|
83
|
+
# return self.free_cash_flow / self.revenue
|
|
84
|
+
#
|
|
85
|
+
# def _compute_book_value_per_share(self) -> float:
|
|
86
|
+
# if self.total_assets is not None and self.total_debt is not None:
|
|
87
|
+
# if (
|
|
88
|
+
# yearly_valuations := self.instrument.valuations.exclude(outstanding_shares_consolidated=0).filter(
|
|
89
|
+
# date__year=self.year,
|
|
90
|
+
# date__lte=self.date_range.upper,
|
|
91
|
+
# outstanding_shares__isnull=False,
|
|
92
|
+
# )
|
|
93
|
+
# ).exists() and (
|
|
94
|
+
# last_outstanding_shares := yearly_valuations.latest("date").outstanding_shares_consolidated
|
|
95
|
+
# ):
|
|
96
|
+
# return (self.total_assets - self.total_debt) / float(last_outstanding_shares)
|
|
97
|
+
#
|
|
98
|
+
# def _compute_net_change_in_cash(self) -> float:
|
|
99
|
+
# if (
|
|
100
|
+
# self.cash_from_operation is not None
|
|
101
|
+
# and self.investment_cash is not None
|
|
102
|
+
# and self.financing_cash is not None
|
|
103
|
+
# ):
|
|
104
|
+
# return self.cash_from_operation - self.investment_cash + self.financing_cash
|
|
105
|
+
#
|
|
106
|
+
# def _compute_employee_count_growth(self) -> float:
|
|
107
|
+
# if self.employee_count is not None and (previous := self.last_fundamental) and previous.employee_count:
|
|
108
|
+
# return self.employee_count / previous.employee_count - 1
|
|
109
|
+
#
|
|
110
|
+
# def _compute_capex_to_sales(self) -> float:
|
|
111
|
+
# if self.capital_expenditures is not None and self.revenue:
|
|
112
|
+
# return self.capital_expenditures / self.revenue
|
|
113
|
+
#
|
|
114
|
+
# def _compute_net_debt_to_ebitda_ratio(self) -> float:
|
|
115
|
+
# if self.net_debt is not None and self.ebitda:
|
|
116
|
+
# return self.net_debt / self.ebitda
|
|
117
|
+
#
|
|
118
|
+
# def _compute_burn_rate(self) -> float:
|
|
119
|
+
# if self.net_change_in_cash is not None:
|
|
120
|
+
# return min(self.net_change_in_cash / 12, 0)
|
|
121
|
+
#
|
|
122
|
+
# def _compute_operating_burn_rate(self) -> float:
|
|
123
|
+
# if self.cash_from_operation is not None:
|
|
124
|
+
# return min(self.cash_from_operation / 12, 0)
|
|
125
|
+
#
|
|
126
|
+
# def _compute_free_cash_burn_rate(self) -> float:
|
|
127
|
+
# if self.free_cash is not None:
|
|
128
|
+
# return min(self.free_cash / 12, 0)
|
|
129
|
+
#
|
|
130
|
+
# def _compute_cash_reserve_to_burn_rate_ratio(self) -> float:
|
|
131
|
+
# if self.cash_and_short_term_investments is not None and self.burn_rate:
|
|
132
|
+
# return abs(self.cash_and_short_term_investments / self.burn_rate / 12)
|
|
133
|
+
#
|
|
134
|
+
# def _compute_cash_reserve_to_operating_burn_rate_ratio(self) -> float:
|
|
135
|
+
# if self.cash_and_short_term_investments is not None and self.operating_burn_rate:
|
|
136
|
+
# return abs(self.cash_and_short_term_investments / self.operating_burn_rate / 12)
|
|
137
|
+
#
|
|
138
|
+
# def _compute_cash_reserve_to_free_cash_burn_rate_ratio(self) -> float:
|
|
139
|
+
# if self.cash_and_short_term_investments is not None and self.free_cash_burn_rate:
|
|
140
|
+
# return abs(self.cash_and_short_term_investments / self.free_cash_burn_rate / 12)
|
|
141
|
+
#
|
|
142
|
+
# def _compute_working_capital_to_burn_rate_ratio(self) -> float:
|
|
143
|
+
# if self.working_capital is not None and self.burn_rate:
|
|
144
|
+
# if self.working_capital <= 0:
|
|
145
|
+
# return None
|
|
146
|
+
# return abs(self.working_capital / self.burn_rate / 12)
|
|
147
|
+
#
|
|
148
|
+
# def _compute_working_capital_to_operating_burn_rate_ratio(self) -> float:
|
|
149
|
+
# if self.working_capital is not None and self.operating_burn_rate:
|
|
150
|
+
# if self.working_capital <= 0:
|
|
151
|
+
# return None
|
|
152
|
+
# return abs(self.working_capital / self.operating_burn_rate / 12)
|
|
153
|
+
#
|
|
154
|
+
# def _compute_working_capital_to_free_cash_burn_rate_ratio(self) -> float:
|
|
155
|
+
# if self.working_capital is not None and self.free_cash_burn_rate:
|
|
156
|
+
# if self.working_capital <= 0:
|
|
157
|
+
# return None
|
|
158
|
+
# return abs(self.working_capital / self.free_cash_burn_rate / 12)
|
|
159
|
+
#
|
|
160
|
+
# def _compute_current_ratio(self) -> float:
|
|
161
|
+
# if self.current_assets is not None and self.current_liabilities:
|
|
162
|
+
# return self.current_assets / self.current_liabilities
|
|
163
|
+
#
|
|
164
|
+
# def _compute_cash_and_short_term_investments_to_current_assets_ratio(self) -> float:
|
|
165
|
+
# if self.cash_and_short_term_investments is not None and self.current_assets:
|
|
166
|
+
# return self.cash_and_short_term_investments / self.current_assets
|
|
167
|
+
#
|
|
168
|
+
# def _compute_rd_to_sales_ratio(self) -> float:
|
|
169
|
+
# if self.cost_research_development is not None and self.revenue:
|
|
170
|
+
# return self.cost_research_development / self.revenue
|
|
171
|
+
#
|
|
172
|
+
# def _compute_interest_coverage_ratio(self) -> float:
|
|
173
|
+
# if self.ebit is not None and self.interest_expense:
|
|
174
|
+
# return self.ebit / self.interest_expense
|
|
175
|
+
#
|
|
176
|
+
# def _compute_return_on_equity(self) -> float:
|
|
177
|
+
# if (
|
|
178
|
+
# (previous := self.last_fundamental)
|
|
179
|
+
# and self.net_profit is not None
|
|
180
|
+
# and self.shareholder_equity is not None
|
|
181
|
+
# and (previous_shareholder := previous.shareholder_equity) is not None
|
|
182
|
+
# ):
|
|
183
|
+
# avg_shareholder = (self.shareholder_equity + previous_shareholder) / 2
|
|
184
|
+
# if avg_shareholder:
|
|
185
|
+
# return self.net_profit / avg_shareholder
|
|
186
|
+
#
|
|
187
|
+
# def _compute_return_on_assets(self) -> float:
|
|
188
|
+
# if (
|
|
189
|
+
# (previous := self.last_fundamental)
|
|
190
|
+
# and self.net_profit is not None
|
|
191
|
+
# and self.total_assets is not None
|
|
192
|
+
# and (previous_total_assets := previous.total_assets) is not None
|
|
193
|
+
# ):
|
|
194
|
+
# avg_total_assets = (self.total_assets + previous_total_assets) / 2
|
|
195
|
+
# if avg_total_assets:
|
|
196
|
+
# return self.net_profit / avg_total_assets
|
|
197
|
+
#
|
|
198
|
+
# def _compute_return_on_capital_employed(self) -> float:
|
|
199
|
+
# if (
|
|
200
|
+
# self.ebit is not None
|
|
201
|
+
# and self.total_assets
|
|
202
|
+
# and self.current_liabilities
|
|
203
|
+
# and (diff := self.total_assets - self.current_liabilities)
|
|
204
|
+
# ):
|
|
205
|
+
# return self.ebit / diff
|
|
206
|
+
#
|
|
207
|
+
# def _compute_return_on_invested_capital(self) -> float:
|
|
208
|
+
# if (
|
|
209
|
+
# self.ebit is not None
|
|
210
|
+
# and self.company_tax_rate is not None
|
|
211
|
+
# and self.shareholder_equity is not None
|
|
212
|
+
# and self.total_liabilities is not None
|
|
213
|
+
# and self.cash_and_cash_equivalents is not None
|
|
214
|
+
# and (previous := self.last_fundamental)
|
|
215
|
+
# ):
|
|
216
|
+
# if (
|
|
217
|
+
# previous.shareholder_equity is not None
|
|
218
|
+
# and previous.total_liabilities is not None
|
|
219
|
+
# and previous.cash_and_cash_equivalents is not None
|
|
220
|
+
# ):
|
|
221
|
+
# divisor = (
|
|
222
|
+
# self.shareholder_equity
|
|
223
|
+
# + self.total_liabilities
|
|
224
|
+
# - self.cash_and_cash_equivalents
|
|
225
|
+
# + previous.shareholder_equity
|
|
226
|
+
# + previous.total_liabilities
|
|
227
|
+
# - previous.cash_and_cash_equivalents
|
|
228
|
+
# ) / 2
|
|
229
|
+
# if divisor:
|
|
230
|
+
# return self.ebit * (1 - self.company_tax_rate) / divisor
|
|
231
|
+
#
|
|
232
|
+
# def _compute_revenue_to_employee_ratio(self) -> float:
|
|
233
|
+
# if self.revenue is not None and self.employee_count:
|
|
234
|
+
# return self.revenue / self.employee_count
|
|
235
|
+
#
|
|
236
|
+
#
|
|
237
|
+
# class GeographicSegmentComputedMixin:
|
|
238
|
+
# def _compute_value_growth(self) -> float:
|
|
239
|
+
# if self.value is not None and (previous_segment := self.last_geographic_segment) and previous_segment.value:
|
|
240
|
+
# return self.value / previous_segment.value - 1
|
|
241
|
+
#
|
|
242
|
+
#
|
|
243
|
+
# class ForecastComputedMixin:
|
|
244
|
+
# def _compute_revenue_growth_y1(self) -> float:
|
|
245
|
+
# if (fundamental := self.yearly_fundamental) and self.revenue_y1 is not None and fundamental.revenue:
|
|
246
|
+
# return self.revenue_y1 / fundamental.revenue - 1
|
|
247
|
+
#
|
|
248
|
+
# def _compute_revenue_growth_y2(self) -> float:
|
|
249
|
+
# if self.revenue_y2 is not None and self.revenue_y1:
|
|
250
|
+
# return self.revenue_y2 / self.revenue_y1 - 1
|
|
251
|
+
#
|
|
252
|
+
# def _compute_revenue_growth_y3(self) -> float:
|
|
253
|
+
# if self.revenue_y3 is not None and self.revenue_y2:
|
|
254
|
+
# return self.revenue_y3 / self.revenue_y2 - 1
|
|
255
|
+
#
|
|
256
|
+
# def _compute_revenue_growth_y4(self) -> float:
|
|
257
|
+
# if self.revenue_y4 is not None and self.revenue_y3:
|
|
258
|
+
# return self.revenue_y4 / self.revenue_y3 - 1
|
|
259
|
+
#
|
|
260
|
+
# def _compute_revenue_growth_y5(self) -> float:
|
|
261
|
+
# if self.revenue_y5 is not None and self.revenue_y4:
|
|
262
|
+
# return self.revenue_y5 / self.revenue_y4 - 1
|
|
263
|
+
#
|
|
264
|
+
# def _compute_deprecation_and_amortization_y1(self):
|
|
265
|
+
# if self.ebitda_margin_y1 is not None and self.ebit_margin_y1 is not None:
|
|
266
|
+
# return self.ebitda_margin_y1 - self.ebit_margin_y1
|
|
267
|
+
#
|
|
268
|
+
# def _compute_deprecation_and_amortization_y2(self):
|
|
269
|
+
# if self.ebitda_margin_y2 is not None and self.ebit_margin_y2 is not None:
|
|
270
|
+
# return self.ebitda_margin_y2 - self.ebit_margin_y2
|
|
271
|
+
#
|
|
272
|
+
# def _compute_deprecation_and_amortization_y3(self):
|
|
273
|
+
# if self.ebitda_margin_y3 is not None and self.ebit_margin_y3 is not None:
|
|
274
|
+
# return self.ebitda_margin_y3 - self.ebit_margin_y3
|
|
275
|
+
#
|
|
276
|
+
# def _compute_deprecation_and_amortization_y4(self):
|
|
277
|
+
# if self.ebitda_margin_y4 is not None and self.ebit_margin_y4 is not None:
|
|
278
|
+
# return self.ebitda_margin_y4 - self.ebit_margin_y4
|
|
279
|
+
#
|
|
280
|
+
# def _compute_deprecation_and_amortization_y5(self):
|
|
281
|
+
# if self.ebitda_margin_y5 is not None and self.ebit_margin_y5 is not None:
|
|
282
|
+
# return self.ebitda_margin_y5 - self.ebit_margin_y5
|
|
283
|
+
#
|
|
284
|
+
# def _compute_gross_profit_margin_y1(self):
|
|
285
|
+
# if (
|
|
286
|
+
# self.gross_profit_margin_without_depreciation_y1 is not None
|
|
287
|
+
# and self.deprecation_and_amortization_y1 is not None
|
|
288
|
+
# ):
|
|
289
|
+
# return self.gross_profit_margin_without_depreciation_y1 + self.deprecation_and_amortization_y1
|
|
290
|
+
#
|
|
291
|
+
# def _compute_gross_profit_margin_y2(self):
|
|
292
|
+
# if (
|
|
293
|
+
# self.gross_profit_margin_without_depreciation_y2 is not None
|
|
294
|
+
# and self.deprecation_and_amortization_y2 is not None
|
|
295
|
+
# ):
|
|
296
|
+
# return self.gross_profit_margin_without_depreciation_y2 + self.deprecation_and_amortization_y2
|
|
297
|
+
#
|
|
298
|
+
# def _compute_gross_profit_margin_y3(self):
|
|
299
|
+
# if (
|
|
300
|
+
# self.gross_profit_margin_without_depreciation_y3 is not None
|
|
301
|
+
# and self.deprecation_and_amortization_y3 is not None
|
|
302
|
+
# ):
|
|
303
|
+
# return self.gross_profit_margin_without_depreciation_y3 + self.deprecation_and_amortization_y3
|
|
304
|
+
#
|
|
305
|
+
# def _compute_gross_profit_margin_y4(self):
|
|
306
|
+
# if (
|
|
307
|
+
# self.gross_profit_margin_without_depreciation_y4 is not None
|
|
308
|
+
# and self.deprecation_and_amortization_y4 is not None
|
|
309
|
+
# ):
|
|
310
|
+
# return self.gross_profit_margin_without_depreciation_y4 + self.deprecation_and_amortization_y4
|
|
311
|
+
#
|
|
312
|
+
# def _compute_gross_profit_margin_y5(self):
|
|
313
|
+
# if (
|
|
314
|
+
# self.gross_profit_margin_without_depreciation_y5 is not None
|
|
315
|
+
# and self.deprecation_and_amortization_y5 is not None
|
|
316
|
+
# ):
|
|
317
|
+
# return self.gross_profit_margin_without_depreciation_y5 + self.deprecation_and_amortization_y5
|
|
318
|
+
#
|
|
319
|
+
# def _compute_gross_profit_y1(self):
|
|
320
|
+
# if self.gross_profit_margin_y1 is not None and self.revenue_y1 is not None:
|
|
321
|
+
# return self.gross_profit_margin_y1 * self.revenue_y1
|
|
322
|
+
#
|
|
323
|
+
# def _compute_gross_profit_y2(self):
|
|
324
|
+
# if self.gross_profit_margin_y2 is not None and self.revenue_y2 is not None:
|
|
325
|
+
# return self.gross_profit_margin_y2 * self.revenue_y2
|
|
326
|
+
#
|
|
327
|
+
# def _compute_gross_profit_y3(self):
|
|
328
|
+
# if self.gross_profit_margin_y3 is not None and self.revenue_y3 is not None:
|
|
329
|
+
# return self.gross_profit_margin_y3 * self.revenue_y3
|
|
330
|
+
#
|
|
331
|
+
# def _compute_gross_profit_y4(self):
|
|
332
|
+
# if self.gross_profit_margin_y4 is not None and self.revenue_y4 is not None:
|
|
333
|
+
# return self.gross_profit_margin_y4 * self.revenue_y4
|
|
334
|
+
#
|
|
335
|
+
# def _compute_gross_profit_y5(self):
|
|
336
|
+
# if self.gross_profit_margin_y5 is not None and self.revenue_y5 is not None:
|
|
337
|
+
# return self.gross_profit_margin_y5 * self.revenue_y5
|
|
338
|
+
#
|
|
339
|
+
# def _compute_net_profit_margin_y1(self) -> float:
|
|
340
|
+
# if self.reported_net_profit_y1 is not None and self.revenue_y1:
|
|
341
|
+
# return self.reported_net_profit_y1 / self.revenue_y1
|
|
342
|
+
#
|
|
343
|
+
# def _compute_net_profit_margin_y2(self) -> float:
|
|
344
|
+
# if self.reported_net_profit_y2 is not None and self.revenue_y2:
|
|
345
|
+
# return self.reported_net_profit_y2 / self.revenue_y2
|
|
346
|
+
#
|
|
347
|
+
# def _compute_net_profit_margin_y3(self) -> float:
|
|
348
|
+
# if self.reported_net_profit_y3 is not None and self.revenue_y3:
|
|
349
|
+
# return self.reported_net_profit_y3 / self.revenue_y3
|
|
350
|
+
#
|
|
351
|
+
# def _compute_net_profit_margin_y4(self) -> float:
|
|
352
|
+
# if self.reported_net_profit_y4 is not None and self.revenue_y4:
|
|
353
|
+
# return self.reported_net_profit_y4 / self.revenue_y4
|
|
354
|
+
#
|
|
355
|
+
# def _compute_net_profit_margin_y5(self) -> float:
|
|
356
|
+
# if self.reported_net_profit_y5 is not None and self.revenue_y5:
|
|
357
|
+
# return self.reported_net_profit_y5 / self.revenue_y5
|
|
358
|
+
#
|
|
359
|
+
# def _compute_ebitda_margin_y1(self) -> float:
|
|
360
|
+
# if self.ebitda_y1 is not None and self.revenue_y1:
|
|
361
|
+
# return self.ebitda_y1 / self.revenue_y1
|
|
362
|
+
#
|
|
363
|
+
# def _compute_ebitda_margin_y2(self) -> float:
|
|
364
|
+
# if self.ebitda_y2 is not None and self.revenue_y2:
|
|
365
|
+
# return self.ebitda_y2 / self.revenue_y2
|
|
366
|
+
#
|
|
367
|
+
# def _compute_ebitda_margin_y3(self) -> float:
|
|
368
|
+
# if self.ebitda_y3 is not None and self.revenue_y3:
|
|
369
|
+
# return self.ebitda_y3 / self.revenue_y3
|
|
370
|
+
#
|
|
371
|
+
# def _compute_ebitda_margin_y4(self) -> float:
|
|
372
|
+
# if self.ebitda_y4 is not None and self.revenue_y4:
|
|
373
|
+
# return self.ebitda_y4 / self.revenue_y4
|
|
374
|
+
#
|
|
375
|
+
# def _compute_ebitda_margin_y5(self) -> float:
|
|
376
|
+
# if self.ebitda_y5 is not None and self.revenue_y5:
|
|
377
|
+
# return self.ebitda_y5 / self.revenue_y5
|
|
378
|
+
#
|
|
379
|
+
# def _compute_ebit_margin_y1(self) -> float:
|
|
380
|
+
# if self.ebit_y1 is not None and self.revenue_y1:
|
|
381
|
+
# return self.ebit_y1 / self.revenue_y1
|
|
382
|
+
#
|
|
383
|
+
# def _compute_ebit_margin_y2(self) -> float:
|
|
384
|
+
# if self.ebit_y2 is not None and self.revenue_y2:
|
|
385
|
+
# return self.ebit_y2 / self.revenue_y2
|
|
386
|
+
#
|
|
387
|
+
# def _compute_ebit_margin_y3(self) -> float:
|
|
388
|
+
# if self.ebit_y3 is not None and self.revenue_y3:
|
|
389
|
+
# return self.ebit_y3 / self.revenue_y3
|
|
390
|
+
#
|
|
391
|
+
# def _compute_ebit_margin_y4(self) -> float:
|
|
392
|
+
# if self.ebit_y4 is not None and self.revenue_y4:
|
|
393
|
+
# return self.ebit_y4 / self.revenue_y4
|
|
394
|
+
#
|
|
395
|
+
# def _compute_ebit_margin_y5(self) -> float:
|
|
396
|
+
# if self.ebit_y5 is not None and self.revenue_y5:
|
|
397
|
+
# return self.ebit_y5 / self.revenue_y5
|
|
398
|
+
#
|
|
399
|
+
# def _compute_eps_y1(self):
|
|
400
|
+
# if self.reported_net_profit_y1 is not None and self.shares:
|
|
401
|
+
# return self.reported_net_profit_y1 / self.shares
|
|
402
|
+
#
|
|
403
|
+
# def _compute_eps_y2(self):
|
|
404
|
+
# if self.reported_net_profit_y2 is not None and self.shares:
|
|
405
|
+
# return self.reported_net_profit_y2 / self.shares
|
|
406
|
+
#
|
|
407
|
+
# def _compute_eps_y3(self):
|
|
408
|
+
# if self.reported_net_profit_y3 is not None and self.shares:
|
|
409
|
+
# return self.reported_net_profit_y3 / self.shares
|
|
410
|
+
#
|
|
411
|
+
# def _compute_eps_y4(self):
|
|
412
|
+
# if self.reported_net_profit_y4 is not None and self.shares:
|
|
413
|
+
# return self.reported_net_profit_y4 / self.shares
|
|
414
|
+
#
|
|
415
|
+
# def _compute_eps_y5(self):
|
|
416
|
+
# if self.reported_net_profit_y5 is not None and self.shares:
|
|
417
|
+
# return self.reported_net_profit_y5 / self.shares
|
|
418
|
+
#
|
|
419
|
+
# def _compute_eps_growth_y1(self) -> float:
|
|
420
|
+
# if (fundamental := self.yearly_fundamental) and self.eps_y1 is not None and fundamental.eps:
|
|
421
|
+
# return self.eps_y1 / fundamental.eps - 1
|
|
422
|
+
#
|
|
423
|
+
# def _compute_eps_growth_y2(self) -> float:
|
|
424
|
+
# if self.eps_y2 is not None and self.eps_y1:
|
|
425
|
+
# return self.eps_y2 / self.eps_y1 - 1
|
|
426
|
+
#
|
|
427
|
+
# def _compute_eps_growth_y3(self) -> float:
|
|
428
|
+
# if self.eps_y3 is not None and self.eps_y2:
|
|
429
|
+
# return self.eps_y3 / self.eps_y2 - 1
|
|
430
|
+
#
|
|
431
|
+
# def _compute_eps_growth_y4(self) -> float:
|
|
432
|
+
# if self.eps_y4 is not None and self.eps_y3:
|
|
433
|
+
# return self.eps_y4 / self.eps_y3 - 1
|
|
434
|
+
#
|
|
435
|
+
# def _compute_eps_growth_y5(self) -> float:
|
|
436
|
+
# if self.eps_y5 is not None and self.eps_y4:
|
|
437
|
+
# return self.eps_y5 / self.eps_y4 - 1
|
|
438
|
+
#
|
|
439
|
+
# def _compute_net_debt_to_ebitda_ratio_y1(self) -> float:
|
|
440
|
+
# if self.net_debt_y1 is not None and self.ebitda_y1:
|
|
441
|
+
# return self.net_debt_y1 / self.ebitda_y1
|
|
442
|
+
#
|
|
443
|
+
# def _compute_net_debt_to_ebitda_ratio_y2(self) -> float:
|
|
444
|
+
# if self.net_debt_y2 is not None and self.ebitda_y2:
|
|
445
|
+
# return self.net_debt_y2 / self.ebitda_y2
|
|
446
|
+
#
|
|
447
|
+
# def _compute_net_debt_to_ebitda_ratio_y3(self) -> float:
|
|
448
|
+
# if self.net_debt_y3 is not None and self.ebitda_y3:
|
|
449
|
+
# return self.net_debt_y3 / self.ebitda_y3
|
|
450
|
+
#
|
|
451
|
+
# def _compute_net_debt_to_ebitda_ratio_y4(self) -> float:
|
|
452
|
+
# if self.net_debt_y4 is not None and self.ebitda_y4:
|
|
453
|
+
# return self.net_debt_y4 / self.ebitda_y4
|
|
454
|
+
#
|
|
455
|
+
# def _compute_net_debt_to_ebitda_ratio_y5(self) -> float:
|
|
456
|
+
# if self.net_debt_y5 is not None and self.ebitda_y5:
|
|
457
|
+
# return self.net_debt_y5 / self.ebitda_y5
|
|
458
|
+
#
|
|
459
|
+
# def _compute_free_cash_flow_growth_y1(self) -> float:
|
|
460
|
+
# if (
|
|
461
|
+
# (daily_fundamental := self.daily_fundamental)
|
|
462
|
+
# and self.free_cash_flow_y1 is not None
|
|
463
|
+
# and daily_fundamental.free_cash_flow
|
|
464
|
+
# ):
|
|
465
|
+
# return self.free_cash_flow_y1 / daily_fundamental.free_cash_flow - 1
|
|
466
|
+
#
|
|
467
|
+
# def _compute_free_cash_flow_growth_y2(self) -> float:
|
|
468
|
+
# if self.free_cash_flow_y2 is not None and self.free_cash_flow_y1:
|
|
469
|
+
# return self.free_cash_flow_y2 / self.free_cash_flow_y1 - 1
|
|
470
|
+
#
|
|
471
|
+
# def _compute_free_cash_flow_growth_y3(self) -> float:
|
|
472
|
+
# if self.free_cash_flow_y3 is not None and self.free_cash_flow_y2:
|
|
473
|
+
# return self.free_cash_flow_y3 / self.free_cash_flow_y2 - 1
|
|
474
|
+
#
|
|
475
|
+
# def _compute_free_cash_flow_growth_y4(self) -> float:
|
|
476
|
+
# if self.free_cash_flow_y4 is not None and self.free_cash_flow_y3:
|
|
477
|
+
# return self.free_cash_flow_y4 / self.free_cash_flow_y3 - 1
|
|
478
|
+
#
|
|
479
|
+
# def _compute_free_cash_flow_growth_y5(self) -> float:
|
|
480
|
+
# if self.free_cash_flow_y5 is not None and self.free_cash_flow_y4:
|
|
481
|
+
# return self.free_cash_flow_y5 / self.free_cash_flow_y4 - 1
|
|
482
|
+
#
|
|
483
|
+
# def _compute_free_cash_flow_to_sales_ratio_y1(self) -> float:
|
|
484
|
+
# if self.free_cash_flow_y1 is not None and self.revenue_y1:
|
|
485
|
+
# return self.free_cash_flow_y1 / self.revenue_y1
|
|
486
|
+
#
|
|
487
|
+
# def _compute_free_cash_flow_to_sales_ratio_y2(self) -> float:
|
|
488
|
+
# if self.free_cash_flow_y2 is not None and self.revenue_y2:
|
|
489
|
+
# return self.free_cash_flow_y2 / self.revenue_y2
|
|
490
|
+
#
|
|
491
|
+
# def _compute_free_cash_flow_to_sales_ratio_y3(self) -> float:
|
|
492
|
+
# if self.free_cash_flow_y3 is not None and self.revenue_y3:
|
|
493
|
+
# return self.free_cash_flow_y3 / self.revenue_y3
|
|
494
|
+
#
|
|
495
|
+
# def _compute_free_cash_flow_to_sales_ratio_y4(self) -> float:
|
|
496
|
+
# if self.free_cash_flow_y4 is not None and self.revenue_y4:
|
|
497
|
+
# return self.free_cash_flow_y4 / self.revenue_y4
|
|
498
|
+
#
|
|
499
|
+
# def _compute_free_cash_flow_to_sales_ratio_y5(self) -> float:
|
|
500
|
+
# if self.free_cash_flow_y5 is not None and self.revenue_y5:
|
|
501
|
+
# return self.free_cash_flow_y5 / self.revenue_y5
|
|
502
|
+
#
|
|
503
|
+
# def _compute_capex_to_sales_y1(self) -> float:
|
|
504
|
+
# if self.capital_expenditures_y1 is not None and self.revenue_y1:
|
|
505
|
+
# return self.capital_expenditures_y1 / self.revenue_y1
|
|
506
|
+
#
|
|
507
|
+
# def _compute_capex_to_sales_y2(self) -> float:
|
|
508
|
+
# if self.capital_expenditures_y2 is not None and self.revenue_y2:
|
|
509
|
+
# return self.capital_expenditures_y2 / self.revenue_y2
|
|
510
|
+
#
|
|
511
|
+
# def _compute_capex_to_sales_y3(self) -> float:
|
|
512
|
+
# if self.capital_expenditures_y3 is not None and self.revenue_y3:
|
|
513
|
+
# return self.capital_expenditures_y3 / self.revenue_y3
|
|
514
|
+
#
|
|
515
|
+
# def _compute_capex_to_sales_y4(self) -> float:
|
|
516
|
+
# if self.capital_expenditures_y4 is not None and self.revenue_y4:
|
|
517
|
+
# return self.capital_expenditures_y4 / self.revenue_y4
|
|
518
|
+
#
|
|
519
|
+
# def _compute_capex_to_sales_y5(self) -> float:
|
|
520
|
+
# if self.capital_expenditures_y5 is not None and self.revenue_y5:
|
|
521
|
+
# return self.capital_expenditures_y5 / self.revenue_y5
|
|
522
|
+
#
|
|
523
|
+
# def __compute_return_on_equity(self, year: int) -> float:
|
|
524
|
+
# if (
|
|
525
|
+
# (net_profit := getattr(self, f"reported_net_profit_y{year}")) is not None
|
|
526
|
+
# and (previous := self.yearly_fundamental)
|
|
527
|
+
# and previous.net_profit is not None
|
|
528
|
+
# and previous.shareholder_equity
|
|
529
|
+
# ):
|
|
530
|
+
# return net_profit / previous.shareholder_equity
|
|
531
|
+
#
|
|
532
|
+
# def _compute_return_on_equity_y1(self):
|
|
533
|
+
# return self.__compute_return_on_equity(1)
|
|
534
|
+
#
|
|
535
|
+
# def _compute_return_on_equity_y2(self):
|
|
536
|
+
# return self.__compute_return_on_equity(2)
|
|
537
|
+
#
|
|
538
|
+
# def _compute_return_on_equity_y3(self):
|
|
539
|
+
# return self.__compute_return_on_equity(3)
|
|
540
|
+
#
|
|
541
|
+
# def _compute_return_on_equity_y4(self):
|
|
542
|
+
# return self.__compute_return_on_equity(4)
|
|
543
|
+
#
|
|
544
|
+
# def _compute_return_on_equity_y5(self):
|
|
545
|
+
# return self.__compute_return_on_equity(5)
|
|
546
|
+
#
|
|
547
|
+
# def __compute_return_on_assets(self, year: int) -> float:
|
|
548
|
+
# if (
|
|
549
|
+
# (net_profit := getattr(self, f"reported_net_profit_y{year}")) is not None
|
|
550
|
+
# and (previous := self.yearly_fundamental)
|
|
551
|
+
# and previous.net_profit is not None
|
|
552
|
+
# and previous.total_assets
|
|
553
|
+
# ):
|
|
554
|
+
# return net_profit / previous.total_assets
|
|
555
|
+
#
|
|
556
|
+
# def _compute_return_on_assets_y1(self):
|
|
557
|
+
# return self.__compute_return_on_assets(1)
|
|
558
|
+
#
|
|
559
|
+
# def _compute_return_on_assets_y2(self):
|
|
560
|
+
# return self.__compute_return_on_assets(2)
|
|
561
|
+
#
|
|
562
|
+
# def _compute_return_on_assets_y3(self):
|
|
563
|
+
# return self.__compute_return_on_assets(3)
|
|
564
|
+
#
|
|
565
|
+
# def _compute_return_on_assets_y4(self):
|
|
566
|
+
# return self.__compute_return_on_assets(4)
|
|
567
|
+
#
|
|
568
|
+
# def _compute_return_on_assets_y5(self):
|
|
569
|
+
# return self.__compute_return_on_assets(5)
|
|
570
|
+
#
|
|
571
|
+
# def __compute_return_on_capital_employed(self, year: int) -> float:
|
|
572
|
+
# if (
|
|
573
|
+
# (previous := self.yearly_fundamental)
|
|
574
|
+
# and (ebit := getattr(self, f"ebit_y{year}")) is not None
|
|
575
|
+
# and previous.total_assets
|
|
576
|
+
# and previous.current_liabilities
|
|
577
|
+
# and (diff := previous.total_assets - previous.current_liabilities)
|
|
578
|
+
# ):
|
|
579
|
+
# return ebit / diff
|
|
580
|
+
#
|
|
581
|
+
# def _compute_return_on_capital_employed_y1(self):
|
|
582
|
+
# return self.__compute_return_on_capital_employed(1)
|
|
583
|
+
#
|
|
584
|
+
# def _compute_return_on_capital_employed_y2(self):
|
|
585
|
+
# return self.__compute_return_on_capital_employed(2)
|
|
586
|
+
#
|
|
587
|
+
# def _compute_return_on_capital_employed_y3(self):
|
|
588
|
+
# return self.__compute_return_on_capital_employed(3)
|
|
589
|
+
#
|
|
590
|
+
# def _compute_return_on_capital_employed_y4(self):
|
|
591
|
+
# return self.__compute_return_on_capital_employed(4)
|
|
592
|
+
#
|
|
593
|
+
# def _compute_return_on_capital_employed_y5(self):
|
|
594
|
+
# return self.__compute_return_on_capital_employed(5)
|
|
595
|
+
#
|
|
596
|
+
# def __compute_return_on_invested_capital(self, year: int) -> float:
|
|
597
|
+
# if (
|
|
598
|
+
# (previous := self.yearly_fundamental)
|
|
599
|
+
# and (ebit := getattr(self, f"ebit_y{year}")) is not None
|
|
600
|
+
# and previous.company_tax_rate is not None
|
|
601
|
+
# and previous.shareholder_equity is not None
|
|
602
|
+
# and previous.total_liabilities is not None
|
|
603
|
+
# and previous.cash_and_cash_equivalents is not None
|
|
604
|
+
# ):
|
|
605
|
+
# divisor = previous.shareholder_equity + previous.total_liabilities - previous.cash_and_cash_equivalents
|
|
606
|
+
# if divisor:
|
|
607
|
+
# return ebit * (1 - previous.company_tax_rate) / divisor
|
|
608
|
+
#
|
|
609
|
+
# def _compute_return_on_invested_capital_y1(self):
|
|
610
|
+
# return self.__compute_return_on_invested_capital(1)
|
|
611
|
+
#
|
|
612
|
+
# def _compute_return_on_invested_capital_y2(self):
|
|
613
|
+
# return self.__compute_return_on_invested_capital(2)
|
|
614
|
+
#
|
|
615
|
+
# def _compute_return_on_invested_capital_y3(self):
|
|
616
|
+
# return self.__compute_return_on_invested_capital(3)
|
|
617
|
+
#
|
|
618
|
+
# def _compute_return_on_invested_capital_y4(self):
|
|
619
|
+
# return self.__compute_return_on_invested_capital(4)
|
|
620
|
+
#
|
|
621
|
+
# def _compute_return_on_invested_capital_y5(self):
|
|
622
|
+
# return self.__compute_return_on_invested_capital(5)
|
|
623
|
+
#
|
|
624
|
+
# def __compute_interest_coverage_ratio(self, year: int) -> float:
|
|
625
|
+
# if (
|
|
626
|
+
# (previous := self.yearly_fundamental)
|
|
627
|
+
# and (ebit := getattr(self, f"ebit_y{year}")) is not None
|
|
628
|
+
# and previous.interest_expense
|
|
629
|
+
# ):
|
|
630
|
+
# return ebit / previous.interest_expense
|
|
631
|
+
#
|
|
632
|
+
# def _compute_interest_coverage_ratio_y1(self):
|
|
633
|
+
# return self.__compute_interest_coverage_ratio(1)
|
|
634
|
+
#
|
|
635
|
+
# def _compute_interest_coverage_ratio_y2(self):
|
|
636
|
+
# return self.__compute_interest_coverage_ratio(2)
|
|
637
|
+
#
|
|
638
|
+
# def _compute_interest_coverage_ratio_y3(self):
|
|
639
|
+
# return self.__compute_interest_coverage_ratio(3)
|
|
640
|
+
#
|
|
641
|
+
# def _compute_interest_coverage_ratio_y4(self):
|
|
642
|
+
# return self.__compute_interest_coverage_ratio(4)
|
|
643
|
+
#
|
|
644
|
+
# def _compute_interest_coverage_ratio_y5(self):
|
|
645
|
+
# return self.__compute_interest_coverage_ratio(5)
|
|
646
|
+
#
|
|
647
|
+
#
|
|
648
|
+
# class DailyFundamentalComputedMixin:
|
|
649
|
+
# def _compute_free_cash_flow_ttm_growth(self) -> float:
|
|
650
|
+
# if (
|
|
651
|
+
# self.free_cash_flow is not None
|
|
652
|
+
# and (last_ttl_daily_fundamental := self.last_ttl_daily_fundamental)
|
|
653
|
+
# and (last_free_cash_flow := last_ttl_daily_fundamental.free_cash_flow)
|
|
654
|
+
# ):
|
|
655
|
+
# return self.free_cash_flow / last_free_cash_flow - 1
|
|
656
|
+
#
|
|
657
|
+
# def _compute_free_cash_flow(self) -> float:
|
|
658
|
+
# if (
|
|
659
|
+
# self.free_cash is not None
|
|
660
|
+
# and (price := self.price)
|
|
661
|
+
# and (outstanding_shares_consolidated := price.outstanding_shares_consolidated)
|
|
662
|
+
# ):
|
|
663
|
+
# return self.free_cash / float(outstanding_shares_consolidated)
|
|
664
|
+
#
|
|
665
|
+
# def _compute_revenue_growth_3y_cagr(self) -> float:
|
|
666
|
+
# if (
|
|
667
|
+
# (forecast := self.forecast)
|
|
668
|
+
# and (fundamental := self.yearly_fundamental)
|
|
669
|
+
# and (revenue_y3 := forecast.revenue_y3) is not None
|
|
670
|
+
# and fundamental.revenue
|
|
671
|
+
# ):
|
|
672
|
+
# if (den := revenue_y3 / fundamental.revenue) >= 0:
|
|
673
|
+
# return math.pow(den, 1 / 3) - 1
|
|
674
|
+
#
|
|
675
|
+
# def _compute_eps_3y_cagr(self) -> float:
|
|
676
|
+
# if (
|
|
677
|
+
# (forecast := self.forecast)
|
|
678
|
+
# and (fundamental := self.yearly_fundamental)
|
|
679
|
+
# and (eps_3y := forecast.eps_y3) is not None
|
|
680
|
+
# and fundamental.eps
|
|
681
|
+
# ):
|
|
682
|
+
# if (den := eps_3y / fundamental.eps) >= 0:
|
|
683
|
+
# return math.pow(den, 1 / 3) - 1
|
|
684
|
+
#
|
|
685
|
+
# def _compute_free_cash_flow_3y_cagr(self) -> float:
|
|
686
|
+
# if (
|
|
687
|
+
# (forecast := self.forecast)
|
|
688
|
+
# and (fundamental := self.yearly_fundamental)
|
|
689
|
+
# and (fcf_3y := forecast.free_cash_flow_y3) is not None
|
|
690
|
+
# and fundamental.free_cash_flow
|
|
691
|
+
# ):
|
|
692
|
+
# if (den := fcf_3y / fundamental.free_cash_flow) >= 0:
|
|
693
|
+
# return math.pow(den, 1 / 3) - 1
|
|
694
|
+
#
|
|
695
|
+
# def _compute_eps_ttm(self):
|
|
696
|
+
# pass
|
|
697
|
+
#
|
|
698
|
+
#
|
|
699
|
+
class InstrumentPriceComputedMixin:
|
|
700
|
+
def _compute_outstanding_shares(self):
|
|
701
|
+
if self.outstanding_shares is None and (previous_price := self.previous_price):
|
|
702
|
+
return previous_price.outstanding_shares
|
|
703
|
+
|
|
704
|
+
def _compute_outstanding_shares_consolidated(self):
|
|
705
|
+
if self.outstanding_shares_consolidated is None and self.outstanding_shares is not None:
|
|
706
|
+
return self.outstanding_shares
|
|
707
|
+
|
|
708
|
+
def _compute_gross_value(self):
|
|
709
|
+
if self.net_value is not None and self.gross_value is None:
|
|
710
|
+
return self.net_value
|
|
711
|
+
|
|
712
|
+
def _compute_volume_50d(self):
|
|
713
|
+
volumes = list(
|
|
714
|
+
filter(
|
|
715
|
+
None,
|
|
716
|
+
self.instrument.valuations.filter(date__lte=self.date)
|
|
717
|
+
.order_by("-date")
|
|
718
|
+
.values_list("volume", flat=True)[:50],
|
|
719
|
+
)
|
|
720
|
+
)
|
|
721
|
+
if len(volumes) > 0:
|
|
722
|
+
return sum(volumes) / len(volumes)
|
|
723
|
+
return 0.0
|
|
724
|
+
|
|
725
|
+
def _compute_volume_200d(self):
|
|
726
|
+
volumes = list(
|
|
727
|
+
filter(
|
|
728
|
+
None,
|
|
729
|
+
self.instrument.valuations.filter(date__lte=self.date)
|
|
730
|
+
.order_by("-date")
|
|
731
|
+
.values_list("volume", flat=True)[:200],
|
|
732
|
+
)
|
|
733
|
+
)
|
|
734
|
+
if len(volumes) > 0:
|
|
735
|
+
return sum(volumes) / len(volumes)
|
|
736
|
+
return 0.0
|
|
737
|
+
|
|
738
|
+
def _compute_performance_1d(self):
|
|
739
|
+
try:
|
|
740
|
+
last_price = self.instrument.prices.get(
|
|
741
|
+
calculated=self.calculated, date=(self.date - pd.tseries.offsets.BDay(1)).date()
|
|
742
|
+
)
|
|
743
|
+
if self.net_value is not None and last_price.net_value:
|
|
744
|
+
return self.net_value / last_price.net_value - 1
|
|
745
|
+
except self.DoesNotExist:
|
|
746
|
+
pass
|
|
747
|
+
|
|
748
|
+
def _compute_performance_7d(self):
|
|
749
|
+
try:
|
|
750
|
+
last_price = self.instrument.prices.get(
|
|
751
|
+
calculated=self.calculated, date=(self.date - pd.tseries.offsets.BDay(7)).date()
|
|
752
|
+
)
|
|
753
|
+
if self.net_value is not None and last_price.net_value:
|
|
754
|
+
return self.net_value / last_price.net_value - 1
|
|
755
|
+
except self.DoesNotExist:
|
|
756
|
+
pass
|
|
757
|
+
|
|
758
|
+
def _compute_performance_30d(self):
|
|
759
|
+
try:
|
|
760
|
+
last_price = self.instrument.prices.get(
|
|
761
|
+
calculated=self.calculated, date=(self.date - pd.tseries.offsets.BDay(30)).date()
|
|
762
|
+
)
|
|
763
|
+
if self.net_value is not None and last_price.net_value:
|
|
764
|
+
return self.net_value / last_price.net_value - 1
|
|
765
|
+
except self.DoesNotExist:
|
|
766
|
+
pass
|
|
767
|
+
|
|
768
|
+
def _compute_performance_90d(self):
|
|
769
|
+
try:
|
|
770
|
+
last_price = self.instrument.prices.get(
|
|
771
|
+
calculated=self.calculated, date=(self.date - pd.tseries.offsets.BDay(90)).date()
|
|
772
|
+
)
|
|
773
|
+
if self.net_value is not None and last_price.net_value:
|
|
774
|
+
return self.net_value / last_price.net_value - 1
|
|
775
|
+
except self.DoesNotExist:
|
|
776
|
+
pass
|
|
777
|
+
|
|
778
|
+
def _compute_performance_365d(self):
|
|
779
|
+
try:
|
|
780
|
+
last_price = self.instrument.prices.get(
|
|
781
|
+
calculated=self.calculated, date=(self.date - pd.tseries.offsets.BDay(365)).date()
|
|
782
|
+
)
|
|
783
|
+
if self.net_value is not None and last_price.net_value:
|
|
784
|
+
return self.net_value / last_price.net_value - 1
|
|
785
|
+
except self.DoesNotExist:
|
|
786
|
+
pass
|
|
787
|
+
|
|
788
|
+
def _compute_performance_ytd(self):
|
|
789
|
+
try:
|
|
790
|
+
last_price = self.instrument.prices.get(
|
|
791
|
+
calculated=self.calculated, date=(date(self.date.year, 1, 1) - pd.tseries.offsets.BDay(0)).date()
|
|
792
|
+
)
|
|
793
|
+
if self.net_value is not None and last_price.net_value:
|
|
794
|
+
return self.net_value / last_price.net_value - 1
|
|
795
|
+
except self.DoesNotExist:
|
|
796
|
+
pass
|
|
797
|
+
|
|
798
|
+
def _compute_performance_inception(self):
|
|
799
|
+
try:
|
|
800
|
+
last_price = self.instrument.prices.get(calculated=self.calculated, date=self.instrument.inception_date)
|
|
801
|
+
if self.net_value is not None and last_price.net_value:
|
|
802
|
+
return self.net_value / last_price.net_value - 1
|
|
803
|
+
except self.DoesNotExist:
|
|
804
|
+
pass
|