wbfdm 1.46.9__py2.py3-none-any.whl → 1.46.11__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/factories/instrument_prices.py +3 -1
- wbfdm/models/instruments/instrument_prices.py +8 -4
- wbfdm/models/instruments/instruments.py +2 -5
- wbfdm/models/instruments/mixin/instruments.py +2 -91
- wbfdm/models/instruments/querysets.py +89 -6
- wbfdm/tasks.py +12 -8
- wbfdm/tests/models/test_instruments.py +2 -0
- {wbfdm-1.46.9.dist-info → wbfdm-1.46.11.dist-info}/METADATA +1 -1
- {wbfdm-1.46.9.dist-info → wbfdm-1.46.11.dist-info}/RECORD +10 -10
- {wbfdm-1.46.9.dist-info → wbfdm-1.46.11.dist-info}/WHEEL +0 -0
|
@@ -42,7 +42,9 @@ class InstrumentPriceFactory(factory.django.DjangoModelFactory):
|
|
|
42
42
|
)[0]
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
-
net_value = factory.Faker(
|
|
45
|
+
net_value = factory.Faker(
|
|
46
|
+
"pydecimal", min_value=90, max_value=110, right_digits=6
|
|
47
|
+
) # we narrow down the range to simulate more realistic returns
|
|
46
48
|
gross_value = factory.LazyAttribute(lambda o: o.net_value + Decimal(random.random()))
|
|
47
49
|
|
|
48
50
|
calculated = False
|
|
@@ -408,16 +408,17 @@ class InstrumentPrice(
|
|
|
408
408
|
)
|
|
409
409
|
|
|
410
410
|
def compute_and_update_statistics(self, min_period: int = 20):
|
|
411
|
-
|
|
411
|
+
df = (
|
|
412
412
|
pd.DataFrame(
|
|
413
413
|
InstrumentPrice.objects.filter_only_valid_prices()
|
|
414
414
|
.filter(instrument=self.instrument, date__lte=self.date)
|
|
415
|
-
.values_list("date", "net_value"),
|
|
416
|
-
columns=["date", "net_value"],
|
|
415
|
+
.values_list("date", "net_value", "volume"),
|
|
416
|
+
columns=["date", "net_value", "volume"],
|
|
417
417
|
)
|
|
418
418
|
.set_index("date")
|
|
419
|
-
.sort_index()
|
|
419
|
+
.sort_index()
|
|
420
420
|
)
|
|
421
|
+
prices_df = df["net_value"]
|
|
421
422
|
if not prices_df.empty and prices_df.shape[0] >= min_period:
|
|
422
423
|
financial_statistics = FinancialStatistics(prices_df)
|
|
423
424
|
min_date = prices_df.index.min()
|
|
@@ -451,6 +452,9 @@ class InstrumentPrice(
|
|
|
451
452
|
self.correlation = correlation
|
|
452
453
|
|
|
453
454
|
self.annualized_daily_volatility = financial_statistics.get_annualized_daily_volatility()
|
|
455
|
+
if not (volume_df := df["volume"]).empty:
|
|
456
|
+
self.volume_50d = volume_df.tail(50).mean()
|
|
457
|
+
self.volume_200d = volume_df.tail(200).mean()
|
|
454
458
|
|
|
455
459
|
@classmethod
|
|
456
460
|
def subquery_closest_value(
|
|
@@ -844,7 +844,7 @@ class Instrument(ComplexToStringMixin, TagModelMixin, ImportMixin, InstrumentPMS
|
|
|
844
844
|
def technical_benchmark_analysis(self, from_date: date | None = None, to_date: date | None = None):
|
|
845
845
|
return TechnicalAnalysis.init_close_from_instrument(self, from_date, to_date)
|
|
846
846
|
|
|
847
|
-
def
|
|
847
|
+
def import_prices(self, start: date | None = None, end: date | None = None, **kwargs):
|
|
848
848
|
if not self.is_leaf_node():
|
|
849
849
|
raise ValueError("Cannot import price on a non-leaf node")
|
|
850
850
|
if not start:
|
|
@@ -861,11 +861,8 @@ class Instrument(ComplexToStringMixin, TagModelMixin, ImportMixin, InstrumentPMS
|
|
|
861
861
|
# we detect when was the last date price imported before start and switch the start date from there
|
|
862
862
|
with suppress(ObjectDoesNotExist):
|
|
863
863
|
start = self.prices.filter(date__lte=start).latest("date").date
|
|
864
|
-
yield from self._get_price_objects(start, end, clear=clear)
|
|
865
|
-
|
|
866
|
-
def import_prices(self, start: date | None = None, end: date | None = None, clear: bool = False):
|
|
867
864
|
# Import instrument prices
|
|
868
|
-
objs = list(self.
|
|
865
|
+
objs = list(self.__class__.objects.filter(id=self.id).get_instrument_prices_from_market_data(start, end))
|
|
869
866
|
self.bulk_save_instrument_prices(objs)
|
|
870
867
|
# compute daily statistics & performances
|
|
871
868
|
self.update_last_valuation_date()
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import calendar
|
|
2
|
-
import math
|
|
3
2
|
from collections import defaultdict
|
|
4
|
-
from
|
|
5
|
-
from datetime import date, timedelta
|
|
3
|
+
from datetime import date
|
|
6
4
|
from decimal import Decimal
|
|
7
|
-
from typing import Dict,
|
|
5
|
+
from typing import Dict, Optional
|
|
8
6
|
|
|
9
7
|
import numpy as np
|
|
10
8
|
import pandas as pd
|
|
11
9
|
from pandas.tseries.offsets import BDay
|
|
12
|
-
from wbcore.contrib.currency.models import CurrencyFXRates
|
|
13
10
|
|
|
14
11
|
from wbfdm.analysis.financial_analysis.financial_statistics_analysis import (
|
|
15
12
|
FinancialStatistics,
|
|
@@ -199,91 +196,6 @@ class InstrumentPMSMixin:
|
|
|
199
196
|
|
|
200
197
|
return df
|
|
201
198
|
|
|
202
|
-
def _get_price_objects(self, from_date: date, to_date: date, clear: bool = False) -> Iterable[InstrumentPrice]:
|
|
203
|
-
df = pd.DataFrame(
|
|
204
|
-
self.__class__.objects.filter(id=self.id).dl.market_data(
|
|
205
|
-
from_date=from_date
|
|
206
|
-
- timedelta(
|
|
207
|
-
days=90
|
|
208
|
-
), # we make sure to at least import the last 80 days to be sure to be able to compute the volume 50d
|
|
209
|
-
to_date=to_date,
|
|
210
|
-
values=[MarketData.CLOSE, MarketData.VOLUME, MarketData.MARKET_CAPITALIZATION],
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
if not df.empty:
|
|
214
|
-
df["calculated"] = False
|
|
215
|
-
df = df.set_index("valuation_date").sort_index()
|
|
216
|
-
|
|
217
|
-
# # if market cap is not found, maybe we have better chance on the primary exhange
|
|
218
|
-
if not df.market_capitalization.notnull().any() and self.parent and (company := self.parent.get_root()):
|
|
219
|
-
with suppress(KeyError):
|
|
220
|
-
df["market_capitalization"] = pd.DataFrame(
|
|
221
|
-
self.__class__.objects.filter(id=company.id).dl.market_data(
|
|
222
|
-
from_date=from_date,
|
|
223
|
-
to_date=to_date,
|
|
224
|
-
values=[MarketData.MARKET_CAPITALIZATION],
|
|
225
|
-
)
|
|
226
|
-
).set_index("valuation_date")["market_capitalization"]
|
|
227
|
-
|
|
228
|
-
ts = pd.date_range(df.index.min(), df.index.max(), freq="B")
|
|
229
|
-
# fill forward missing data
|
|
230
|
-
df = df.reindex(ts)
|
|
231
|
-
df[["close", "market_capitalization"]] = df[["close", "market_capitalization"]].astype(float).ffill()
|
|
232
|
-
df.volume = df.volume.astype(float).fillna(0)
|
|
233
|
-
df.calculated = df.calculated.astype(bool).fillna(
|
|
234
|
-
True
|
|
235
|
-
) # we do not ffill calculated but set the to True to mark them as "estimated"/"not real"
|
|
236
|
-
df["volume_50d"] = df["volume"].rolling(50).mean()
|
|
237
|
-
df = df[df.index.date >= from_date] # we import only from the requested from_date
|
|
238
|
-
df = df.reset_index().dropna(subset=["index", "close"])
|
|
239
|
-
df = df.replace([np.inf, -np.inf, np.nan], None)
|
|
240
|
-
|
|
241
|
-
for row in df.to_dict("records"):
|
|
242
|
-
if (_date := row.get("index")) and (close := row.get("close", None)):
|
|
243
|
-
# we make sure data does not exist 10 digits (for db constraint)
|
|
244
|
-
if int(math.log10(close)) + 1 < 10:
|
|
245
|
-
try:
|
|
246
|
-
try:
|
|
247
|
-
p = InstrumentPrice.objects.get(instrument=self, date=_date, calculated=False)
|
|
248
|
-
except InstrumentPrice.DoesNotExist:
|
|
249
|
-
p = InstrumentPrice.objects.get(instrument=self, date=_date, calculated=True)
|
|
250
|
-
|
|
251
|
-
# update only if net value is different with existing instrument price
|
|
252
|
-
if (
|
|
253
|
-
round(p.net_value, 2) != round(close, 2)
|
|
254
|
-
or (p.market_capitalization != row.get("market_capitalization"))
|
|
255
|
-
or (p.volume != row.get("volume"))
|
|
256
|
-
or (p.calculated != row.get("calculated"))
|
|
257
|
-
or clear
|
|
258
|
-
):
|
|
259
|
-
p.net_value = close
|
|
260
|
-
p.gross_value = close
|
|
261
|
-
p.calculated = row["calculated"]
|
|
262
|
-
p.volume = row.get("volume", p.volume)
|
|
263
|
-
p.volume_50d = row.get("volume_50d", p.volume_50d)
|
|
264
|
-
p.market_capitalization = row.get("market_capitalization", p.market_capitalization)
|
|
265
|
-
p.market_capitalization_consolidated = p.market_capitalization
|
|
266
|
-
p.set_dynamic_field(False)
|
|
267
|
-
p.id = None
|
|
268
|
-
yield p
|
|
269
|
-
except InstrumentPrice.DoesNotExist:
|
|
270
|
-
with suppress(CurrencyFXRates.DoesNotExist):
|
|
271
|
-
p = InstrumentPrice(
|
|
272
|
-
currency_fx_rate_to_usd=CurrencyFXRates.objects.get( # we need to get the currency rate because we bulk create the object, and thus save is not called
|
|
273
|
-
date=_date, currency=self.currency
|
|
274
|
-
),
|
|
275
|
-
instrument=self,
|
|
276
|
-
date=_date,
|
|
277
|
-
calculated=row["calculated"],
|
|
278
|
-
net_value=close,
|
|
279
|
-
gross_value=close,
|
|
280
|
-
volume=row.get("volume", None),
|
|
281
|
-
market_capitalization=row.get("market_capitalization", None),
|
|
282
|
-
volume_50d=row.get("volume_50d", None),
|
|
283
|
-
)
|
|
284
|
-
p.set_dynamic_field(False)
|
|
285
|
-
yield p
|
|
286
|
-
|
|
287
199
|
@classmethod
|
|
288
200
|
def bulk_save_instrument_prices(cls, objs):
|
|
289
201
|
InstrumentPrice.objects.bulk_create(
|
|
@@ -297,7 +209,6 @@ class InstrumentPMSMixin:
|
|
|
297
209
|
"market_capitalization",
|
|
298
210
|
"market_capitalization_consolidated",
|
|
299
211
|
"calculated",
|
|
300
|
-
"volume_50d",
|
|
301
212
|
],
|
|
302
213
|
batch_size=1000,
|
|
303
214
|
)
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from contextlib import suppress
|
|
3
|
+
from datetime import date
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
1
7
|
from django.db.models import (
|
|
2
8
|
AutoField,
|
|
3
9
|
Exists,
|
|
@@ -8,6 +14,9 @@ from django.db.models import (
|
|
|
8
14
|
Subquery,
|
|
9
15
|
)
|
|
10
16
|
from django.db.models.functions import Coalesce
|
|
17
|
+
from wbcore.contrib.currency.models import CurrencyFXRates
|
|
18
|
+
|
|
19
|
+
from wbfdm.enums import MarketData
|
|
11
20
|
|
|
12
21
|
|
|
13
22
|
class InstrumentQuerySet(QuerySet):
|
|
@@ -30,9 +39,7 @@ class InstrumentQuerySet(QuerySet):
|
|
|
30
39
|
parent=OuterRef("pk"),
|
|
31
40
|
is_primary=True,
|
|
32
41
|
is_security=True,
|
|
33
|
-
).values(
|
|
34
|
-
"id"
|
|
35
|
-
)[:1]
|
|
42
|
+
).values("id")[:1]
|
|
36
43
|
),
|
|
37
44
|
F("id"),
|
|
38
45
|
),
|
|
@@ -44,9 +51,7 @@ class InstrumentQuerySet(QuerySet):
|
|
|
44
51
|
base_qs.filter(
|
|
45
52
|
parent=OuterRef("primary_security"),
|
|
46
53
|
is_primary=True,
|
|
47
|
-
).values(
|
|
48
|
-
"id"
|
|
49
|
-
)[:1]
|
|
54
|
+
).values("id")[:1]
|
|
50
55
|
),
|
|
51
56
|
F("primary_security"),
|
|
52
57
|
),
|
|
@@ -71,3 +76,81 @@ class InstrumentQuerySet(QuerySet):
|
|
|
71
76
|
entities in the QuerySet.
|
|
72
77
|
"""
|
|
73
78
|
return self.model.dl_proxy(self)
|
|
79
|
+
|
|
80
|
+
def get_instrument_prices_from_market_data(self, from_date: date, to_date: date):
|
|
81
|
+
from wbfdm.models import InstrumentPrice
|
|
82
|
+
|
|
83
|
+
def _dict_to_object(instrument, row):
|
|
84
|
+
if (
|
|
85
|
+
(price_date := row.get("date"))
|
|
86
|
+
and (close := row.get("close", None))
|
|
87
|
+
and int(math.log10(close)) + 1 < 10
|
|
88
|
+
):
|
|
89
|
+
try:
|
|
90
|
+
try:
|
|
91
|
+
p = InstrumentPrice.objects.get(instrument=instrument, date=price_date, calculated=False)
|
|
92
|
+
except InstrumentPrice.DoesNotExist:
|
|
93
|
+
p = InstrumentPrice.objects.get(instrument=instrument, date=price_date, calculated=True)
|
|
94
|
+
p.net_value = close
|
|
95
|
+
p.gross_value = close
|
|
96
|
+
p.calculated = row["calculated"]
|
|
97
|
+
p.volume = row.get("volume", p.volume)
|
|
98
|
+
p.market_capitalization = row.get("market_capitalization", p.market_capitalization)
|
|
99
|
+
p.market_capitalization_consolidated = p.market_capitalization
|
|
100
|
+
p.set_dynamic_field(False)
|
|
101
|
+
p.id = None
|
|
102
|
+
return p
|
|
103
|
+
except InstrumentPrice.DoesNotExist:
|
|
104
|
+
with suppress(CurrencyFXRates.DoesNotExist):
|
|
105
|
+
p = InstrumentPrice(
|
|
106
|
+
currency_fx_rate_to_usd=CurrencyFXRates.objects.get(
|
|
107
|
+
# we need to get the currency rate because we bulk create the object, and thus save is not called
|
|
108
|
+
date=price_date,
|
|
109
|
+
currency=instrument.currency,
|
|
110
|
+
),
|
|
111
|
+
instrument=instrument,
|
|
112
|
+
date=price_date,
|
|
113
|
+
calculated=row["calculated"],
|
|
114
|
+
net_value=close,
|
|
115
|
+
gross_value=close,
|
|
116
|
+
volume=row.get("volume", None),
|
|
117
|
+
market_capitalization=row.get("market_capitalization", None),
|
|
118
|
+
)
|
|
119
|
+
p.set_dynamic_field(False)
|
|
120
|
+
return p
|
|
121
|
+
|
|
122
|
+
df = pd.DataFrame(
|
|
123
|
+
self.dl.market_data(
|
|
124
|
+
from_date=from_date,
|
|
125
|
+
to_date=to_date,
|
|
126
|
+
values=[MarketData.CLOSE, MarketData.VOLUME, MarketData.MARKET_CAPITALIZATION],
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
if not df.empty:
|
|
130
|
+
df["valuation_date"] = pd.to_datetime(df["valuation_date"])
|
|
131
|
+
df = df.rename(columns={"valuation_date": "date"}).set_index(["instrument_id", "date"]).sort_index()
|
|
132
|
+
df = df.drop(columns=df.columns.difference(["calculated", "close", "market_capitalization", "volume"]))
|
|
133
|
+
df["calculated"] = False
|
|
134
|
+
idx = pd.MultiIndex.from_product(
|
|
135
|
+
[
|
|
136
|
+
df.index.levels[0],
|
|
137
|
+
pd.date_range(
|
|
138
|
+
df.index.get_level_values("date").min(), df.index.get_level_values("date").max(), freq="B"
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
names=["instrument_id", "date"],
|
|
142
|
+
)
|
|
143
|
+
df = df.reindex(idx)
|
|
144
|
+
df[["close", "market_capitalization"]] = df[["close", "market_capitalization"]].astype(float).ffill()
|
|
145
|
+
df.volume = df.volume.astype(float).fillna(0)
|
|
146
|
+
df.calculated = df.calculated.astype(bool).fillna(
|
|
147
|
+
True
|
|
148
|
+
) # we do not ffill calculated but set the to True to mark them as "estimated"/"not real"
|
|
149
|
+
df = df.reset_index("date").dropna(subset=["close"])
|
|
150
|
+
df = df.replace([np.inf, -np.inf, np.nan], None)
|
|
151
|
+
|
|
152
|
+
for instrument_id, dff in df.groupby(level=0):
|
|
153
|
+
instrument = self.get(id=instrument_id)
|
|
154
|
+
yield from filter(
|
|
155
|
+
lambda x: x, map(lambda row: _dict_to_object(instrument, row), dff.to_dict("records"))
|
|
156
|
+
)
|
wbfdm/tasks.py
CHANGED
|
@@ -19,7 +19,10 @@ from .signals import investable_universe_updated
|
|
|
19
19
|
|
|
20
20
|
@shared_task(queue="portfolio")
|
|
21
21
|
def update_of_investable_universe_data(
|
|
22
|
-
start: date | None = None,
|
|
22
|
+
start: date | None = None,
|
|
23
|
+
end: date | None = None,
|
|
24
|
+
with_background_tasks: bool = True,
|
|
25
|
+
instrument_ids: list[int] | None = None,
|
|
23
26
|
):
|
|
24
27
|
"""
|
|
25
28
|
Update the investable universe data on a daily basis.
|
|
@@ -27,6 +30,8 @@ def update_of_investable_universe_data(
|
|
|
27
30
|
Parameters:
|
|
28
31
|
- start (date | None): The start date for updating data. If None, defaults to three business days before 'end'.
|
|
29
32
|
- end (date | None): The end date for updating data. If None, defaults to the current date.
|
|
33
|
+
- with_background_tasks (bool | True): If true, will trigger the post import background tasks automatically.
|
|
34
|
+
- instrument_ids (list[int] | None): if specified, narrow down the instrument queryset with this list ids
|
|
30
35
|
|
|
31
36
|
Notes:
|
|
32
37
|
- The function resets the investable universe by marking all instruments as not in the investable universe.
|
|
@@ -51,12 +56,9 @@ def update_of_investable_universe_data(
|
|
|
51
56
|
Q(is_managed=True)
|
|
52
57
|
| Q(dl_parameters__market_data__path="wbfdm.contrib.internal.dataloaders.market_data.MarketDataDataloader")
|
|
53
58
|
) # we exclude product and index managed to avoid circular import
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
prices.extend(list(instrument.get_price_objects(start=start, end=end, clear=clear)))
|
|
58
|
-
except Exception as e:
|
|
59
|
-
print(f"Connection error while processing instrument {instrument}: {str(e)}") # noqa
|
|
59
|
+
if instrument_ids:
|
|
60
|
+
instruments = instruments.filter(id__in=instrument_ids)
|
|
61
|
+
prices = list(instruments.get_instrument_prices_from_market_data(start, end))
|
|
60
62
|
Instrument.bulk_save_instrument_prices(prices)
|
|
61
63
|
investable_universe_updated.send(sender=Instrument, end_date=end)
|
|
62
64
|
if with_background_tasks:
|
|
@@ -105,5 +107,7 @@ def daily_update_instrument_price_statistics(from_date: date = None, to_date: da
|
|
|
105
107
|
p.compute_and_update_statistics()
|
|
106
108
|
objs.append(p)
|
|
107
109
|
InstrumentPrice.objects.bulk_update(
|
|
108
|
-
objs,
|
|
110
|
+
objs,
|
|
111
|
+
fields=["sharpe_ratio", "correlation", "beta", "annualized_daily_volatility", "volume_50d", "volume_200d"],
|
|
112
|
+
batch_size=1000,
|
|
109
113
|
)
|
|
@@ -157,12 +157,14 @@ class TestInstrumentModel:
|
|
|
157
157
|
"close": 1,
|
|
158
158
|
"volume": 1,
|
|
159
159
|
"market_capitalization": 1,
|
|
160
|
+
"instrument_id": instrument.id,
|
|
160
161
|
},
|
|
161
162
|
{
|
|
162
163
|
"valuation_date": to_date,
|
|
163
164
|
"close": 2,
|
|
164
165
|
"volume": 2,
|
|
165
166
|
"market_capitalization": 2,
|
|
167
|
+
"instrument_id": instrument.id,
|
|
166
168
|
},
|
|
167
169
|
]
|
|
168
170
|
instrument.import_prices(from_date, to_date + timedelta(days=1))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wbfdm
|
|
3
|
-
Version: 1.46.
|
|
3
|
+
Version: 1.46.11
|
|
4
4
|
Summary: The workbench module ensures rapid access to diverse financial data (market, fundamental, forecasts, ESG), with features for storing instruments, classifying them, and conducting financial analysis.
|
|
5
5
|
Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
|
|
6
6
|
Requires-Dist: roman==4.*
|
|
@@ -6,7 +6,7 @@ wbfdm/jinja2.py,sha256=pkIC1U-0rf6vn0DDEUzZ8dPYiTGEPY8LBTRMi9wYiuc,199
|
|
|
6
6
|
wbfdm/menu.py,sha256=c_uP0wIxboHvY0BfHbYPI9GI4F41UxSgREqbzcaxzHM,399
|
|
7
7
|
wbfdm/preferences.py,sha256=8ghDcaapOMso1kjtNfKbSFykPUTxzqI5R77gM3BgiMs,927
|
|
8
8
|
wbfdm/signals.py,sha256=PhAsFpQZF1YVe5UpedaRelUD_TVjemqRYm1HzV-bhmY,597
|
|
9
|
-
wbfdm/tasks.py,sha256=
|
|
9
|
+
wbfdm/tasks.py,sha256=55yXOyVe6-TeJRHAElGzt-VCw3a_23liQP8B8S2PFRM,4359
|
|
10
10
|
wbfdm/urls.py,sha256=pDp9I0kktxicad8sXXEUT7402jZPMJNcE5R1doTlcMw,8887
|
|
11
11
|
wbfdm/utils.py,sha256=4cWrCpqXxHIjtSlt4DDPFvmtaqXw_H0nqhM6sGuXx0o,1938
|
|
12
12
|
wbfdm/admin/__init__.py,sha256=Z1VtH_gjD71K79KcD-2Q2Lu_p_7j0akMZj7gNxdz1CQ,1398
|
|
@@ -125,7 +125,7 @@ wbfdm/factories/classifications.py,sha256=GJ0eARFTsj5dnKUsUYbLSIZLzTCy7RWhy7_f8D
|
|
|
125
125
|
wbfdm/factories/controversies.py,sha256=GhuoEms1O7DIMVNAIbFEzD9DRv8j1MXIv0u1JK6Pi-o,929
|
|
126
126
|
wbfdm/factories/exchanges.py,sha256=heJHQE59dCDFeDuScJJti4C_SsMsz3O0kmczpGYVNlQ,831
|
|
127
127
|
wbfdm/factories/instrument_list.py,sha256=ypnrTLCl0XRfGj6y-3XJSQ2Wl5TULxZU0I3nF6svH3Y,743
|
|
128
|
-
wbfdm/factories/instrument_prices.py,sha256=
|
|
128
|
+
wbfdm/factories/instrument_prices.py,sha256=EjRFbMjP3pLrxoNsNqNo37FGjayIiV99bkQPVgZLj4I,3623
|
|
129
129
|
wbfdm/factories/instruments.py,sha256=vM6VymnO0bIecEk-reT8l2TO8O5uHwPYnqVnPREXRKQ,2238
|
|
130
130
|
wbfdm/factories/instruments_relationships.py,sha256=opGQMM3sHQV5F04nGPCCsRw8ux8vSQ78tHNJjIDPyUE,1138
|
|
131
131
|
wbfdm/factories/options.py,sha256=nna8LgB_2-XNGm37--Edkdv1oc49oeKtr7f8tcIJPU4,2463
|
|
@@ -227,20 +227,20 @@ wbfdm/models/exchanges/exchanges.py,sha256=j-C22QPZEg-Mb6KCer7RRMVOcP1mprTc380zF
|
|
|
227
227
|
wbfdm/models/instruments/__init__.py,sha256=OvEkECJaCubBQC7B9yUrx15V982labvegeGXyEASVno,636
|
|
228
228
|
wbfdm/models/instruments/classifications.py,sha256=EeZ_P8f1F1NfjUmLf9fDMF0iPM73qxQoArUfvjuCwHg,10906
|
|
229
229
|
wbfdm/models/instruments/instrument_lists.py,sha256=GxfFyfYxEcJS36LAarHja49TOM8ffhIivpZX2-tPtZg,4234
|
|
230
|
-
wbfdm/models/instruments/instrument_prices.py,sha256=
|
|
230
|
+
wbfdm/models/instruments/instrument_prices.py,sha256=WCaFMZ9sF7xeGTp9jkq7TA8RfYHrwvTjujzqgCKj9SI,22070
|
|
231
231
|
wbfdm/models/instruments/instrument_relationships.py,sha256=zpCZCnt5CqIg5bd6le_6TyirsSwGV2NaqTVKw3bd5vM,10660
|
|
232
232
|
wbfdm/models/instruments/instrument_requests.py,sha256=wC07CEqQxMGfrnATyr56T6GWxXknai105G2WIw1i3xk,7767
|
|
233
|
-
wbfdm/models/instruments/instruments.py,sha256=
|
|
233
|
+
wbfdm/models/instruments/instruments.py,sha256=V0bBgDxeEXtuu5uUBd15g4FIPAboAC53WN-FMt35qxg,39475
|
|
234
234
|
wbfdm/models/instruments/options.py,sha256=hFprq7B5t4ctz8nVqzFsBEzftq_KDUSsSXl1zJyh7tE,7094
|
|
235
235
|
wbfdm/models/instruments/private_equities.py,sha256=uzwZi8IkmCKAHVTxnuFya9tehx7kh57sTlTEi1ieDaM,2198
|
|
236
|
-
wbfdm/models/instruments/querysets.py,sha256=
|
|
236
|
+
wbfdm/models/instruments/querysets.py,sha256=7-hbYxfLAZQJO-e76SmYx6NjZ8q0tdR-4pAZyb-izas,6556
|
|
237
237
|
wbfdm/models/instruments/utils.py,sha256=88jnWINSSC0OwH-mCEOPLZXuhBCtEsxBpSaZ38GteaE,1365
|
|
238
238
|
wbfdm/models/instruments/llm/__init__.py,sha256=dSmxRmEWb0A4O_lUoWuRKt2mBtUuLCTPVVJqGyi_n40,52
|
|
239
239
|
wbfdm/models/instruments/llm/create_instrument_news_relationships.py,sha256=Eza39rlkNJxpURroIsJLImKC6F-KtTmkdjHn1kv4F3Q,3439
|
|
240
240
|
wbfdm/models/instruments/mixin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
241
241
|
wbfdm/models/instruments/mixin/financials_computed.py,sha256=L5wjXDsR0maiwfOKP6KyWNJNH4nGOoAjSc_hDM7fsj0,35105
|
|
242
242
|
wbfdm/models/instruments/mixin/financials_serializer_fields.py,sha256=Au3P9LqByh5LGyv8w_miJamtHiEerOGZQhM1NzrfECM,68768
|
|
243
|
-
wbfdm/models/instruments/mixin/instruments.py,sha256=
|
|
243
|
+
wbfdm/models/instruments/mixin/instruments.py,sha256=W9v29cjST-5MAMMuXVnlH4ItdRTdoA_vMCkLUhQgW2s,9888
|
|
244
244
|
wbfdm/serializers/__init__.py,sha256=AXb03RKHo6B0ts_IQOvx89n9wKG8pxiumYv9cr4EhvA,197
|
|
245
245
|
wbfdm/serializers/esg.py,sha256=epoX8cjkPuVv45aW-Jf85-rSO_ZrSnXcTxMcadKdC-I,1086
|
|
246
246
|
wbfdm/serializers/exchanges.py,sha256=vi5kFadYRQo-6CV2b56nrABpt80LPtjZumJVaBE4bcc,1175
|
|
@@ -272,7 +272,7 @@ wbfdm/tests/models/test_classifications.py,sha256=f2aM9UyV54fkEncp-uewEdOc3_0D-i
|
|
|
272
272
|
wbfdm/tests/models/test_exchanges.py,sha256=KwK278MpA3FkpVgjW2l2PIHL7e8uDur7dOzIaTQEwyw,138
|
|
273
273
|
wbfdm/tests/models/test_instrument_list.py,sha256=UIxKgBd4U-T2I4WDZfwacgJ1nKwJWYX1HKhdQDpx1tA,4899
|
|
274
274
|
wbfdm/tests/models/test_instrument_prices.py,sha256=ObqFbJxZveiOPAK9_kC_JO9VBNmZB6bvGM4BejsFJ3c,16633
|
|
275
|
-
wbfdm/tests/models/test_instruments.py,sha256=
|
|
275
|
+
wbfdm/tests/models/test_instruments.py,sha256=DCl73yP2n6hIBZWtXGr2H5UvYbVO_5iGN5lwGo3bgtk,9544
|
|
276
276
|
wbfdm/tests/models/test_merge.py,sha256=tXD5xIxZyZtXpm9WIQ4Yc8TQwsUnkxkKIvMNwaHOvgM,4632
|
|
277
277
|
wbfdm/tests/models/test_options.py,sha256=DoEAHhNQE4kucpBRRm2S05ozabkERz-I4mUolsE2Qi8,2269
|
|
278
278
|
wbfdm/viewsets/__init__.py,sha256=ZM29X0-5AkSH2rzF_gKUI4FoaEWCcMTXPjruJ4Gi_x8,383
|
|
@@ -351,6 +351,6 @@ wbfdm/viewsets/statements/__init__.py,sha256=odxtFYUDICPmz8WCE3nx93EvKZLSPBEI4d7
|
|
|
351
351
|
wbfdm/viewsets/statements/statements.py,sha256=kmtM0uZ3f7eJJe5gVmd-iVra9dHwTB9x12p7f5qTEx8,4084
|
|
352
352
|
wbfdm/viewsets/technical_analysis/__init__.py,sha256=qtCIBg0uSiZeJq_1tEQFilnorMBkMe6uCMfqar6-cLE,77
|
|
353
353
|
wbfdm/viewsets/technical_analysis/monthly_performances.py,sha256=O1j8CGfOranL74LqVvcf7jERaDIboEJZiBf_AbbVDQ8,3974
|
|
354
|
-
wbfdm-1.46.
|
|
355
|
-
wbfdm-1.46.
|
|
356
|
-
wbfdm-1.46.
|
|
354
|
+
wbfdm-1.46.11.dist-info/METADATA,sha256=APQKFb1E4ZdhaxdSeTIdrizO5INV05Su8CRUTH7zBcM,738
|
|
355
|
+
wbfdm-1.46.11.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
356
|
+
wbfdm-1.46.11.dist-info/RECORD,,
|
|
File without changes
|