wbfdm 1.54.5__py2.py3-none-any.whl → 1.54.7__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.

@@ -2,11 +2,14 @@ from datetime import date
2
2
  from decimal import Decimal
3
3
  from typing import Iterator
4
4
 
5
+ from django.db.models import Value
6
+ from django.db.models.functions import Coalesce
7
+ from wbcore.contrib.currency.models import Currency, CurrencyFXRates
5
8
  from wbcore.contrib.dataloader.dataloaders import Dataloader
6
9
 
7
10
  from wbfdm.dataloaders.protocols import MarketDataProtocol
8
11
  from wbfdm.dataloaders.types import MarketDataDict
9
- from wbfdm.enums import MarketData
12
+ from wbfdm.enums import Frequency, MarketData
10
13
  from wbfdm.models.instruments.instrument_prices import InstrumentPrice
11
14
 
12
15
  MarketDataMap = {
@@ -33,11 +36,13 @@ def _cast_decimal_to_float(value: float | Decimal) -> float:
33
36
  class MarketDataDataloader(MarketDataProtocol, Dataloader):
34
37
  def market_data(
35
38
  self,
36
- values: list[MarketData] | None = [MarketData.CLOSE],
39
+ values: list[MarketData] = [MarketData.CLOSE],
37
40
  from_date: date | None = None,
38
41
  to_date: date | None = None,
39
42
  exact_date: date | None = None,
40
- calculated: bool | None = None,
43
+ frequency: Frequency = Frequency.DAILY,
44
+ target_currency: str | None = None,
45
+ apply_fx_rate: bool = True,
41
46
  **kwargs,
42
47
  ) -> Iterator[MarketDataDict]:
43
48
  """Get prices for instruments.
@@ -51,7 +56,27 @@ class MarketDataDataloader(MarketDataProtocol, Dataloader):
51
56
  Returns:
52
57
  Iterator[MarketDataDict]: An iterator of dictionaries conforming to the DailyValuationDict.
53
58
  """
54
- prices = InstrumentPrice.objects.filter(instrument__in=self.entities).annotate_market_data() # type: ignore
59
+ try:
60
+ target_currency = Currency.objects.get(key=target_currency)
61
+ except Currency.DoesNotExist:
62
+ target_currency = None
63
+ fx_rate = (
64
+ Coalesce(
65
+ CurrencyFXRates.get_fx_rates_subquery_for_two_currencies(
66
+ "date", "instrument__currency", target_currency
67
+ ),
68
+ Value(Decimal("1")),
69
+ )
70
+ if target_currency
71
+ else Value(Decimal("1"))
72
+ )
73
+
74
+ prices = (
75
+ InstrumentPrice.objects.filter(instrument__in=self.entities)
76
+ .annotate_market_data()
77
+ .annotate(fx_rate=fx_rate)
78
+ ) # type: ignore
79
+ calculated = kwargs.get("calculated", None)
55
80
  if calculated is not None:
56
81
  prices = prices.filter(calculated=calculated)
57
82
  else:
@@ -72,11 +97,18 @@ class MarketDataDataloader(MarketDataProtocol, Dataloader):
72
97
  "date",
73
98
  "instrument",
74
99
  "calculated",
100
+ "fx_rate",
75
101
  *set(values_map.values()),
76
102
  ):
77
103
  external_id = row.pop("instrument")
78
104
  val_date = row.pop("date")
79
105
  if row:
106
+ fx_rate = row["fx_rate"]
107
+ if apply_fx_rate and row.get("net_value"):
108
+ row["net_value"] = row["net_value"] * fx_rate
109
+ if apply_fx_rate and row.get("market_capitalization"):
110
+ row["market_capitalization"] = row["market_capitalization"] * float(fx_rate)
111
+
80
112
  yield MarketDataDict(
81
113
  id=f"{external_id}_{val_date}",
82
114
  valuation_date=val_date,
@@ -84,5 +116,6 @@ class MarketDataDataloader(MarketDataProtocol, Dataloader):
84
116
  external_id=external_id,
85
117
  source="wbfdm",
86
118
  calculated=row["calculated"],
119
+ fx_rate=_cast_decimal_to_float(fx_rate),
87
120
  **{MarketData[k].value: _cast_decimal_to_float(row[v]) for k, v in values_map.items()},
88
121
  )
