bw-essentials-core 0.0.8__py3-none-any.whl → 0.1.0__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 bw-essentials-core might be problematic. Click here for more details.

@@ -17,3 +17,4 @@ class Services(Enum):
17
17
  USER_REPORTING = 'User_Reporting'
18
18
  PAYMENT = 'Payment'
19
19
  MODEL_PORTFOLIO = "Model_Portfolio"
20
+ PROMETHEUS_USER_APP = "Prometheus_User_App"
@@ -67,7 +67,8 @@ class MarketPricer(ApiClient):
67
67
  self.name = Services.MARKET_PRICER.value
68
68
  self.urls = {
69
69
  "live": "live",
70
- "eod": "eod"
70
+ "eod": "eod",
71
+ "index_performance": "index/{}/performance",
71
72
  }
72
73
 
73
74
  def get_live_prices(self, securities, exchange):
@@ -231,3 +232,71 @@ class MarketPricer(ApiClient):
231
232
 
232
233
  logger.info(f"{market_pricing_eod_response=}")
233
234
  return market_pricing_eod_response.get("data")
235
+
236
+ def get_index_performance_by_dates(self, index_name, start_date, end_date):
237
+ """
238
+ Fetches index performance metrics over a given date range.
239
+
240
+ Args:
241
+ index_name (str): The index name (e.g., "NIFTY50").
242
+ start_date (str): The start date for performance metrics in 'YYYY-MM-DD' format.
243
+ end_date (str): The end date for performance metrics in 'YYYY-MM-DD' format.
244
+
245
+ Returns:
246
+ dict: A dictionary containing cumulative return, volatility, drawdown, and a list
247
+ of daily return data.
248
+ Example:
249
+ {
250
+ "data": {
251
+ "cumulative_return": 2.332968692796598,
252
+ "volatility": 6.378354401960588,
253
+ "drawdown": 0,
254
+ "daily_returns": [
255
+ {
256
+ "date": "2025-06-05",
257
+ "close": 22934.5,
258
+ "daily_return": 0.5552912703301827,
259
+ "current_weight": 100.55529127033019
260
+ },
261
+ {
262
+ "date": "2025-06-06",
263
+ "close": 23165.1,
264
+ "daily_return": 1.0054721053434745,
265
+ "current_weight": 101.56634667450024
266
+ },
267
+ {
268
+ "date": "2025-06-09",
269
+ "close": 23329.4,
270
+ "daily_return": 0.7092565972087517,
271
+ "current_weight": 102.28671268883305
272
+ },
273
+ {
274
+ "date": "2025-06-10",
275
+ "close": 23339.95,
276
+ "daily_return": 0.04522190883606836,
277
+ "current_weight": 102.3329686927966
278
+ }
279
+ ]
280
+ },
281
+ "success": true
282
+ }
283
+ Raises:
284
+ ValueError: If input parameters are missing or invalid.
285
+ """
286
+ logger.info(f"In - get_index_performance_by_dates | {index_name=}, {start_date=}, {end_date=}")
287
+
288
+ if not all([index_name, start_date, end_date]):
289
+ logger.error("Missing required parameters for index performance fetch")
290
+ raise ValueError("index_name, start_date, and end_date are required")
291
+
292
+ endpoint = self.urls['index_performance'].format(index_name)
293
+ params = {"start_date": start_date, "end_date": end_date}
294
+ response = self._get(
295
+ url=self.base_url,
296
+ endpoint=endpoint,
297
+ params=params
298
+ )
299
+ logger.debug(f"Raw response from MarketPricer index API: {response}")
300
+ data = response.get("data")
301
+ logger.info("Out - get_index_performance_by_dates success")
302
+ return data
@@ -0,0 +1,323 @@
1
+ """
2
+ prometheus_user_app.py
3
+
4
+ Module to interact with the Prometheus User App Service API.
5
+
6
+ This module provides a high-level client wrapper around the Prometheus User App Service,
7
+ enabling secure, structured, and logged communication with its endpoints. It includes:
8
+
9
+ - Type-safe interfaces
10
+ - Detailed request and response logging
11
+ - Validated endpoint routing
12
+ - Clear, maintainable service integration points
13
+ """
14
+
15
+ from typing import Optional, Dict, Any
16
+ import logging
17
+
18
+ from bw_essentials.constants.services import Services
19
+ from bw_essentials.services.api_client import ApiClient
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class PrometheusUserApp(ApiClient):
25
+ """
26
+ Client for interacting with the Prometheus User App Service API.
27
+
28
+ This class handles API communication with Prometheus User App endpoints. It abstracts
29
+ request handling, URL construction, and logging while ensuring robust error management.
30
+
31
+ Attributes:
32
+ base_url (str): Base URL derived from tenant configuration
33
+ name (str): Service identifier
34
+ urls (Dict[str, str]): A dictionary of endpoint path templates
35
+ """
36
+
37
+ def __init__(self, service_user: str):
38
+ """
39
+ Initialize the PrometheusUserApp client with the given service user.
40
+
41
+ Args:
42
+ service_user (str): Username or service identifier for authentication.
43
+ """
44
+ super().__init__(user=service_user)
45
+ self.base_url = self.get_base_url(Services.PROMETHEUS_USER_APP.value)
46
+ self.name = Services.PROMETHEUS_USER_APP.value
47
+ self.urls = {
48
+ "user_details": "user/details",
49
+ "profile": "user/profile",
50
+ "get_profile": "user/profile/",
51
+ "login": "user/login",
52
+ "register_user": "user/register",
53
+ "update_dealer_disclaimer": "user/{}/dealer/disclaimer/",
54
+ "dealer_users": "user/{}/dealer/users",
55
+ "user_disclaimer": "user/{}/disclaimer/",
56
+ "dealer_login": "user/{}/login/dealer",
57
+ "send_otp": "user/{}/otp/send",
58
+ "verify_otp": "user/{}/otp/verify",
59
+ "register_dealer_user": "user/{}/register/dealer",
60
+ "sso": "user/{}/sso"
61
+ }
62
+
63
+ def get_user_details(self, user_id: str) -> Optional[Dict[str, Any]]:
64
+ """
65
+ Fetch detailed user information by user ID.
66
+
67
+ Args:
68
+ user_id (str): Unique identifier of the user.
69
+
70
+ Returns:
71
+ Optional[Dict[str, Any]]: Parsed user data if found, else None.
72
+ """
73
+ logger.info("Fetching user details for user_id=%s", user_id)
74
+ response = self._get(
75
+ endpoint=self.urls["user_details"],
76
+ url=self.base_url,
77
+ params={'user_id': user_id}
78
+ )
79
+ return response.get('data')
80
+
81
+ def update_user_profile(self, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
82
+ """
83
+ Update the user profile with provided data.
84
+
85
+ Args:
86
+ data (Dict[str, Any]): Dictionary containing user profile updates.
87
+
88
+ Returns:
89
+ Optional[Dict[str, Any]]: Updated profile data if successful, else None.
90
+ """
91
+ logger.info("Updating user profile with data: %s", data)
92
+ response = self._put(
93
+ endpoint=self.urls["profile"],
94
+ url=self.base_url,
95
+ data=data
96
+ )
97
+ return response.get('data')
98
+
99
+ def login(self, user_id: str, password: str) -> Optional[Dict[str, Any]]:
100
+ """
101
+ Authenticate a user using credentials.
102
+
103
+ Args:
104
+ user_id (str): User identifier (e.g., username).
105
+ password (str): User password.
106
+
107
+ Returns:
108
+ Optional[Dict[str, Any]]: Auth token or login response if successful.
109
+ """
110
+ logger.info("Logging in user %s", user_id)
111
+ response = self._post(
112
+ endpoint=self.urls["login"],
113
+ url=self.base_url,
114
+ json={'username': user_id, 'password': password}
115
+ )
116
+ return response.get('data')
117
+
118
+ def get_user_profile(self, user_id: str) -> Optional[Dict[str, Any]]:
119
+ """
120
+ Retrieve profile details of a user.
121
+
122
+ Args:
123
+ user_id (str): Unique user identifier.
124
+
125
+ Returns:
126
+ Optional[Dict[str, Any]]: User profile information if successful.
127
+ """
128
+ logger.info("Fetching profile for user %s", user_id)
129
+ response = self._get(
130
+ endpoint=self.urls["get_profile"],
131
+ url=self.base_url
132
+ )
133
+ return response.get('data')
134
+
135
+ def register_user(self, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
136
+ """
137
+ Register a new user with the service.
138
+
139
+ Args:
140
+ data (Dict[str, Any]): Registration details including user information.
141
+
142
+ Returns:
143
+ Optional[Dict[str, Any]]: Registration confirmation or created user details.
144
+ """
145
+ logger.info("Registering new user with data: %s", data)
146
+ response = self._post(
147
+ endpoint=self.urls["register_user"],
148
+ url=self.base_url,
149
+ json=data
150
+ )
151
+ return response
152
+
153
+ def update_dealer_disclaimer(self, broker: str, user_id: str, disclaimer_accepted: bool) -> Optional[Dict[str, Any]]:
154
+ """
155
+ Update a user's dealer disclaimer status.
156
+
157
+ Args:
158
+ broker (str): Broker identifier.
159
+ user_id (str): User ID whose disclaimer is being updated.
160
+ disclaimer_accepted (bool): Whether disclaimer is accepted.
161
+
162
+ Returns:
163
+ Optional[Dict[str, Any]]: Updated disclaimer status if successful.
164
+ """
165
+ logger.info("Updating dealer disclaimer for user_id=%s, broker=%s", user_id, broker)
166
+ response = self._put(
167
+ endpoint=self.urls["update_dealer_disclaimer"].format(broker),
168
+ url=self.base_url,
169
+ json={'disclaimer_accepted': disclaimer_accepted, 'user_id': user_id}
170
+ )
171
+ return response.get('data')
172
+
173
+ def dealer_users(self, broker: str, dealer_id: str) -> Optional[Dict[str, Any]]:
174
+ """
175
+ Get users associated with a specific dealer.
176
+
177
+ Args:
178
+ broker (str): Broker name.
179
+ dealer_id (str): Dealer identifier.
180
+
181
+ Returns:
182
+ Optional[Dict[str, Any]]: List of users or None if failed.
183
+ """
184
+ logger.info("Fetching dealer users for broker=%s, dealer_id=%s", broker, dealer_id)
185
+ response = self._post(
186
+ endpoint=self.urls["dealer_users"].format(broker),
187
+ url=self.base_url,
188
+ json={'dealer_id': dealer_id}
189
+ )
190
+ return response.get('data')
191
+
192
+ def user_disclaimer(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
193
+ """
194
+ Fetch disclaimer details for a user under a specific broker.
195
+
196
+ Args:
197
+ broker (str): Broker name.
198
+ data (Dict[str, Any]): Payload for disclaimer lookup.
199
+
200
+ Returns:
201
+ Optional[Dict[str, Any]]: Disclaimer information if available.
202
+ """
203
+ logger.info("Fetching user disclaimer for broker=%s, data=%s", broker, data)
204
+ response = self._post(
205
+ endpoint=self.urls["user_disclaimer"].format(broker),
206
+ url=self.base_url,
207
+ json=data
208
+ )
209
+ return response.get('data')
210
+
211
+ def dealer_login(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
212
+ """
213
+ Perform login for a dealer under the specified broker.
214
+
215
+ Args:
216
+ broker (str): Broker identifier.
217
+ data (Dict[str, Any]): Login payload.
218
+
219
+ Returns:
220
+ Optional[Dict[str, Any]]: Login response if successful.
221
+ """
222
+ logger.info("Dealer login request for broker=%s, data=%s", broker, data)
223
+ response = self._post(
224
+ endpoint=self.urls["dealer_login"].format(broker),
225
+ url=self.base_url,
226
+ json=data
227
+ )
228
+ return response.get('data')
229
+
230
+ def send_otp(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
231
+ """
232
+ Send OTP to user for verification under a broker.
233
+
234
+ Args:
235
+ broker (str): Broker name.
236
+ data (Dict[str, Any]): Payload including mobile/email.
237
+
238
+ Returns:
239
+ Optional[Dict[str, Any]]: Response with OTP status.
240
+ """
241
+ logger.info("Sending OTP for broker=%s, data=%s", broker, data)
242
+ response = self._post(
243
+ endpoint=self.urls["send_otp"].format(broker),
244
+ url=self.base_url,
245
+ json=data
246
+ )
247
+ return response.get('data')
248
+
249
+ def verify_otp(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
250
+ """
251
+ Verify user OTP under a broker.
252
+
253
+ Args:
254
+ broker (str): Broker name.
255
+ data (Dict[str, Any]): OTP verification data.
256
+
257
+ Returns:
258
+ Optional[Dict[str, Any]]: Verification status.
259
+ """
260
+ logger.info("Verifying OTP for broker=%s, data=%s", broker, data)
261
+ response = self._post(
262
+ endpoint=self.urls["verify_otp"].format(broker),
263
+ url=self.base_url,
264
+ json=data
265
+ )
266
+ return response.get('data')
267
+
268
+ def register_dealer_user(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
269
+ """
270
+ Register a new dealer user.
271
+
272
+ Args:
273
+ broker (str): Broker name.
274
+ data (Dict[str, Any]): Dealer user details.
275
+
276
+ Returns:
277
+ Optional[Dict[str, Any]]: Registration status or dealer user details.
278
+ """
279
+ logger.info("Registering dealer user for broker=%s, data=%s", broker, data)
280
+ response = self._post(
281
+ endpoint=self.urls["register_dealer_user"].format(broker),
282
+ url=self.base_url,
283
+ json=data
284
+ )
285
+ return response.get('data')
286
+
287
+ def update_dealer_user(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
288
+ """
289
+ Update an existing dealer user's information.
290
+
291
+ Args:
292
+ broker (str): Broker name.
293
+ data (Dict[str, Any]): Dealer user update payload.
294
+
295
+ Returns:
296
+ Optional[Dict[str, Any]]: Updated dealer user data.
297
+ """
298
+ logger.info("Updating dealer user for broker=%s, data=%s", broker, data)
299
+ response = self._put(
300
+ endpoint=self.urls["register_dealer_user"].format(broker),
301
+ url=self.base_url,
302
+ json=data
303
+ )
304
+ return response.get('data')
305
+
306
+ def sso(self, broker: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
307
+ """
308
+ Perform Single Sign-On (SSO) operation.
309
+
310
+ Args:
311
+ broker (str): Broker identifier.
312
+ data (Dict[str, Any]): SSO payload.
313
+
314
+ Returns:
315
+ Optional[Dict[str, Any]]: SSO token or user session.
316
+ """
317
+ logger.info("Performing SSO for broker=%s, data=%s", broker, data)
318
+ response = self._post(
319
+ endpoint=self.urls["sso"].format(broker),
320
+ url=self.base_url,
321
+ json=data
322
+ )
323
+ return response.get('data')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bw-essentials-core
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: Reusable utilities for S3, email, Data Loch, Microsoft Teams Notifications and more.
5
5
  Author: InvestorAI
6
6
  Author-email: support+tech@investorai.in
@@ -1,6 +1,6 @@
1
1
  bw_essentials/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  bw_essentials/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- bw_essentials/constants/services.py,sha256=ZRz-cfumqafmAsetW48S3KMTjTaNAdoV1zu3bRuYfNc,513
3
+ bw_essentials/constants/services.py,sha256=ymvYcXjJpWQoGnnCnqURhQao61QJAMM-g9sJCx_Y8s4,561
4
4
  bw_essentials/data_loch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  bw_essentials/data_loch/data_loch.py,sha256=Ae3WIywFawThKcpsnf8nC8ldI9ARrSNqz8e5b6sINwY,11006
6
6
  bw_essentials/email_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,13 +13,14 @@ bw_essentials/s3_utils/s3_utils.py,sha256=wzjVrTX22_8PMX86svKFYGMZwgjBHbOaeEsxO-
13
13
  bw_essentials/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  bw_essentials/services/api_client.py,sha256=74tKxhY_HiVCN3kgtk27a4GNznMaRphAspt9CWecuiY,7793
15
15
  bw_essentials/services/broker.py,sha256=As-VT1MxkCD9p-13dQJDwf0_aEJVA6H0wNqL-EYHO4g,8718
16
- bw_essentials/services/market_pricer.py,sha256=WAFcG5JfnqWxjzXRPrMdovPuKqWJIwa3ENvGAyEyjeM,8712
16
+ bw_essentials/services/market_pricer.py,sha256=Qc9lxzAjhefAvjyEKsBPDu60bF6_61cnSpZNfjGMyDg,11755
17
17
  bw_essentials/services/master_data.py,sha256=2o_r2gdhIKBeTFgn5IIY-becgCEpYBymO4BKmixdV1g,11987
18
18
  bw_essentials/services/model_portfolio_reporting.py,sha256=iOtm4gyfU8P7C0R1gp6gUJI4ZRxJD5K2GLOalI_gToM,3249
19
19
  bw_essentials/services/trade_placement.py,sha256=PrzeU2XXC9HF1IQ1dMDM_ZHxmC491sOl-JbA6GWPwII,20772
20
+ bw_essentials/services/user_app.py,sha256=_Y2NgDq6kEoco7ZRJ3-MMYJ-K2HGStJoFoeGOH_HotQ,11107
20
21
  bw_essentials/services/user_portfolio.py,sha256=_5M6yPfQt4MXedINBawEoPkb_o7IGzxPeHkvZkoQm8k,16191
21
22
  bw_essentials/services/user_portfolio_reporting.py,sha256=QaZzcw912Uos5ZafEaxLXDmCyetTFiVX3at3cTAv6MA,6003
22
- bw_essentials_core-0.0.8.dist-info/METADATA,sha256=GHHKudJ8VIoZ0nOUAeAnpQy0FYXXuPMSTQYA_aa3Wt0,7501
23
- bw_essentials_core-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- bw_essentials_core-0.0.8.dist-info/top_level.txt,sha256=gDc5T_y5snwKGXDQUusEus-FEt0RFwG644Yn_58wQOQ,14
25
- bw_essentials_core-0.0.8.dist-info/RECORD,,
23
+ bw_essentials_core-0.1.0.dist-info/METADATA,sha256=jara8u2pGS_rtKqecDPjm1TkRMhy8GdZwFlmC7YYz5s,7501
24
+ bw_essentials_core-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
+ bw_essentials_core-0.1.0.dist-info/top_level.txt,sha256=gDc5T_y5snwKGXDQUusEus-FEt0RFwG644Yn_58wQOQ,14
26
+ bw_essentials_core-0.1.0.dist-info/RECORD,,