wbfdm 1.44.5__py2.py3-none-any.whl → 1.45.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.

Files changed (144) hide show
  1. wbfdm/admin/classifications.py +1 -0
  2. wbfdm/admin/esg.py +1 -0
  3. wbfdm/admin/exchanges.py +1 -0
  4. wbfdm/admin/instrument_lists.py +1 -0
  5. wbfdm/admin/instrument_prices.py +1 -0
  6. wbfdm/admin/instrument_requests.py +1 -0
  7. wbfdm/admin/instruments.py +1 -0
  8. wbfdm/admin/instruments_relationships.py +1 -0
  9. wbfdm/admin/options.py +1 -0
  10. wbfdm/analysis/esg/enums.py +2 -3
  11. wbfdm/analysis/esg/esg_analysis.py +1 -0
  12. wbfdm/analysis/esg/utils.py +1 -0
  13. wbfdm/analysis/financial_analysis/financial_metric_analysis.py +1 -0
  14. wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +1 -0
  15. wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +4 -1
  16. wbfdm/analysis/financial_analysis/statement_with_estimates.py +3 -4
  17. wbfdm/analysis/financial_analysis/utils.py +1 -0
  18. wbfdm/analysis/technical_analysis/technical_analysis.py +1 -0
  19. wbfdm/contrib/dsws/client.py +17 -2
  20. wbfdm/contrib/internal/dataloaders/market_data.py +1 -0
  21. wbfdm/contrib/metric/admin/instruments.py +1 -0
  22. wbfdm/contrib/metric/backends/base.py +5 -4
  23. wbfdm/contrib/metric/backends/performances.py +4 -3
  24. wbfdm/contrib/metric/backends/statistics.py +1 -0
  25. wbfdm/contrib/metric/factories.py +1 -0
  26. wbfdm/contrib/metric/filters.py +1 -0
  27. wbfdm/contrib/metric/serializers.py +1 -0
  28. wbfdm/contrib/metric/tasks.py +2 -1
  29. wbfdm/contrib/metric/tests/backends/test_performances.py +1 -0
  30. wbfdm/contrib/metric/tests/backends/test_statistics.py +1 -0
  31. wbfdm/contrib/metric/tests/test_models.py +1 -0
  32. wbfdm/contrib/metric/tests/test_viewsets.py +1 -0
  33. wbfdm/contrib/metric/viewsets/configs/display.py +1 -0
  34. wbfdm/contrib/metric/viewsets/mixins.py +1 -0
  35. wbfdm/contrib/metric/viewsets/viewsets.py +1 -0
  36. wbfdm/contrib/msci/dataloaders/esg.py +1 -0
  37. wbfdm/contrib/msci/dataloaders/esg_controversies.py +1 -0
  38. wbfdm/contrib/msci/sync.py +1 -0
  39. wbfdm/contrib/qa/dataloaders/adjustments.py +5 -5
  40. wbfdm/contrib/qa/dataloaders/corporate_actions.py +5 -5
  41. wbfdm/contrib/qa/dataloaders/financials.py +1 -0
  42. wbfdm/contrib/qa/dataloaders/market_data.py +9 -7
  43. wbfdm/contrib/qa/dataloaders/officers.py +8 -10
  44. wbfdm/contrib/qa/dataloaders/reporting_dates.py +1 -0
  45. wbfdm/contrib/qa/dataloaders/statements.py +1 -0
  46. wbfdm/contrib/qa/dataloaders/utils.py +2 -2
  47. wbfdm/dataloaders/proxies.py +1 -0
  48. wbfdm/factories/classifications.py +1 -0
  49. wbfdm/factories/controversies.py +1 -0
  50. wbfdm/factories/exchanges.py +1 -0
  51. wbfdm/factories/instrument_list.py +1 -0
  52. wbfdm/factories/instrument_prices.py +1 -0
  53. wbfdm/factories/instruments.py +1 -0
  54. wbfdm/factories/instruments_relationships.py +1 -0
  55. wbfdm/factories/options.py +1 -0
  56. wbfdm/figures/financials/financial_analysis_charts.py +1 -0
  57. wbfdm/figures/financials/financials_charts.py +1 -0
  58. wbfdm/filters/classifications.py +1 -0
  59. wbfdm/filters/exchanges.py +1 -0
  60. wbfdm/filters/financials.py +1 -0
  61. wbfdm/filters/financials_analysis.py +1 -0
  62. wbfdm/filters/instrument_prices.py +1 -0
  63. wbfdm/filters/instruments.py +1 -0
  64. wbfdm/filters/utils.py +1 -0
  65. wbfdm/import_export/backends/cbinsights/mixin.py +1 -0
  66. wbfdm/import_export/backends/refinitiv/mixin.py +1 -0
  67. wbfdm/import_export/backends/refinitiv/utils/controller.py +1 -0
  68. wbfdm/import_export/handlers/instrument.py +9 -9
  69. wbfdm/import_export/handlers/instrument_price.py +5 -0
  70. wbfdm/import_export/parsers/cbinsights/deals.py +1 -0
  71. wbfdm/import_export/parsers/cbinsights/equities.py +1 -0
  72. wbfdm/import_export/parsers/cbinsights/fundamentals.py +1 -0
  73. wbfdm/import_export/parsers/refinitiv/instrument.py +1 -0
  74. wbfdm/import_export/parsers/refinitiv/instrument_price.py +1 -0
  75. wbfdm/import_export/resources/classification.py +1 -0
  76. wbfdm/import_export/resources/instrument_prices.py +1 -0
  77. wbfdm/import_export/resources/instruments.py +1 -0
  78. wbfdm/models/esg/controversies.py +1 -0
  79. wbfdm/models/instruments/instrument_lists.py +1 -0
  80. wbfdm/models/instruments/instrument_prices.py +1 -0
  81. wbfdm/models/instruments/instrument_requests.py +1 -0
  82. wbfdm/models/instruments/instruments.py +12 -7
  83. wbfdm/models/instruments/llm/create_instrument_news_relationships.py +2 -2
  84. wbfdm/models/instruments/mixin/instruments.py +24 -19
  85. wbfdm/models/instruments/options.py +1 -0
  86. wbfdm/models/instruments/private_equities.py +1 -0
  87. wbfdm/models/instruments/utils.py +1 -0
  88. wbfdm/serializers/esg.py +1 -0
  89. wbfdm/serializers/exchanges.py +1 -0
  90. wbfdm/serializers/instruments/__init__.py +1 -0
  91. wbfdm/serializers/instruments/classifications.py +1 -0
  92. wbfdm/serializers/instruments/instrument_lists.py +1 -0
  93. wbfdm/serializers/instruments/instrument_prices.py +1 -0
  94. wbfdm/serializers/instruments/instrument_relationships.py +6 -8
  95. wbfdm/serializers/instruments/instrument_requests.py +1 -0
  96. wbfdm/serializers/instruments/instruments.py +1 -1
  97. wbfdm/signals.py +3 -0
  98. wbfdm/tasks.py +45 -5
  99. wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +1 -0
  100. wbfdm/tests/analysis/financial_analysis/test_utils.py +1 -0
  101. wbfdm/tests/analysis/test_esg.py +1 -0
  102. wbfdm/tests/dataloaders/test_cache.py +1 -0
  103. wbfdm/tests/models/test_classifications.py +1 -0
  104. wbfdm/tests/models/test_instrument_list.py +1 -0
  105. wbfdm/tests/models/test_instrument_prices.py +1 -0
  106. wbfdm/tests/models/test_instruments.py +2 -1
  107. wbfdm/tests/models/test_merge.py +14 -9
  108. wbfdm/tests/models/test_options.py +1 -0
  109. wbfdm/urls.py +1 -0
  110. wbfdm/viewsets/configs/buttons/instruments.py +1 -0
  111. wbfdm/viewsets/configs/display/esg.py +1 -0
  112. wbfdm/viewsets/configs/display/instrument_lists.py +1 -0
  113. wbfdm/viewsets/configs/display/instrument_prices.py +1 -0
  114. wbfdm/viewsets/configs/display/instrument_requests.py +1 -0
  115. wbfdm/viewsets/configs/display/instruments.py +31 -4
  116. wbfdm/viewsets/configs/endpoints/__init__.py +1 -0
  117. wbfdm/viewsets/configs/endpoints/instrument_requests.py +1 -0
  118. wbfdm/viewsets/configs/endpoints/instruments_relationships.py +10 -0
  119. wbfdm/viewsets/configs/titles/classifications.py +1 -0
  120. wbfdm/viewsets/configs/titles/financials_analysis.py +1 -0
  121. wbfdm/viewsets/configs/titles/instrument_prices.py +1 -0
  122. wbfdm/viewsets/configs/titles/market_data.py +1 -0
  123. wbfdm/viewsets/esg.py +1 -0
  124. wbfdm/viewsets/exchanges.py +1 -0
  125. wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +1 -0
  126. wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +2 -1
  127. wbfdm/viewsets/financial_analysis/financial_summary.py +4 -4
  128. wbfdm/viewsets/financial_analysis/statement_with_estimates.py +1 -0
  129. wbfdm/viewsets/instruments/classifications.py +1 -0
  130. wbfdm/viewsets/instruments/financials_analysis.py +1 -0
  131. wbfdm/viewsets/instruments/instrument_lists.py +1 -0
  132. wbfdm/viewsets/instruments/instrument_prices.py +1 -0
  133. wbfdm/viewsets/instruments/instrument_requests.py +1 -0
  134. wbfdm/viewsets/instruments/instruments.py +1 -0
  135. wbfdm/viewsets/instruments/instruments_relationships.py +17 -0
  136. wbfdm/viewsets/market_data.py +3 -2
  137. wbfdm/viewsets/mixins.py +1 -0
  138. wbfdm/viewsets/officers.py +1 -0
  139. wbfdm/viewsets/prices.py +1 -0
  140. wbfdm/viewsets/statements/statements.py +1 -0
  141. wbfdm/viewsets/technical_analysis/monthly_performances.py +1 -0
  142. {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/METADATA +1 -1
  143. {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/RECORD +144 -144
  144. {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/WHEEL +0 -0
@@ -6,6 +6,7 @@ import numpy as np
6
6
  import pandas as pd
7
7
  from django.db import models
8
8
  from wbcore.contrib.currency.models import CurrencyFXRates
9
+
9
10
  from wbfdm.models import Instrument
10
11
 
11
12
 
@@ -11,6 +11,7 @@ from django.db.models import ExpressionWrapper, F, FloatField, QuerySet
11
11
  from pandas.tseries.offsets import BYearEnd
12
12
  from plotly.subplots import make_subplots
13
13
  from wbcore.contrib.currency.models import CurrencyFXRates
14
+
14
15
  from wbfdm.enums import MarketData
15
16
  from wbfdm.models import Instrument, InstrumentPrice
16
17
 
@@ -1,4 +1,5 @@
1
1
  from wbcore import filters as wb_filters
2
+
2
3
  from wbfdm.models import (
3
4
  Classification,
4
5
  ClassificationGroup,
@@ -1,4 +1,5 @@
1
1
  from wbcore import filters as wb_filters
2
+
2
3
  from wbfdm.models import Exchange
3
4
 
4
5
 
@@ -1,5 +1,6 @@
1
1
  from wbcore import filters
2
2
  from wbcore.filters.defaults import five_year_data_range
3
+
3
4
  from wbfdm.enums import CalendarType, DataType, Indicator, MarketDataChartType
4
5
  from wbfdm.models.instruments import Instrument
5
6
 
@@ -4,6 +4,7 @@ from django.db import models
4
4
  from pandas.tseries.offsets import BYearEnd
5
5
  from psycopg.types.range import DateRange
6
6
  from wbcore import filters as wb_filters
7
+
7
8
  from wbfdm.figures.financials.financial_analysis_charts import (
8
9
  PeriodChoices,
9
10
  VariableChoices,
@@ -3,6 +3,7 @@ from datetime import date
3
3
  from django.db import models
4
4
  from psycopg.types.range import DateRange
5
5
  from wbcore import filters as wb_filters
6
+
6
7
  from wbfdm.filters.utils import get_earliest_date, get_latest_date
7
8
  from wbfdm.models import Instrument, InstrumentPrice
8
9
 
@@ -4,6 +4,7 @@ from django.db.models import Q
4
4
  from psycopg.types.range import DateRange
5
5
  from wbcore import filters as wb_filters
6
6
  from wbcore.contrib.tags.filters import TagFilterMixin
7
+
7
8
  from wbfdm.filters.utils import get_earliest_date, get_latest_date
8
9
  from wbfdm.models.instruments import (
9
10
  ClassificationGroup,
wbfdm/filters/utils.py CHANGED
@@ -3,6 +3,7 @@ from datetime import date
3
3
  from pandas.tseries.offsets import BDay, BMonthBegin, BMonthEnd
4
4
  from psycopg.types.range import DateRange
5
5
  from wbcore.utils.date import current_quarter_date_start
6
+
6
7
  from wbfdm.models import Instrument
7
8
 
8
9
 
@@ -1,6 +1,7 @@
1
1
  from datetime import date
2
2
 
3
3
  from django.db import models
4
+
4
5
  from wbfdm.models.instruments import Instrument
5
6
 
6
7
 
@@ -1,6 +1,7 @@
1
1
  from datetime import date
2
2
 
3
3
  from django.db import models
4
+
4
5
  from wbfdm.models.instruments import Instrument
5
6
 
6
7
 
@@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
6
6
  from pandas.tseries.offsets import QuarterEnd, YearEnd
7
7
  from tqdm import tqdm
8
8
  from wbcore.contrib.io.models import ImportedObjectProviderRelationship, Provider
9
+
9
10
  from wbfdm.contrib.dsws.client import Client
10
11
  from wbfdm.models.instruments.instruments import Instrument
11
12
 
@@ -12,6 +12,7 @@ from wbcore.contrib.currency.import_export.handlers import CurrencyImportHandler
12
12
  from wbcore.contrib.geography.models import Geography
13
13
  from wbcore.contrib.io.exceptions import DeserializationError
14
14
  from wbcore.contrib.io.imports import ImportExportHandler, ImportState
15
+
15
16
  from wbfdm.models.exchanges import Exchange
16
17
 
17
18
 
@@ -66,7 +67,6 @@ class InstrumentLookup:
66
67
  "refinitiv_mnemonic_code",
67
68
  "sedol",
68
69
  "cusip",
69
- "ticker",
70
70
  "identifier",
71
71
  ]:
72
72
  if identifier := identifiers.get(identifier_key, None):
@@ -78,7 +78,6 @@ class InstrumentLookup:
78
78
  identifier = identifier.upper()
79
79
  instrument = instruments.get(**{identifier_key: identifier})
80
80
  break
81
-
82
81
  if not instrument and not exact_lookup:
83
82
  if instrument_type:
84
83
  if isinstance(instrument_type, str): # in case we receive a key as instrument type
@@ -102,14 +101,15 @@ class InstrumentLookup:
102
101
  conditions.append(Q(old_isins__contains=[field_value]))
103
102
  if conditions:
104
103
  instruments = instruments.filter(reduce(operator.or_, conditions))
105
- if currency or exchange:
106
- instruments_tmp = instruments
107
- if exchange:
108
- instruments_tmp = instruments_tmp.filter(exchange=exchange)
109
- if currency:
110
- instruments_tmp = instruments_tmp.filter(currency=currency)
111
- if instruments_tmp.count() >= 1:
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():
112
111
  instruments = instruments_tmp
112
+
113
113
  # last chance
114
114
  if name and instruments.count() > 1:
115
115
  instruments = instruments.annotate(similarity_score=TrigramSimilarity("name", name))
@@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError
5
5
  from django.db import models
6
6
  from wbcore.contrib.io.exceptions import DeserializationError
7
7
  from wbcore.contrib.io.imports import ImportExportHandler
8
+
8
9
  from wbfdm.import_export.handlers.instrument import InstrumentImportHandler
9
10
 
10
11
  MAX_NB_DATES_BEFORE_HISTORICAL_IMPORT = 3
@@ -61,6 +62,10 @@ class InstrumentPriceImportHandler(ImportExportHandler):
61
62
  if price.exists():
62
63
  return price.first()
63
64
 
65
+ def _save_object(self, _object, **kwargs):
66
+ _object.compute_and_update_statistics() # compute beta, correlation, sharpe and annualized daily volatility everytime object changes
67
+ return super()._save_object(_object, **kwargs)
68
+
64
69
  def _post_processing_objects(
65
70
  self,
66
71
  created_objs: List[models.Model],
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
+
3
4
  from wbfdm.models import Deal
4
5
 
5
6
  COLUMNS_MAP = {
@@ -1,6 +1,7 @@
1
1
  import json
2
2
 
3
3
  from wbcore.contrib.geography.models import Geography
4
+
4
5
  from wbfdm.models import ClassificationGroup
5
6
 
6
7
  FIELDS_MAP = {
@@ -2,6 +2,7 @@ import json
2
2
 
3
3
  import pandas as pd
4
4
  from django.contrib.contenttypes.models import ContentType
5
+
5
6
  from wbfdm.models import Instrument
6
7
 
7
8
 
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
+
3
4
  from wbfdm.import_export.backends.refinitiv.instrument import DEFAULT_MAPPING
4
5
  from wbfdm.models import ClassificationGroup
5
6
 
@@ -1,5 +1,6 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
+
3
4
  from wbfdm.import_export.backends.refinitiv.instrument_price import DEFAULT_MAPPING
4
5
 
5
6
  from .utils import _clean_and_return_dict
@@ -1,4 +1,5 @@
1
1
  from import_export.widgets import ManyToManyWidget
2
+
2
3
  from wbfdm.models import Classification, ClassificationGroup
3
4
  from wbfdm.preferences import get_default_classification_group
4
5
 
@@ -1,6 +1,7 @@
1
1
  from import_export import fields
2
2
  from import_export.widgets import ForeignKeyWidget
3
3
  from wbcore.contrib.io.resources import FilterModelResource
4
+
4
5
  from wbfdm.models import Instrument, InstrumentPrice
5
6
 
6
7
 
@@ -13,6 +13,7 @@ from wbcore.contrib.geography.import_export.resources.geography import (
13
13
  )
14
14
  from wbcore.contrib.io.resources import FilterModelResource
15
15
  from wbcore.contrib.notifications.dispatch import send_notification
16
+
16
17
  from wbfdm.models import Exchange, Instrument, InstrumentClassificationThroughModel
17
18
 
18
19
  from .classification import ClassificationManyToManyWidget
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Self
2
2
 
3
3
  from django.db import models
4
+
4
5
  from wbfdm.enums import (
5
6
  ESGControveryFlag,
6
7
  ESGControverySeverity,
@@ -4,6 +4,7 @@ from wbcore.contrib.io.mixins import ImportMixin
4
4
  from wbcore.contrib.notifications.utils import create_notification_type
5
5
  from wbcore.models import WBModel
6
6
  from wbcore.utils.models import ComplexToStringMixin
7
+
7
8
  from wbfdm.import_export.handlers.instrument_list import InstrumentListImportHandler
8
9
  from wbfdm.models.instruments.instruments import Instrument
9
10
 
@@ -23,6 +23,7 @@ from wbcore.contrib.currency.models import CurrencyFXRates
23
23
  from wbcore.contrib.io.mixins import ImportMixin
24
24
  from wbcore.models import DynamicDecimalField, DynamicFloatField, DynamicModel, WBModel
25
25
  from wbcore.signals import pre_merge
26
+
26
27
  from wbfdm.analysis.financial_analysis.financial_statistics_analysis import (
27
28
  FinancialStatistics,
28
29
  )
@@ -10,6 +10,7 @@ from wbcore.contrib.tags.models import Tag
10
10
  from wbcore.enums import RequestType
11
11
  from wbcore.metadata.configs.buttons import ActionButton, ButtonDefaultColor
12
12
  from wbcore.models import WBModel
13
+
13
14
  from wbfdm.models.instruments.classifications import Classification
14
15
 
15
16
  from .instruments import Instrument, InstrumentType
@@ -30,6 +30,9 @@ from wbcore.contrib.tags.models import TagModelMixin
30
30
  from wbcore.models import WBModel
31
31
  from wbcore.signals import pre_merge
32
32
  from wbcore.utils.models import ComplexToStringMixin
33
+ from wbnews.models import News
34
+ from wbnews.signals import create_news_relationships
35
+
33
36
  from wbfdm.analysis import TechnicalAnalysis
34
37
  from wbfdm.contrib.internal.dataloaders.market_data import MarketDataDataloader
35
38
  from wbfdm.contrib.metric.dispatch import compute_metrics
@@ -42,8 +45,6 @@ from wbfdm.signals import (
42
45
  add_instrument_to_investable_universe,
43
46
  instrument_price_imported,
44
47
  )
45
- from wbnews.models import News
46
- from wbnews.signals import create_news_relationships
47
48
 
48
49
  from ...dataloaders.proxies import InstrumentDataloaderProxy
49
50
  from .instrument_relationships import RelatedInstrumentThroughModel
@@ -514,7 +515,9 @@ class Instrument(ComplexToStringMixin, TagModelMixin, ImportMixin, InstrumentPMS
514
515
  models.Index(fields=["is_security"], name="instrument_security_index"),
515
516
  models.Index(fields=["level"], name="instrument_level_index"),
516
517
  GinIndex(fields=["search_vector"], name="instrument_sv_gin_idx"), # type: ignore
517
- GinIndex(fields=["trigram_search_vector"], opclasses=["gin_trgm_ops"], name="instrument_trigram_sv_gin_idx"), # type: ignore
518
+ GinIndex(
519
+ fields=["trigram_search_vector"], opclasses=["gin_trgm_ops"], name="instrument_trigram_sv_gin_idx"
520
+ ), # type: ignore
518
521
  ]
519
522
  notification_types = [
520
523
  create_notification_type(
@@ -841,7 +844,7 @@ class Instrument(ComplexToStringMixin, TagModelMixin, ImportMixin, InstrumentPMS
841
844
  def technical_benchmark_analysis(self, from_date: date | None = None, to_date: date | None = None):
842
845
  return TechnicalAnalysis.init_close_from_instrument(self, from_date, to_date)
843
846
 
844
- def import_prices(self, start: date | None = None, end: date | None = None, clear: bool = False):
847
+ def get_price_objects(self, start: date | None = None, end: date | None = None, clear: bool = False):
845
848
  if not self.is_leaf_node():
846
849
  raise ValueError("Cannot import price on a non-leaf node")
847
850
  if not start:
@@ -858,12 +861,14 @@ class Instrument(ComplexToStringMixin, TagModelMixin, ImportMixin, InstrumentPMS
858
861
  # we detect when was the last date price imported before start and switch the start date from there
859
862
  with suppress(ObjectDoesNotExist):
860
863
  start = self.prices.filter(date__lte=start).latest("date").date
861
- # Import instrument prices
862
- self.save_prices_in_db(start, end, clear=clear)
864
+ yield from self._get_price_objects(start, end, clear=clear)
863
865
 
866
+ def import_prices(self, start: date | None = None, end: date | None = None, clear: bool = False):
867
+ # Import instrument prices
868
+ objs = list(self.get_price_objects(start=start, end=end, clear=clear))
869
+ self.bulk_save_instrument_prices(objs)
864
870
  # compute daily statistics & performances
865
871
  self.update_last_valuation_date()
866
-
867
872
  instrument_price_imported.send(sender=Instrument, instrument=self, start=start, end=end)
868
873
 
869
874
  @classmethod
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from typing import Any
2
3
 
3
4
  from celery import shared_task
@@ -6,7 +7,6 @@ from langchain_core.messages import HumanMessage, SystemMessage
6
7
  from pydantic import BaseModel, Field
7
8
  from wbcore.contrib.ai.exceptions import APIStatusErrors
8
9
  from wbcore.contrib.ai.llm.utils import run_llm
9
- import logging
10
10
 
11
11
  logger = logging.getLogger("llm")
12
12
 
@@ -82,5 +82,5 @@ def run_company_extraction_llm(title: str, description: str, *args) -> list[dict
82
82
  except tuple(APIStatusErrors) as e: # for APIStatusError, we let celery retry it
83
83
  raise e
84
84
  except Exception as e: # otherwise we log the error and silently fail
85
- logger.error(str(e))
85
+ logger.warning(str(e))
86
86
  return relationships
@@ -4,12 +4,13 @@ from collections import defaultdict
4
4
  from contextlib import suppress
5
5
  from datetime import date, timedelta
6
6
  from decimal import Decimal
7
- from typing import Dict, Optional
7
+ from typing import Dict, Iterable, Optional
8
8
 
9
9
  import numpy as np
10
10
  import pandas as pd
11
11
  from pandas.tseries.offsets import BDay
12
12
  from wbcore.contrib.currency.models import CurrencyFXRates
13
+
13
14
  from wbfdm.analysis.financial_analysis.financial_statistics_analysis import (
14
15
  FinancialStatistics,
15
16
  )
@@ -198,7 +199,7 @@ class InstrumentPMSMixin:
198
199
 
199
200
  return df
200
201
 
201
- def save_prices_in_db(self, from_date: date, to_date: date, clear: bool = False):
202
+ def _get_price_objects(self, from_date: date, to_date: date, clear: bool = False) -> Iterable[InstrumentPrice]:
202
203
  df = pd.DataFrame(
203
204
  self.__class__.objects.filter(id=self.id).dl.market_data(
204
205
  from_date=from_date
@@ -236,8 +237,7 @@ class InstrumentPMSMixin:
236
237
  df = df[df.index.date >= from_date] # we import only from the requested from_date
237
238
  df = df.reset_index().dropna(subset=["index", "close"])
238
239
  df = df.replace([np.inf, -np.inf, np.nan], None)
239
- update_objs = []
240
- create_objs = []
240
+
241
241
  for row in df.to_dict("records"):
242
242
  if (_date := row.get("index")) and (close := row.get("close", None)):
243
243
  # we make sure data does not exist 10 digits (for db constraint)
@@ -264,7 +264,8 @@ class InstrumentPMSMixin:
264
264
  p.market_capitalization = row.get("market_capitalization", p.market_capitalization)
265
265
  p.market_capitalization_consolidated = p.market_capitalization
266
266
  p.set_dynamic_field(False)
267
- update_objs.append(p)
267
+ p.id = None
268
+ yield p
268
269
  except InstrumentPrice.DoesNotExist:
269
270
  with suppress(CurrencyFXRates.DoesNotExist):
270
271
  p = InstrumentPrice(
@@ -281,17 +282,21 @@ class InstrumentPMSMixin:
281
282
  volume_50d=row.get("volume_50d", None),
282
283
  )
283
284
  p.set_dynamic_field(False)
284
- create_objs.append(p)
285
- InstrumentPrice.objects.bulk_update(
286
- update_objs,
287
- fields=[
288
- "net_value",
289
- "gross_value",
290
- "volume",
291
- "market_capitalization",
292
- "market_capitalization_consolidated",
293
- "calculated",
294
- "volume_50d",
295
- ],
296
- )
297
- InstrumentPrice.objects.bulk_create(create_objs, ignore_conflicts=True)
285
+ yield p
286
+
287
+ @classmethod
288
+ def bulk_save_instrument_prices(cls, objs):
289
+ InstrumentPrice.objects.bulk_create(
290
+ objs,
291
+ unique_fields=["instrument", "calculated", "date"],
292
+ update_conflicts=True,
293
+ update_fields=[
294
+ "net_value",
295
+ "gross_value",
296
+ "volume",
297
+ "market_capitalization",
298
+ "market_capitalization_consolidated",
299
+ "calculated",
300
+ "volume_50d",
301
+ ],
302
+ )
@@ -1,5 +1,6 @@
1
1
  from django.db import models
2
2
  from wbcore.contrib.io.mixins import ImportMixin
3
+
3
4
  from wbfdm.import_export.handlers.option import (
4
5
  OptionAggregateImportHandler,
5
6
  OptionImportHandler,
@@ -1,6 +1,7 @@
1
1
  from django.contrib.postgres.fields import ArrayField
2
2
  from django.db import models
3
3
  from wbcore.contrib.io.mixins import ImportMixin
4
+
4
5
  from wbfdm.import_export.handlers.private_equities import DealImportHandler
5
6
 
6
7
 
@@ -3,6 +3,7 @@ from functools import lru_cache
3
3
 
4
4
  from wbcore.contrib.currency.models import Currency
5
5
  from wbcore.contrib.geography.models import Geography
6
+
6
7
  from wbfdm.preferences import get_non_ticker_words
7
8
 
8
9
  START_DELIMITER = r"(?:^|(?<=[(\s\.\-<]))"
wbfdm/serializers/esg.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from wbcore import serializers
2
+
2
3
  from wbfdm.enums import (
3
4
  ESGControveryFlag,
4
5
  ESGControverySeverity,
@@ -1,5 +1,6 @@
1
1
  from wbcore import serializers as wb_serializers
2
2
  from wbcore.contrib.geography.serializers import GeographyRepresentationSerializer
3
+
3
4
  from wbfdm.models import Exchange
4
5
 
5
6
 
@@ -19,6 +19,7 @@ from .instrument_relationships import (
19
19
  RelatedInstrumentThroughInstrumentModelSerializer,
20
20
  InstrumentFavoriteGroupRepresentationSerializer,
21
21
  InstrumentFavoriteGroupModelSerializer,
22
+ ReadOnlyInstrumentFavoriteGroupModelSerializer,
22
23
  InstrumentClassificationThroughModelSerializer,
23
24
  )
24
25
  from .instrument_requests import InstrumentRequestRepresentationSerializer, InstrumentRequestModelSerializer
@@ -2,6 +2,7 @@ from rest_framework import serializers as rf_serializers
2
2
  from rest_framework.reverse import reverse
3
3
  from wbcore import serializers as wb_serializers
4
4
  from wbcore.serializers import DefaultAttributeFromRemoteField, DefaultFromView
5
+
5
6
  from wbfdm.models.instruments import Classification, ClassificationGroup
6
7
  from wbfdm.preferences import get_default_classification_group
7
8
 
@@ -1,5 +1,6 @@
1
1
  from rest_framework.reverse import reverse
2
2
  from wbcore import serializers as wb_serializers
3
+
3
4
  from wbfdm.models.instruments.instrument_lists import (
4
5
  InstrumentList,
5
6
  InstrumentListThroughModel,
@@ -1,4 +1,5 @@
1
1
  from wbcore import serializers as wb_serializers
2
+
2
3
  from wbfdm.models import InstrumentPrice
3
4
  from wbfdm.serializers.instruments.instruments import (
4
5
  InvestableInstrumentRepresentationSerializer,
@@ -4,6 +4,7 @@ from wbcore import serializers as wb_serializers
4
4
  from wbcore.contrib.directory.models import Person
5
5
  from wbcore.contrib.directory.serializers import PersonRepresentationSerializer
6
6
  from wbcore.contrib.tags.serializers import TagSerializerMixin
7
+
7
8
  from wbfdm.models import (
8
9
  Classification,
9
10
  Instrument,
@@ -34,16 +35,8 @@ class InstrumentFavoriteGroupModelSerializer(wb_serializers.ModelSerializer):
34
35
  owner = wb_serializers.PrimaryKeyRelatedField(
35
36
  queryset=lambda: Person.objects.filter_only_internal(),
36
37
  default=wb_serializers.CurrentUserDefault("profile"),
37
- read_only=lambda view: not view.request.user.is_superuser,
38
38
  )
39
39
  _owner = PersonRepresentationSerializer(source="owner")
40
- public = wb_serializers.BooleanField(read_only=True)
41
-
42
- # @wb_serializers.register_only_instance_resource()
43
- # def instruments(self, instance, request, user, **kwargs):
44
- # if instance.instruments.exists():
45
- # return {"instruments": reverse("wbfdm:favoritegroup-instrument-list", args=[instance.id], request=request)}
46
- # return dict()
47
40
 
48
41
  def validate(self, data):
49
42
  if not data.get("owner", None):
@@ -65,6 +58,11 @@ class InstrumentFavoriteGroupModelSerializer(wb_serializers.ModelSerializer):
65
58
  )
66
59
 
67
60
 
61
+ class ReadOnlyInstrumentFavoriteGroupModelSerializer(InstrumentFavoriteGroupModelSerializer):
62
+ class Meta(InstrumentFavoriteGroupModelSerializer.Meta):
63
+ read_only_fields = InstrumentFavoriteGroupModelSerializer.Meta.fields
64
+
65
+
68
66
  class RelatedInstrumentThroughInstrumentModelSerializer(wb_serializers.ModelSerializer):
69
67
  _related_instrument = InvestableInstrumentRepresentationSerializer(source="related_instrument")
70
68
  _instrument = InvestableInstrumentRepresentationSerializer(source="instrument")
@@ -1,5 +1,6 @@
1
1
  from wbcore import serializers as wb_serializers
2
2
  from wbcore.contrib.directory.serializers import PersonRepresentationSerializer
3
+
3
4
  from wbfdm.models.instruments import InstrumentRequest
4
5
 
5
6
  from .instruments import InstrumentModelSerializer, InstrumentRepresentationSerializer
@@ -8,6 +8,7 @@ from wbcore.contrib.currency.serializers import CurrencyRepresentationSerializer
8
8
  from wbcore.contrib.geography.models import Geography
9
9
  from wbcore.contrib.geography.serializers import CountryRepresentationSerializer
10
10
  from wbcore.contrib.tags.serializers import TagRepresentationSerializer
11
+
11
12
  from wbfdm.models import Instrument, InstrumentType
12
13
 
13
14
  from ..exchanges import ExchangeRepresentationSerializer
@@ -128,7 +129,6 @@ class InstrumentModelListSerializer(
128
129
  "instrument_type",
129
130
  "old_isins",
130
131
  "inception_date",
131
- "delisted_date",
132
132
  "country",
133
133
  "is_active",
134
134
  "parent",
wbfdm/signals.py CHANGED
@@ -5,3 +5,6 @@ add_instrument_to_investable_universe = ModelSignal(use_caching=False)
5
5
 
6
6
  # this signal is triggered whenever prices are stored in the system and action needs to be considered
7
7
  instrument_price_imported = ModelSignal(use_caching=False)
8
+
9
+ # this signal is triggered whenever all prices are imported in the system and action needs to be considered
10
+ investable_universe_updated = ModelSignal(use_caching=False)