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.
- wbfdm/admin/classifications.py +1 -0
- wbfdm/admin/esg.py +1 -0
- wbfdm/admin/exchanges.py +1 -0
- wbfdm/admin/instrument_lists.py +1 -0
- wbfdm/admin/instrument_prices.py +1 -0
- wbfdm/admin/instrument_requests.py +1 -0
- wbfdm/admin/instruments.py +1 -0
- wbfdm/admin/instruments_relationships.py +1 -0
- wbfdm/admin/options.py +1 -0
- wbfdm/analysis/esg/enums.py +2 -3
- wbfdm/analysis/esg/esg_analysis.py +1 -0
- wbfdm/analysis/esg/utils.py +1 -0
- wbfdm/analysis/financial_analysis/financial_metric_analysis.py +1 -0
- wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +1 -0
- wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +4 -1
- wbfdm/analysis/financial_analysis/statement_with_estimates.py +3 -4
- wbfdm/analysis/financial_analysis/utils.py +1 -0
- wbfdm/analysis/technical_analysis/technical_analysis.py +1 -0
- wbfdm/contrib/dsws/client.py +17 -2
- wbfdm/contrib/internal/dataloaders/market_data.py +1 -0
- wbfdm/contrib/metric/admin/instruments.py +1 -0
- wbfdm/contrib/metric/backends/base.py +5 -4
- wbfdm/contrib/metric/backends/performances.py +4 -3
- wbfdm/contrib/metric/backends/statistics.py +1 -0
- wbfdm/contrib/metric/factories.py +1 -0
- wbfdm/contrib/metric/filters.py +1 -0
- wbfdm/contrib/metric/serializers.py +1 -0
- wbfdm/contrib/metric/tasks.py +2 -1
- wbfdm/contrib/metric/tests/backends/test_performances.py +1 -0
- wbfdm/contrib/metric/tests/backends/test_statistics.py +1 -0
- wbfdm/contrib/metric/tests/test_models.py +1 -0
- wbfdm/contrib/metric/tests/test_viewsets.py +1 -0
- wbfdm/contrib/metric/viewsets/configs/display.py +1 -0
- wbfdm/contrib/metric/viewsets/mixins.py +1 -0
- wbfdm/contrib/metric/viewsets/viewsets.py +1 -0
- wbfdm/contrib/msci/dataloaders/esg.py +1 -0
- wbfdm/contrib/msci/dataloaders/esg_controversies.py +1 -0
- wbfdm/contrib/msci/sync.py +1 -0
- wbfdm/contrib/qa/dataloaders/adjustments.py +5 -5
- wbfdm/contrib/qa/dataloaders/corporate_actions.py +5 -5
- wbfdm/contrib/qa/dataloaders/financials.py +1 -0
- wbfdm/contrib/qa/dataloaders/market_data.py +9 -7
- wbfdm/contrib/qa/dataloaders/officers.py +8 -10
- wbfdm/contrib/qa/dataloaders/reporting_dates.py +1 -0
- wbfdm/contrib/qa/dataloaders/statements.py +1 -0
- wbfdm/contrib/qa/dataloaders/utils.py +2 -2
- wbfdm/dataloaders/proxies.py +1 -0
- wbfdm/factories/classifications.py +1 -0
- wbfdm/factories/controversies.py +1 -0
- wbfdm/factories/exchanges.py +1 -0
- wbfdm/factories/instrument_list.py +1 -0
- wbfdm/factories/instrument_prices.py +1 -0
- wbfdm/factories/instruments.py +1 -0
- wbfdm/factories/instruments_relationships.py +1 -0
- wbfdm/factories/options.py +1 -0
- wbfdm/figures/financials/financial_analysis_charts.py +1 -0
- wbfdm/figures/financials/financials_charts.py +1 -0
- wbfdm/filters/classifications.py +1 -0
- wbfdm/filters/exchanges.py +1 -0
- wbfdm/filters/financials.py +1 -0
- wbfdm/filters/financials_analysis.py +1 -0
- wbfdm/filters/instrument_prices.py +1 -0
- wbfdm/filters/instruments.py +1 -0
- wbfdm/filters/utils.py +1 -0
- wbfdm/import_export/backends/cbinsights/mixin.py +1 -0
- wbfdm/import_export/backends/refinitiv/mixin.py +1 -0
- wbfdm/import_export/backends/refinitiv/utils/controller.py +1 -0
- wbfdm/import_export/handlers/instrument.py +9 -9
- wbfdm/import_export/handlers/instrument_price.py +5 -0
- wbfdm/import_export/parsers/cbinsights/deals.py +1 -0
- wbfdm/import_export/parsers/cbinsights/equities.py +1 -0
- wbfdm/import_export/parsers/cbinsights/fundamentals.py +1 -0
- wbfdm/import_export/parsers/refinitiv/instrument.py +1 -0
- wbfdm/import_export/parsers/refinitiv/instrument_price.py +1 -0
- wbfdm/import_export/resources/classification.py +1 -0
- wbfdm/import_export/resources/instrument_prices.py +1 -0
- wbfdm/import_export/resources/instruments.py +1 -0
- wbfdm/models/esg/controversies.py +1 -0
- wbfdm/models/instruments/instrument_lists.py +1 -0
- wbfdm/models/instruments/instrument_prices.py +1 -0
- wbfdm/models/instruments/instrument_requests.py +1 -0
- wbfdm/models/instruments/instruments.py +12 -7
- wbfdm/models/instruments/llm/create_instrument_news_relationships.py +2 -2
- wbfdm/models/instruments/mixin/instruments.py +24 -19
- wbfdm/models/instruments/options.py +1 -0
- wbfdm/models/instruments/private_equities.py +1 -0
- wbfdm/models/instruments/utils.py +1 -0
- wbfdm/serializers/esg.py +1 -0
- wbfdm/serializers/exchanges.py +1 -0
- wbfdm/serializers/instruments/__init__.py +1 -0
- wbfdm/serializers/instruments/classifications.py +1 -0
- wbfdm/serializers/instruments/instrument_lists.py +1 -0
- wbfdm/serializers/instruments/instrument_prices.py +1 -0
- wbfdm/serializers/instruments/instrument_relationships.py +6 -8
- wbfdm/serializers/instruments/instrument_requests.py +1 -0
- wbfdm/serializers/instruments/instruments.py +1 -1
- wbfdm/signals.py +3 -0
- wbfdm/tasks.py +45 -5
- wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +1 -0
- wbfdm/tests/analysis/financial_analysis/test_utils.py +1 -0
- wbfdm/tests/analysis/test_esg.py +1 -0
- wbfdm/tests/dataloaders/test_cache.py +1 -0
- wbfdm/tests/models/test_classifications.py +1 -0
- wbfdm/tests/models/test_instrument_list.py +1 -0
- wbfdm/tests/models/test_instrument_prices.py +1 -0
- wbfdm/tests/models/test_instruments.py +2 -1
- wbfdm/tests/models/test_merge.py +14 -9
- wbfdm/tests/models/test_options.py +1 -0
- wbfdm/urls.py +1 -0
- wbfdm/viewsets/configs/buttons/instruments.py +1 -0
- wbfdm/viewsets/configs/display/esg.py +1 -0
- wbfdm/viewsets/configs/display/instrument_lists.py +1 -0
- wbfdm/viewsets/configs/display/instrument_prices.py +1 -0
- wbfdm/viewsets/configs/display/instrument_requests.py +1 -0
- wbfdm/viewsets/configs/display/instruments.py +31 -4
- wbfdm/viewsets/configs/endpoints/__init__.py +1 -0
- wbfdm/viewsets/configs/endpoints/instrument_requests.py +1 -0
- wbfdm/viewsets/configs/endpoints/instruments_relationships.py +10 -0
- wbfdm/viewsets/configs/titles/classifications.py +1 -0
- wbfdm/viewsets/configs/titles/financials_analysis.py +1 -0
- wbfdm/viewsets/configs/titles/instrument_prices.py +1 -0
- wbfdm/viewsets/configs/titles/market_data.py +1 -0
- wbfdm/viewsets/esg.py +1 -0
- wbfdm/viewsets/exchanges.py +1 -0
- wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +1 -0
- wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +2 -1
- wbfdm/viewsets/financial_analysis/financial_summary.py +4 -4
- wbfdm/viewsets/financial_analysis/statement_with_estimates.py +1 -0
- wbfdm/viewsets/instruments/classifications.py +1 -0
- wbfdm/viewsets/instruments/financials_analysis.py +1 -0
- wbfdm/viewsets/instruments/instrument_lists.py +1 -0
- wbfdm/viewsets/instruments/instrument_prices.py +1 -0
- wbfdm/viewsets/instruments/instrument_requests.py +1 -0
- wbfdm/viewsets/instruments/instruments.py +1 -0
- wbfdm/viewsets/instruments/instruments_relationships.py +17 -0
- wbfdm/viewsets/market_data.py +3 -2
- wbfdm/viewsets/mixins.py +1 -0
- wbfdm/viewsets/officers.py +1 -0
- wbfdm/viewsets/prices.py +1 -0
- wbfdm/viewsets/statements/statements.py +1 -0
- wbfdm/viewsets/technical_analysis/monthly_performances.py +1 -0
- {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/METADATA +1 -1
- {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/RECORD +144 -144
- {wbfdm-1.44.5.dist-info → wbfdm-1.45.1.dist-info}/WHEEL +0 -0
|
@@ -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
|
|
wbfdm/filters/classifications.py
CHANGED
wbfdm/filters/exchanges.py
CHANGED
wbfdm/filters/financials.py
CHANGED
|
@@ -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
|
|
wbfdm/filters/instruments.py
CHANGED
|
@@ -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
|
@@ -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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if instruments_tmp.
|
|
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],
|
|
@@ -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
|
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
+
)
|
wbfdm/serializers/esg.py
CHANGED
wbfdm/serializers/exchanges.py
CHANGED
|
@@ -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
|
|
|
@@ -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)
|