wbfdm 1.49.5__py2.py3-none-any.whl → 1.59.4__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. wbfdm/admin/exchanges.py +1 -1
  2. wbfdm/admin/instruments.py +3 -2
  3. wbfdm/analysis/financial_analysis/change_point_detection.py +88 -0
  4. wbfdm/analysis/financial_analysis/statement_with_estimates.py +5 -6
  5. wbfdm/analysis/financial_analysis/utils.py +6 -0
  6. wbfdm/contrib/dsws/client.py +3 -3
  7. wbfdm/contrib/dsws/dataloaders/market_data.py +31 -3
  8. wbfdm/contrib/internal/dataloaders/market_data.py +43 -9
  9. wbfdm/contrib/metric/backends/base.py +2 -2
  10. wbfdm/contrib/metric/backends/statistics.py +47 -13
  11. wbfdm/contrib/metric/dispatch.py +3 -0
  12. wbfdm/contrib/metric/exceptions.py +1 -1
  13. wbfdm/contrib/metric/filters.py +19 -0
  14. wbfdm/contrib/metric/models.py +6 -0
  15. wbfdm/contrib/metric/orchestrators.py +4 -4
  16. wbfdm/contrib/metric/signals.py +7 -0
  17. wbfdm/contrib/metric/tasks.py +2 -3
  18. wbfdm/contrib/metric/viewsets/configs/display.py +2 -2
  19. wbfdm/contrib/metric/viewsets/mixins.py +6 -6
  20. wbfdm/contrib/msci/client.py +6 -2
  21. wbfdm/contrib/qa/database_routers.py +1 -1
  22. wbfdm/contrib/qa/dataloaders/adjustments.py +2 -1
  23. wbfdm/contrib/qa/dataloaders/corporate_actions.py +2 -1
  24. wbfdm/contrib/qa/dataloaders/financials.py +19 -1
  25. wbfdm/contrib/qa/dataloaders/fx_rates.py +86 -0
  26. wbfdm/contrib/qa/dataloaders/market_data.py +29 -40
  27. wbfdm/contrib/qa/dataloaders/officers.py +1 -1
  28. wbfdm/contrib/qa/dataloaders/statements.py +18 -3
  29. wbfdm/contrib/qa/jinja2/qa/sql/ibes/financials.sql +1 -1
  30. wbfdm/contrib/qa/sync/exchanges.py +2 -1
  31. wbfdm/contrib/qa/sync/utils.py +76 -17
  32. wbfdm/dataloaders/protocols.py +12 -1
  33. wbfdm/dataloaders/proxies.py +15 -1
  34. wbfdm/dataloaders/types.py +7 -1
  35. wbfdm/enums.py +2 -0
  36. wbfdm/factories/instruments.py +4 -2
  37. wbfdm/figures/financials/financial_analysis_charts.py +2 -8
  38. wbfdm/filters/classifications.py +2 -2
  39. wbfdm/filters/financials.py +9 -18
  40. wbfdm/filters/financials_analysis.py +36 -16
  41. wbfdm/filters/instrument_prices.py +8 -5
  42. wbfdm/filters/instruments.py +21 -7
  43. wbfdm/import_export/backends/cbinsights/utils/client.py +8 -8
  44. wbfdm/import_export/backends/refinitiv/utils/controller.py +1 -1
  45. wbfdm/import_export/handlers/instrument.py +160 -104
  46. wbfdm/import_export/handlers/option.py +2 -2
  47. wbfdm/import_export/parsers/cbinsights/equities.py +2 -3
  48. wbfdm/jinja2.py +2 -1
  49. wbfdm/locale/de/LC_MESSAGES/django.mo +0 -0
  50. wbfdm/locale/de/LC_MESSAGES/django.po +257 -0
  51. wbfdm/locale/en/LC_MESSAGES/django.mo +0 -0
  52. wbfdm/locale/en/LC_MESSAGES/django.po +255 -0
  53. wbfdm/locale/fr/LC_MESSAGES/django.mo +0 -0
  54. wbfdm/locale/fr/LC_MESSAGES/django.po +257 -0
  55. wbfdm/migrations/0031_exchange_apply_round_lot_size_and_more.py +23 -0
  56. wbfdm/migrations/0032_alter_instrumentprice_outstanding_shares.py +18 -0
  57. wbfdm/migrations/0033_alter_controversy_review.py +18 -0
  58. wbfdm/migrations/0034_alter_instrumentlist_instrument_list_type.py +18 -0
  59. wbfdm/models/esg/controversies.py +19 -23
  60. wbfdm/models/exchanges/exchanges.py +8 -4
  61. wbfdm/models/fields.py +2 -2
  62. wbfdm/models/fk_fields.py +3 -3
  63. wbfdm/models/instruments/instrument_lists.py +1 -0
  64. wbfdm/models/instruments/instrument_prices.py +8 -1
  65. wbfdm/models/instruments/instrument_relationships.py +3 -0
  66. wbfdm/models/instruments/instruments.py +139 -26
  67. wbfdm/models/instruments/llm/create_instrument_news_relationships.py +29 -22
  68. wbfdm/models/instruments/mixin/financials_computed.py +0 -4
  69. wbfdm/models/instruments/mixin/financials_serializer_fields.py +118 -118
  70. wbfdm/models/instruments/mixin/instruments.py +7 -4
  71. wbfdm/models/instruments/options.py +6 -0
  72. wbfdm/models/instruments/private_equities.py +3 -0
  73. wbfdm/models/instruments/querysets.py +138 -37
  74. wbfdm/models/instruments/utils.py +5 -0
  75. wbfdm/serializers/exchanges.py +1 -0
  76. wbfdm/serializers/instruments/__init__.py +1 -0
  77. wbfdm/serializers/instruments/instruments.py +9 -2
  78. wbfdm/serializers/instruments/mixins.py +3 -3
  79. wbfdm/tasks.py +13 -2
  80. wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +0 -1
  81. wbfdm/tests/models/test_instrument_prices.py +0 -14
  82. wbfdm/tests/models/test_instruments.py +21 -9
  83. wbfdm/tests/models/test_queryset.py +89 -0
  84. wbfdm/viewsets/configs/display/exchanges.py +1 -1
  85. wbfdm/viewsets/configs/display/financial_summary.py +2 -2
  86. wbfdm/viewsets/configs/display/instrument_prices.py +2 -70
  87. wbfdm/viewsets/configs/display/instruments.py +3 -4
  88. wbfdm/viewsets/configs/display/instruments_relationships.py +3 -1
  89. wbfdm/viewsets/configs/display/prices.py +1 -0
  90. wbfdm/viewsets/configs/display/statement_with_estimates.py +1 -2
  91. wbfdm/viewsets/configs/endpoints/classifications.py +0 -12
  92. wbfdm/viewsets/configs/endpoints/instrument_prices.py +4 -23
  93. wbfdm/viewsets/configs/titles/instrument_prices.py +2 -1
  94. wbfdm/viewsets/esg.py +2 -2
  95. wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +2 -2
  96. wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +1 -1
  97. wbfdm/viewsets/financial_analysis/financial_summary.py +6 -6
  98. wbfdm/viewsets/financial_analysis/statement_with_estimates.py +7 -3
  99. wbfdm/viewsets/instruments/financials_analysis.py +9 -12
  100. wbfdm/viewsets/instruments/instrument_prices.py +9 -9
  101. wbfdm/viewsets/instruments/instruments.py +9 -7
  102. wbfdm/viewsets/instruments/utils.py +3 -3
  103. wbfdm/viewsets/market_data.py +1 -1
  104. wbfdm/viewsets/prices.py +5 -0
  105. wbfdm/viewsets/statements/statements.py +7 -3
  106. {wbfdm-1.49.5.dist-info → wbfdm-1.59.4.dist-info}/METADATA +2 -1
  107. {wbfdm-1.49.5.dist-info → wbfdm-1.59.4.dist-info}/RECORD +108 -95
  108. {wbfdm-1.49.5.dist-info → wbfdm-1.59.4.dist-info}/WHEEL +1 -1
  109. wbfdm/menu.py +0 -11