@@ -45,6 +45,7 @@ class DatastreamMarketDataDataloader(MarketDataProtocol, Dataloader):
45
45
  exact_date: date | None = None,
46
46
  frequency: Frequency = Frequency.DAILY,
47
47
  target_currency: str | None = None,
48
+ apply_fx_rate: bool = True,
48
49
  **kwargs,
49
50
  ) -> Iterator[MarketDataDict]:
50
51
  """Get market data for instruments.
@@ -97,7 +98,7 @@ class DatastreamMarketDataDataloader(MarketDataProtocol, Dataloader):
97
98
  fx_code = pk.Table("DS2FxCode")
98
99
  fx_rate = pk.Table("DS2FxRate")
99
100
  query = (
100
- query
101
+ query.select((1 / fx_rate.midrate).as_("fx_rate"))
101
102
  # Join FX code table matching currencies and ensuring SPOT rate type
102
103
  .left_join(fx_code)
103
104
  .on(
@@ -127,7 +128,7 @@ class DatastreamMarketDataDataloader(MarketDataProtocol, Dataloader):
127
128
  )
128
129
 
129
130
  value = pricing_2.Close_
130
- if fx_rate:
131
+ if fx_rate and apply_fx_rate:
131
132
  value /= Case().when(pricing_2.Currency == target_currency, 1).else_(fx_rate.midrate)
132
133
 
133
134
  query = query.select(value.as_("undadjusted_close"))
@@ -145,7 +146,7 @@ class DatastreamMarketDataDataloader(MarketDataProtocol, Dataloader):
145
146
  ):
146
147
  ds2_value = DS2MarketData[market_data.name].value
147
148
  value = getattr(pricing, ds2_value)
148
- if fx_rate and market_data is not MarketData.SHARES_OUTSTANDING:
149
+ if fx_rate and apply_fx_rate and market_data is not MarketData.SHARES_OUTSTANDING:
149
150
  value /= Case().when(pricing.Currency == target_currency, 1).else_(fx_rate.midrate)
150
151
 
151
152
  query = query.select(value.as_(market_data.value))
@@ -45,6 +45,7 @@ class MarketDataProtocol(Protocol):
45
45
  exact_date: date | None = None,
46
46
  frequency: Frequency = Frequency.DAILY,
47
47
  target_currency: str | None = None,
48
+ apply_fx_rate: bool = True,
48
49
  ) -> Iterator[MarketDataDict]: ...
49
50
 
50
51
 
@@ -80,6 +80,7 @@ class InstrumentDataloaderProxy(
80
80
  exact_date: date | None = None,
81
81
  frequency: Frequency = Frequency.DAILY,
82
82
  target_currency: str | None = None,
83
+ apply_fx_rate: bool = True,
83
84
  ) -> Iterator[MarketDataDict]:
84
85
  if not values:
85
86
  values = list(MarketData)
@@ -93,6 +94,7 @@ class InstrumentDataloaderProxy(
93
94
  exact_date=exact_date,
94
95
  frequency=frequency,
95
96
  target_currency=target_currency,
97
+ apply_fx_rate=apply_fx_rate,
96
98
  ),
97
99
  )
98
100
 
@@ -65,7 +65,7 @@ class MarketDataDict(BaseDict):
65
65
  volume: NotRequired[float]
66
66
  market_cap: NotRequired[float]
67
67
  calculated: NotRequired[bool]
68
-
68
+ fx_rate: NotRequired[float]
69
69
  unadjusted_close: NotRequired[float]
70
70
  unadjusted_outstanding_shares: NotRequired[float | int]
71
71
 
@@ -42,7 +42,7 @@ class InstrumentPMSMixin:
42
42
  def get_price(self, val_date: date, price_date_timedelta: int = 3) -> Decimal:
43
43
  if self.is_cash:
44
44
  return Decimal(1)
45
- return self._build_dto(val_date, price_date_timedelta=price_date_timedelta).close
45
+ return Decimal(self._build_dto(val_date, price_date_timedelta=price_date_timedelta).close)
46
46
 
47
47
  def _build_dto(self, val_date: date, price_date_timedelta: int = 3) -> PriceDTO: # for backward compatibility
48
48
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbfdm
3
- Version: 1.54.5
3
+ Version: 1.54.7
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.*
@@ -41,7 +41,7 @@ wbfdm/contrib/dsws/client.py,sha256=C598w5P85kvX7_gR0Yf-WHRLj77HqztUhofBMl3JWAE,
41
41
  wbfdm/contrib/dsws/dataloaders/market_data.py,sha256=7Vj64nLqJAA3BiNeJPr3uLnnZ5kFOY2XGyAAJRL7vOk,6097
42
42
  wbfdm/contrib/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  wbfdm/contrib/internal/dataloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- wbfdm/contrib/internal/dataloaders/market_data.py,sha256=aoWSMamtnMbejYZrHzr37dQCG40nuA3fNBQNOyqhceQ,3260
44
+ wbfdm/contrib/internal/dataloaders/market_data.py,sha256=FA7_f_20Gx1EjWOK9HDcWfX_gbBpOYXaxMR_O5T-K-Q,4542
45
45
  wbfdm/contrib/metric/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  wbfdm/contrib/metric/apps.py,sha256=T-Na_lyFeLR8ebGvaW-mcjKFLqiZcsPXnUlOuVPOiWw,297
47
47
  wbfdm/contrib/metric/decorators.py,sha256=VPmyjEJaq5HqXOt_ZizqcXfRqSEXw4lanDmubUjb2GY,476
@@ -100,7 +100,7 @@ wbfdm/contrib/qa/dataloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
100
100
  wbfdm/contrib/qa/dataloaders/adjustments.py,sha256=DQEexOLA7WyBB1dZJHQd-6zbzyEIURgSSgS7bJRvXzQ,2980
101
101
  wbfdm/contrib/qa/dataloaders/corporate_actions.py,sha256=lWT6klrTXKqxiko2HGrxHH8E2C00FJS-AOX3IhglRrI,2912
102
102
  wbfdm/contrib/qa/dataloaders/financials.py,sha256=xUHpvhUkvmdPL_RyWCrs7XgChgTklX5qemmaXMedgkY,3475
103
- wbfdm/contrib/qa/dataloaders/market_data.py,sha256=Wp-LcQkytDCXQWywWkilhjgpIiT29U6_AUQRy3-pY6A,8104
103
+ wbfdm/contrib/qa/dataloaders/market_data.py,sha256=0LjTN04WM2i9rrJ_M3M04cY0UbxMElsWiaqOC75xpHQ,8221
104
104
  wbfdm/contrib/qa/dataloaders/officers.py,sha256=vytlQJJxmn4Y5HfNh5mHJAvuIrrsQSkNO-sONyhxftY,2940
105
105
  wbfdm/contrib/qa/dataloaders/reporting_dates.py,sha256=q25ccB0pbGfLJLV1A1_AY1XYWJ_Fa10egY09L1J-C5A,2628
106
106
  wbfdm/contrib/qa/dataloaders/statements.py,sha256=hC6YErJcvBTmaAmzscgeC4sBK3lYE2U5eIKRIE9b_cs,10094
@@ -118,9 +118,9 @@ wbfdm/contrib/qa/sync/instruments.py,sha256=8aTQVJ_cw1phe4FWikn79pjCfUijaTcwkdhQ
118
118
  wbfdm/contrib/qa/sync/utils.py,sha256=LUnjNR28moT92cjP04SVCRQ_Ssp6SaO9kehgWV4zvOs,11782
119
119
  wbfdm/dataloaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  wbfdm/dataloaders/cache.py,sha256=K9BeVxT7p-BMvjurINt18bfrUDccp840uIjfDBLJRNk,4841
121
- wbfdm/dataloaders/protocols.py,sha256=xFbexmhh38MWmkWsOSsfcNxdtrrWfO02qNihsqwA_BQ,2987
122
- wbfdm/dataloaders/proxies.py,sha256=3Wt-_bj98Ykf9uiJSznNqCUbRQYdR8EvqQ73tPuBfD8,7072
123
- wbfdm/dataloaders/types.py,sha256=wrV0PrKLkLog-rCjtcGFkQuzdeY1w-JGfAetq-mKqFs,5565
121
+ wbfdm/dataloaders/protocols.py,sha256=1vornH4tCIsd7HLL3lCiE0ni6YQEI6fF6Em_I-AUSFc,3023
122
+ wbfdm/dataloaders/proxies.py,sha256=gA5QFXgbC78tQpmypow1dJ5k7UyqFGXDEyeS-xpUGEw,7157
123
+ wbfdm/dataloaders/types.py,sha256=8PNHfBQj4C0xVn6cJxKQV3oBFxkTW3MGn4dMMRKVGJ4,5596
124
124
  wbfdm/factories/__init__.py,sha256=yYxAKBde_ksIr-3g4RjL6d5Wu-nmsuEDdYNyJpgfpQU,660
125
125
  wbfdm/factories/classifications.py,sha256=GJ0eARFTsj5dnKUsUYbLSIZLzTCy7RWhy7_f8Dn6IMQ,1827
126
126
  wbfdm/factories/controversies.py,sha256=GhuoEms1O7DIMVNAIbFEzD9DRv8j1MXIv0u1JK6Pi-o,929
@@ -248,7 +248,7 @@ wbfdm/models/instruments/llm/create_instrument_news_relationships.py,sha256=f9MT
248
248
  wbfdm/models/instruments/mixin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
249
  wbfdm/models/instruments/mixin/financials_computed.py,sha256=L5wjXDsR0maiwfOKP6KyWNJNH4nGOoAjSc_hDM7fsj0,35105
250
250
  wbfdm/models/instruments/mixin/financials_serializer_fields.py,sha256=-OkpcUt1rZmB3nUcO2vckpJdVm8IxRqkPDEgcPqqoRU,68886
251
- wbfdm/models/instruments/mixin/instruments.py,sha256=BNcI1pnGQFjHHTx2qqzYnbfcJ_dq84OzO5g_uxxDOVI,10013
251
+ wbfdm/models/instruments/mixin/instruments.py,sha256=fpt03q_Sq35R_ZmJpdcw81aA7wTocnp7ANE1jb-_cfI,10022
252
252
  wbfdm/serializers/__init__.py,sha256=AXb03RKHo6B0ts_IQOvx89n9wKG8pxiumYv9cr4EhvA,197
253
253
  wbfdm/serializers/esg.py,sha256=epoX8cjkPuVv45aW-Jf85-rSO_ZrSnXcTxMcadKdC-I,1086
254
254
  wbfdm/serializers/exchanges.py,sha256=wYvy0XBS9tRFHqin23gABQ_pj3Rmsx1B075SZ5GqwDo,1211
@@ -359,6 +359,6 @@ wbfdm/viewsets/statements/__init__.py,sha256=odxtFYUDICPmz8WCE3nx93EvKZLSPBEI4d7
359
359
  wbfdm/viewsets/statements/statements.py,sha256=gA6RCI8-B__JwjEb6OZxpn8Y-9aF-YQ3HIQ7e1vfJMw,4304
360
360
  wbfdm/viewsets/technical_analysis/__init__.py,sha256=qtCIBg0uSiZeJq_1tEQFilnorMBkMe6uCMfqar6-cLE,77
361
361
  wbfdm/viewsets/technical_analysis/monthly_performances.py,sha256=O1j8CGfOranL74LqVvcf7jERaDIboEJZiBf_AbbVDQ8,3974
362
- wbfdm-1.54.5.dist-info/METADATA,sha256=f2DXjGzPPVLrC04yFs9BiudySSdeC8UCr9BAJPRscQ4,768
363
- wbfdm-1.54.5.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
364
- wbfdm-1.54.5.dist-info/RECORD,,
362
+ wbfdm-1.54.7.dist-info/METADATA,sha256=aa803KkLY9b9cQMX2W5Hn4jWv1apafyFnkqOeDBcCzQ,768
363
+ wbfdm-1.54.7.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
364
+ wbfdm-1.54.7.dist-info/RECORD,,
File without changes