@@ -7,7 +7,8 @@ from typing import Any, Dict, Optional
7
7
  from django.contrib.postgres.search import TrigramSimilarity
8
8
  from django.core.exceptions import MultipleObjectsReturned
9
9
  from django.db import IntegrityError, models
10
- from django.db.models import Q
10
+ from django.db.models import Exists, OuterRef, Q
11
+ from slugify import slugify
11
12
  from wbcore.contrib.currency.import_export.handlers import CurrencyImportHandler
12
13
  from wbcore.contrib.geography.models import Geography
13
14
  from wbcore.contrib.io.exceptions import DeserializationError
@@ -24,6 +25,7 @@ class InstrumentLookup:
24
25
  "refinitiv_mnemonic_code",
25
26
  "identifier",
26
27
  "ticker",
28
+ "currency",
27
29
  ]
28
30
 
29
31
  def __init__(self, model, trigram_similarity_min_score: float = 0.8):
@@ -33,34 +35,20 @@ class InstrumentLookup:
33
35
 
34
36
  @classmethod
35
37
  def _get_cache_key(cls, **data):
36
- if data:
37
- return "-".join([f"{k}:{data.get(k, None)}" for k in cls.ORDERED_KEYS if data.get(k, None) is not None])
38
+ return "-".join([f"{k}:{slugify(str(data[k]))}" for k in cls.ORDERED_KEYS if data.get(k, None) is not None])
38
39
 
39
- def _lookup_instrument(
40
- self,
41
- instrument_type=None,
42
- currency=None,
43
- exchange=None,
44
- name=None,
45
- only_investable_universe: bool = False,
46
- exact_lookup: bool = False,
47
- **identifiers,
48
- ):
49
- identifiers = {k: v for k, v in identifiers.items() if v is not None}
50
- # General lookup, we try to gracefully find the instrument based on all available identifier fields
51
- cache_key = self._get_cache_key(**identifiers)
40
+ def _get_cache(self, **kwargs):
41
+ cache_key = self._get_cache_key(**kwargs)
52
42
  if cache_key and cache_key in self.cache:
53
43
  return self.cache[cache_key]
54
44
 
55
- instrument = None
56
-
57
- # We need to lookup ticker because some provider gives us ticker with or without space in it
58
- if only_investable_universe:
59
- instruments = self.model.objects.filter(is_investable_universe=True)
60
- else:
61
- instruments = self.model.objects.filter(is_security=True)
45
+ def _set_cache(self, instrument, **kwargs):
46
+ cache_key = self._get_cache_key(**kwargs)
47
+ self.cache[cache_key] = instrument
62
48
 
49
+ def _get_instruments_from_identifiers(self, queryset, **identifiers):
63
50
  # Try exact lookup on the filtered out universe
51
+ identifiers = {k: v for k, v in identifiers.items() if v is not None}
64
52
  for identifier_key in [
65
53
  "isin",
66
54
  "refinitiv_identifier_code",
@@ -76,87 +64,153 @@ class InstrumentLookup:
76
64
  identifier_key != "refinitiv_identifier_code"
77
65
  ): # RIC cannot be uppercased because its symbology implies meaning for lowercase characters
78
66
  identifier = identifier.upper()
79
- instrument = instruments.get(**{identifier_key: identifier})
80
- break
81
- if not instrument and not exact_lookup:
82
- if instrument_type:
83
- if isinstance(instrument_type, str): # in case we receive a key as instrument type
84
- instruments = instruments.filter(instrument_type__key=instrument_type)
85
- else: # in case we receive a primary key as instrument type
86
- instruments = instruments.filter(instrument_type=instrument_type)
87
- lookup_fields = [
88
- "isin",
89
- "refinitiv_identifier_code",
90
- "refinitiv_mnemonic_code",
91
- "refinitiv_ticker",
92
- "identifier",
93
- "ticker",
94
- ]
95
-
96
- conditions = []
97
- for field in lookup_fields:
98
- if field_value := identifiers.get(field, None):
99
- conditions.append(Q(**{f"{field}": field_value}))
100
- if field == "isin":
101
- conditions.append(Q(old_isins__contains=[field_value]))
102
- if conditions:
103
- instruments = instruments.filter(reduce(operator.or_, conditions))
104
-
105
- if currency: # if currency is provides, we use it as validator
106
- instruments = instruments.filter(currency=currency)
107
-
108
- if exchange:
109
- instruments_tmp = instruments.filter(exchange=exchange)
110
- if instruments_tmp.exists():
111
- instruments = instruments_tmp
112
-
113
- # last chance
114
- if name and instruments.count() > 1:
115
- instruments = instruments.annotate(similarity_score=TrigramSimilarity("name", name))
116
- if instruments.filter(similarity_score__gt=self.trigram_similarity_min_score).count() == 1:
117
- instruments = instruments.filter(similarity_score__gt=self.trigram_similarity_min_score)
118
- if instruments.count() == 1:
119
- instrument = instruments.first()
120
- elif instrument_type and identifiers:
121
- # if instrument type was provided but we still didn't find the security, we try without the instrument type in case it was mislabeled
122
- instrument = self._lookup_instrument(
123
- only_investable_universe=only_investable_universe,
124
- exact_lookup=exact_lookup,
125
- currency=currency,
126
- exchange=exchange,
127
- **identifiers,
128
- )
129
- if not instrument and name and identifiers:
130
- # Sometime, identifier provided emptied the queryset of possible instruments. In a last chance approach, we try to only look for security with the given name
131
- instrument = self._lookup_instrument(
132
- only_investable_universe=only_investable_universe,
133
- exact_lookup=exact_lookup,
134
- instrument_type=instrument_type,
135
- currency=currency,
136
- exchange=exchange,
137
- name=name,
67
+ # we try to lookup the instrument with the unique identifier
68
+ instrument = queryset.get(**{identifier_key: identifier})
69
+ return self.model.objects.filter(id=instrument.id)
70
+ return queryset
71
+
72
+ def _filter_identifiers(self, queryset, **identifiers):
73
+ lookup_fields = [
74
+ "isin",
75
+ "refinitiv_identifier_code",
76
+ "refinitiv_mnemonic_code",
77
+ "refinitiv_ticker",
78
+ "identifier",
79
+ "ticker",
80
+ ]
81
+ conditions = []
82
+ for field in lookup_fields:
83
+ if field_value := identifiers.get(field, None):
84
+ conditions.append(Q(**{f"{field}": field_value}))
85
+ if field == "isin":
86
+ conditions.append(Q(old_isins__contains=[field_value]))
87
+ if field == "ticker":
88
+ conditions.append(
89
+ Q(ticker__regex=rf"^{field_value}(\.[A-Za-z])$")
90
+ ) # initial regex: ([A-Za-z]?|\.?[A-Za-z])$ This led to a false positive, I don't remember why we allow an extra random character to be consider for eval
91
+ conditions.append(Q(refinitiv_mnemonic_code=f"@{field_value}"))
92
+
93
+ if conditions:
94
+ return queryset.filter(reduce(operator.or_, conditions))
95
+ return queryset
96
+
97
+ def _filter_name(self, queryset, name):
98
+ instruments = queryset.annotate(similarity_score=TrigramSimilarity("name", name))
99
+ if (
100
+ queryset.count() > 1
101
+ and instruments.filter(similarity_score__gt=self.trigram_similarity_min_score).count() == 1
102
+ ):
103
+ return instruments.filter(similarity_score__gt=self.trigram_similarity_min_score)
104
+ return queryset
105
+
106
+ def _filter_currency(self, queryset, currency):
107
+ qs = queryset.filter(currency=currency)
108
+ if qs.exists():
109
+ return qs
110
+ return queryset
111
+
112
+ def _filter_exchange(self, queryset, exchange):
113
+ return queryset.filter(exchange=exchange)
114
+
115
+ def _filter_instrument_type(self, queryset, instrument_type):
116
+ if not isinstance(instrument_type, str):
117
+ instrument_type = instrument_type.key
118
+ if instrument_type == "equity":
119
+ qs = queryset.filter(instrument_type__key__in=["equity", "american_depository_receipt"])
120
+ else:
121
+ qs = queryset.filter(instrument_type__key=instrument_type)
122
+ if qs.exists():
123
+ return qs
124
+ return queryset
125
+
126
+ def _get_queryset(self, only_investable_universe: bool = False):
127
+ instruments = self.model.objects.filter(is_security=True)
128
+ if only_investable_universe:
129
+ instruments = instruments.annotate(
130
+ has_children_in_investable_universe=Exists(
131
+ self.model.objects.filter(is_investable_universe=True, parent=OuterRef("id"))
138
132
  )
139
- if instrument:
140
- self.cache[cache_key] = instrument
141
- return instrument
133
+ ).filter(Q(is_investable_universe=True) | Q(has_children_in_investable_universe=True))
134
+ return instruments
142
135
 
143
- def lookup(self, only_security: bool = False, exact_lookup: bool = False, **lookup_kwargs):
144
- # To speed up lookup process, we try to get the quote from the investable universe first
145
- security = self._lookup_instrument(only_investable_universe=True, exact_lookup=exact_lookup, **lookup_kwargs)
146
- if not security:
147
- security = self._lookup_instrument(
148
- only_investable_universe=False, exact_lookup=exact_lookup, **lookup_kwargs
136
+ def _get_security_candidates( # noqa: C901
137
+ self,
138
+ queryset,
139
+ instrument_type=None,
140
+ currency=None,
141
+ name=None,
142
+ **identifiers,
143
+ ):
144
+ # We need to lookup ticker because some provider gives us ticker with or without space in it
145
+ instruments = self._get_instruments_from_identifiers(queryset, **identifiers)
146
+ if instrument_type:
147
+ instruments = self._filter_instrument_type(instruments, instrument_type)
148
+
149
+ if instruments.count() == 1:
150
+ return instruments
151
+
152
+ instruments = self._filter_identifiers(instruments, **identifiers)
153
+
154
+ if currency:
155
+ instruments = self._filter_currency(instruments, currency)
156
+ if name:
157
+ instruments = self._filter_name(instruments, name)
158
+ return instruments
159
+
160
+ def _lookup_quote(
161
+ self,
162
+ security,
163
+ currency=None,
164
+ exchange=None,
165
+ refinitiv_identifier_code=None,
166
+ refinitiv_mnemonic_code=None,
167
+ **kwargs,
168
+ ):
169
+ quotes = security.children.all()
170
+ if not quotes.exists():
171
+ return security
172
+ if quotes.count() == 1:
173
+ return quotes.first()
174
+ if exchange:
175
+ quotes = self._filter_exchange(quotes, exchange)
176
+ if currency:
177
+ quotes = self._filter_currency(quotes, currency)
178
+ if refinitiv_mnemonic_code or refinitiv_identifier_code:
179
+ quotes = self._filter_identifiers(
180
+ quotes,
181
+ refinitiv_identifier_code=refinitiv_identifier_code,
182
+ refinitiv_mnemonic_code=refinitiv_mnemonic_code,
149
183
  )
150
- if not only_security and security:
151
- quotes = security.children.all()
152
- if quotes.exists():
153
- # We try to find the quote for that security based on the given exchange (if provided). Otherwise, we default to the security primary exchange
154
- exchange = lookup_kwargs.get("exchange")
155
- if exchange and quotes.filter(exchange=exchange).exists():
156
- return quotes.filter(exchange=exchange).first()
157
- else:
158
- return quotes.filter(is_primary=True).first()
159
- return security
184
+ if quotes.filter(is_investable_universe=True).count() == 1:
185
+ quotes = quotes.filter(is_investable_universe=True)
186
+ if quotes.count() == 1:
187
+ return quotes.first()
188
+ return security.children.filter(is_primary=True).first()
189
+
190
+ def lookup_security(self, **kwargs):
191
+ queryset = self._get_queryset(only_investable_universe=True)
192
+ security_candidates_in_investable_universe = self._get_security_candidates(queryset, **kwargs)
193
+
194
+ if security_candidates_in_investable_universe.count() == 1:
195
+ return security_candidates_in_investable_universe.first()
196
+ queryset = self._get_queryset(only_investable_universe=False)
197
+ security_candidates = self._get_security_candidates(queryset, **kwargs)
198
+
199
+ if security_candidates.exists() > 1:
200
+ security_candidates = security_candidates.filter(delisted_date__isnull=True)
201
+ if security_candidates.count() == 1:
202
+ return security_candidates.first()
203
+
204
+ def lookup(self, only_security: bool = False, **lookup_kwargs):
205
+ # To speed up lookup process, we try to get the quote from the investable universe first
206
+ if instrument := self._get_cache(**lookup_kwargs):
207
+ return instrument
208
+ instrument = self.lookup_security(**lookup_kwargs)
209
+ if not only_security and instrument:
210
+ instrument = self._lookup_quote(instrument, **lookup_kwargs)
211
+ if instrument:
212
+ self._set_cache(instrument, **lookup_kwargs)
213
+ return instrument
160
214
 
161
215
 
162
216
  class InstrumentImportHandler(ImportExportHandler):
@@ -175,7 +229,9 @@ class InstrumentImportHandler(ImportExportHandler):
175
229
  if isinstance(data, int):
176
230
  data = dict(id=data)
177
231
  if data.get("currency", None):
178
- data["currency"] = self.currency_handler.process_object(data["currency"], read_only=True)[0]
232
+ data["currency"] = self.currency_handler.process_object(
233
+ data["currency"], read_only=True, raise_exception=False
234
+ )[0]
179
235
  if instrument_type := data.get("instrument_type", None):
180
236
  if isinstance(instrument_type, str):
181
237
  data["instrument_type"] = InstrumentType.objects.get_or_create(
@@ -209,8 +265,8 @@ class InstrumentImportHandler(ImportExportHandler):
209
265
  if instrument_id := data.pop("id", None):
210
266
  try:
211
267
  return self.model.objects.get(id=instrument_id)
212
- except self.model.DoesNotExist:
213
- raise DeserializationError("Instrument id does not match an existing instrument")
268
+ except self.model.DoesNotExist as e:
269
+ raise DeserializationError("Instrument id does not match an existing instrument") from e
214
270
  else:
215
271
  return self.instrument_lookup.lookup(only_security=only_security, **data)
216
272
 
@@ -26,7 +26,7 @@ class OptionAggregateImportHandler(ImportExportHandler):
26
26
 
27
27
  def _get_instance(self, data: Dict[str, Any], history: Optional[models.QuerySet] = None, **kwargs) -> models.Model:
28
28
  with suppress(ObjectDoesNotExist):
29
- return self.model.objects.filter(instrument=data["instrument"], date=data["date"], type=data["type"])
29
+ return self.model.objects.get(instrument=data["instrument"], date=data["date"], type=data["type"])
30
30
 
31
31
 
32
32
  class OptionImportHandler(ImportExportHandler):
@@ -46,7 +46,7 @@ class OptionImportHandler(ImportExportHandler):
46
46
 
47
47
  def _get_instance(self, data: Dict[str, Any], history: Optional[models.QuerySet] = None, **kwargs) -> models.Model:
48
48
  with suppress(ObjectDoesNotExist):
49
- return self.model.objects.filter(
49
+ return self.model.objects.get(
50
50
  instrument=data["instrument"],
51
51
  contract_identifier=data["contract_identifier"],
52
52
  date=data["date"],
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from contextlib import suppress
2
3
 
3
4
  from wbcore.contrib.geography.models import Geography
4
5
 
@@ -44,14 +45,12 @@ def parse(import_source):
44
45
  d["headquarter_address"] = f"{street}, {postal_code} {city_name}"
45
46
 
46
47
  if sector_id := summary.get("sectorId", None):
47
- try:
48
+ with suppress(Exception):
48
49
  code_aggregated = f"{int(sector_id):03}"
49
50
  if industry_id := summary.get("industryId", None):
50
51
  code_aggregated += f"{int(industry_id):03}"
51
52
  if subindustry_id := summary.get("subindustryId", None):
52
53
  code_aggregated += f"{int(subindustry_id):03}"
53
- except Exception:
54
- pass
55
54
  d["classifications"] = [{"code_aggregated": code_aggregated, "group": cbinsight_group.id}]
56
55
  data.append(d)
57
56
  return {"data": data}
wbfdm/jinja2.py CHANGED
@@ -3,5 +3,6 @@ from jinjasql import JinjaSql # type: ignore
3
3
 
4
4
 
5
5
  def get_environment(**options):
6
- env = Environment(**options)
6
+ # we except only SQL template, so we need to not escape special characters (possible XSS attack for HTML template)
7
+ env = Environment(**options) # noqa: S701
7
8
  return JinjaSql(env=env, param_style="format").env
Binary file
@@ -0,0 +1,257 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2025-05-30 11:37+0200\n"
12
+ "PO-Revision-Date: 2025-05-30 09:40+0000\n"
13
+ "Language-Team: German (https://app.transifex.com/stainly/teams/171242/de/)\n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Language: de\n"
18
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
+
20
+ #: contrib/metric/filters.py:27
21
+ msgid "Basket Content Type"
22
+ msgstr ""
23
+
24
+ #: contrib/metric/viewsets/configs/utils.py:127
25
+ msgid "Fundamentals"
26
+ msgstr ""
27
+
28
+ #: models/exchanges/exchanges.py:184
29
+ #: viewsets/configs/display/instruments.py:77
30
+ msgid "Exchange"
31
+ msgstr ""
32
+
33
+ #: models/exchanges/exchanges.py:185
34
+ msgid "Exchanges"
35
+ msgstr ""
36
+
37
+ #: models/instruments/instrument_requests.py:116
38
+ #: models/instruments/instrument_requests.py:182
39
+ msgid "An instrument already exists with the proposed identifier"
40
+ msgstr ""
41
+
42
+ #: viewsets/configs/buttons/classifications.py:10
43
+ msgid "Tree Chart"
44
+ msgstr ""
45
+
46
+ #: viewsets/configs/buttons/classifications.py:11
47
+ msgid "Icicle Chart"
48
+ msgstr ""
49
+
50
+ #: viewsets/configs/display/classifications.py:40
51
+ msgid "Child Classifications"
52
+ msgstr ""
53
+
54
+ #: viewsets/configs/display/classifications.py:43
55
+ msgid "Instruments"
56
+ msgstr ""
57
+
58
+ #: viewsets/configs/display/classifications.py:69
59
+ msgid "Classification"
60
+ msgstr ""
61
+
62
+ #: viewsets/configs/display/classifications.py:112
63
+ msgid "Related Instruments"
64
+ msgstr ""
65
+
66
+ #: viewsets/configs/display/esg.py:14
67
+ msgid "Initiated"
68
+ msgstr ""
69
+
70
+ #: viewsets/configs/display/esg.py:15
71
+ msgid "Review"
72
+ msgstr ""
73
+
74
+ #: viewsets/configs/display/esg.py:16
75
+ msgid "Headline"
76
+ msgstr ""
77
+
78
+ #: viewsets/configs/display/esg.py:17
79
+ msgid "Narrative"
80
+ msgstr ""
81
+
82
+ #: viewsets/configs/display/esg.py:18
83
+ msgid "Source"
84
+ msgstr ""
85
+
86
+ #: viewsets/configs/display/esg.py:19
87
+ msgid "Status"
88
+ msgstr ""
89
+
90
+ #: viewsets/configs/display/esg.py:20
91
+ msgid "Type"
92
+ msgstr ""
93
+
94
+ #: viewsets/configs/display/esg.py:21
95
+ msgid "Assessment"
96
+ msgstr ""
97
+
98
+ #: viewsets/configs/display/esg.py:22
99
+ msgid "Response"
100
+ msgstr ""
101
+
102
+ #: viewsets/configs/display/esg.py:70
103
+ msgid "Section"
104
+ msgstr ""
105
+
106
+ #: viewsets/configs/display/esg.py:71
107
+ msgid "Adverse sustainability indicator"
108
+ msgstr ""
109
+
110
+ #: viewsets/configs/display/esg.py:72
111
+ msgid "Metric"
112
+ msgstr ""
113
+
114
+ #: viewsets/configs/display/esg.py:73
115
+ msgid "Factor"
116
+ msgstr ""
117
+
118
+ #: viewsets/configs/display/esg.py:74
119
+ msgid "Value"
120
+ msgstr ""
121
+
122
+ #: viewsets/configs/display/instrument_lists.py:34
123
+ msgid "Exclusion List"
124
+ msgstr ""
125
+
126
+ #: viewsets/configs/display/instrument_lists.py:105
127
+ msgid "Validated"
128
+ msgstr ""
129
+
130
+ #: viewsets/configs/display/instrument_lists.py:109
131
+ msgid "Not Validated"
132
+ msgstr ""
133
+
134
+ #: viewsets/configs/display/instrument_requests.py:95
135
+ msgid "Instrument Data"
136
+ msgstr ""
137
+
138
+ #: viewsets/configs/display/instruments.py:70
139
+ #: viewsets/configs/display/officers.py:11
140
+ msgid "Name"
141
+ msgstr ""
142
+
143
+ #: viewsets/configs/display/instruments.py:73
144
+ msgid "Information"
145
+ msgstr ""
146
+
147
+ #: viewsets/configs/display/instruments.py:76
148
+ msgid "Instrument Type"
149
+ msgstr ""
150
+
151
+ #: viewsets/configs/display/instruments.py:78
152
+ msgid "ISIN"
153
+ msgstr ""
154
+
155
+ #: viewsets/configs/display/instruments.py:79
156
+ msgid "Ticker"
157
+ msgstr ""
158
+
159
+ #: viewsets/configs/display/instruments.py:80
160
+ msgid "RIC"
161
+ msgstr ""
162
+
163
+ #: viewsets/configs/display/instruments.py:81
164
+ msgid "Refinitiv Mnemonic"
165
+ msgstr ""
166
+
167
+ #: viewsets/configs/display/instruments.py:82
168
+ msgid "Description"
169
+ msgstr ""
170
+
171
+ #: viewsets/configs/display/instruments.py:83
172
+ msgid "Currency"
173
+ msgstr ""
174
+
175
+ #: viewsets/configs/display/instruments.py:84
176
+ msgid "Country"
177
+ msgstr ""
178
+
179
+ #: viewsets/configs/display/instruments.py:91
180
+ msgid "Extra"
181
+ msgstr ""
182
+
183
+ #: viewsets/configs/display/instruments.py:94
184
+ msgid "Primary"
185
+ msgstr ""
186
+
187
+ #: viewsets/configs/display/instruments.py:95
188
+ msgid "Investable Universe"
189
+ msgstr ""
190
+
191
+ #: viewsets/configs/display/instruments.py:96
192
+ msgid "Security"
193
+ msgstr ""
194
+
195
+ #: viewsets/configs/display/instruments.py:97
196
+ msgid "Internally Managed"
197
+ msgstr ""
198
+
199
+ #: viewsets/configs/display/instruments.py:100
200
+ msgid "Performance"
201
+ msgstr ""
202
+
203
+ #: viewsets/configs/display/instruments.py:330
204
+ msgid "Hierarchy"
205
+ msgstr ""
206
+
207
+ #: viewsets/configs/display/instruments.py:343
208
+ msgid "News"
209
+ msgstr ""
210
+
211
+ #: viewsets/configs/display/officers.py:10
212
+ msgid "Position"
213
+ msgstr ""
214
+
215
+ #: viewsets/configs/display/officers.py:12
216
+ msgid "Age"
217
+ msgstr ""
218
+
219
+ #: viewsets/configs/display/officers.py:13
220
+ msgid "Sex"
221
+ msgstr ""
222
+
223
+ #: viewsets/configs/display/officers.py:14
224
+ msgid "Start"
225
+ msgstr ""
226
+
227
+ #: viewsets/configs/display/prices.py:12
228
+ msgid "Date"
229
+ msgstr ""
230
+
231
+ #: viewsets/configs/display/prices.py:13
232
+ msgid "Open"
233
+ msgstr ""
234
+
235
+ #: viewsets/configs/display/prices.py:14
236
+ msgid "High"
237
+ msgstr ""
238
+
239
+ #: viewsets/configs/display/prices.py:15
240
+ msgid "Low"
241
+ msgstr ""
242
+
243
+ #: viewsets/configs/display/prices.py:16
244
+ msgid "Close"
245
+ msgstr ""
246
+
247
+ #: viewsets/configs/display/prices.py:17
248
+ msgid "Volume"
249
+ msgstr ""
250
+
251
+ #: viewsets/configs/display/prices.py:18
252
+ msgid "Oustanding Shares"
253
+ msgstr ""
254
+
255
+ #: viewsets/configs/display/prices.py:19
256
+ msgid "Market Cap."
257
+ msgstr ""
Binary file