bw-essentials-core 0.0.1__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.

@@ -0,0 +1,250 @@
1
+ """
2
+ broker.py
3
+
4
+ This module provides a `Broker` client class for interacting with the Broker Service API.
5
+ It is designed to handle key operations such as authentication, retrieving user balances and holdings,
6
+ authorizing holdings, validating broker users, handling user trading instructions, and acknowledging
7
+ surveillance orders.
8
+
9
+ The client is initialized with contextual information such as `service_user`, `request_id`, and
10
+ `tenant_id`, which are common to all requests. However, parameters like `broker_name`, `user_id`,
11
+ and `entity_id` are passed at the method level for better flexibility and multi-user support.
12
+
13
+ The `Broker` class extends the `ApiClient` class from `bw_essentials` and relies on its
14
+ HTTP request methods (`_get`, `_post`) for communication with the Broker Service.
15
+
16
+ Typical usage example:
17
+ broker = Broker(
18
+ service_user="system"
19
+ )
20
+
21
+ balance = broker.get_balance(broker_name="kite", user_id="U123")
22
+
23
+ APIs supported:
24
+ - Authenticate with the broker
25
+ - Get user balance and holdings
26
+ - Validate authentication for a broker user
27
+ - Fetch CDSL redirect link for holdings authorization
28
+ - Send user trading instructions
29
+ - Post trade details
30
+ - Acknowledge surveillance orders
31
+ """
32
+ import logging
33
+
34
+ from bw_essentials.constants.services import Services
35
+ from bw_essentials.services.api_client import ApiClient
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class Broker(ApiClient):
41
+ """
42
+ Class for making API calls to the Broker Service.
43
+ """
44
+
45
+ def __init__(self,
46
+ service_user: str):
47
+ """
48
+ Initialize the Broker API client.
49
+
50
+ Args:
51
+ service_user (str): Service user identifier.
52
+ """
53
+ logger.info(f"Initializing Broker client for user: {service_user}")
54
+ super().__init__(user=service_user)
55
+ self.base_url = self.get_base_url(Services.BROKER.value)
56
+ self.name = Services.BROKER.value
57
+
58
+ self.urls = {
59
+ "balance": "brokers/{}/balance",
60
+ "holdings": "brokers/{}/holdings",
61
+ "validate_auth": "brokers/{}/auth/validate",
62
+ "authorised_holdings": "brokers/{}/holdings/authorise",
63
+ "user_instructions": "brokers/{}/user/instructions",
64
+ "details": "brokers/{}/trade/details",
65
+ "surveillance_orders": "brokers/{}/surveillance/orders"
66
+ }
67
+
68
+ def authenticate(self, broker_name: str, user_id: str, entity_id: str,
69
+ access_token: str = None, group_id: str = None,
70
+ session_id: str = None, session_key: str = None,
71
+ participant_id: str = None, client_order_number: str = None):
72
+ """
73
+ Authenticate the user with the broker service.
74
+
75
+ Args:
76
+ broker_name (str): Name of the broker.
77
+ user_id (str): User identifier.
78
+ entity_id (str): Entity identifier.
79
+ access_token (str, optional): Broker access token.
80
+ group_id (str, optional): Group ID for session.
81
+ session_id (str, optional): Broker session ID.
82
+ session_key (str, optional): Session key.
83
+ participant_id (str, optional): Participant ID.
84
+ client_order_number (str, optional): Client order reference number.
85
+ """
86
+ data = {
87
+ "user_id": user_id,
88
+ "entity_id": entity_id,
89
+ "access_token": access_token,
90
+ "group_id": group_id,
91
+ "session_id": session_id,
92
+ "session_key": session_key,
93
+ "participant_id": participant_id,
94
+ "client_order_number": client_order_number
95
+ }
96
+ logger.info(f"In - authenticate {data =}")
97
+ filtered_data = {k: v for k, v in data.items() if v is not None}
98
+ endpoint = f"brokers/{broker_name}/auth"
99
+ self._post(url=self.base_url, endpoint=endpoint, json=filtered_data)
100
+
101
+ def get_balance(self, broker_name: str, user_id: str) -> float:
102
+ """
103
+ Fetch user balance from the broker service.
104
+
105
+ Args:
106
+ broker_name (str): Broker name.
107
+ user_id (str): User ID.
108
+
109
+ Returns:
110
+ float: Account balance.
111
+ """
112
+ logger.info(f"In - get_balance {user_id =}")
113
+ response = self._get(
114
+ url=self.base_url,
115
+ endpoint=self.urls["balance"].format(broker_name),
116
+ params={"user_id": user_id}
117
+ )
118
+ logger.info(f"{response =}")
119
+ return response["data"]["balance"]
120
+
121
+ def get_holdings(self, broker_name: str, user_id: str) -> list:
122
+ """
123
+ Fetch user holdings from the broker service.
124
+
125
+ Args:
126
+ broker_name (str): Broker name.
127
+ user_id (str): User ID.
128
+
129
+ Returns:
130
+ list: List of user holdings.
131
+ """
132
+ logger.info(f"In - get_holdings {user_id =}")
133
+ response = self._get(
134
+ url=self.base_url,
135
+ endpoint=self.urls["holdings"].format(broker_name),
136
+ params={"user_id": user_id}
137
+ )
138
+ logger.info(f"{response =}")
139
+ return response["data"]["holdings"]
140
+
141
+ def validate_auth(self, broker_name: str, broker_user_id: str) -> bool:
142
+ """
143
+ Validate a broker user's authentication.
144
+
145
+ Args:
146
+ broker_name (str): Broker name.
147
+ broker_user_id (str): Broker-specific user ID.
148
+
149
+ Returns:
150
+ bool: Authentication validity status.
151
+ """
152
+ payload = {
153
+ "user_id": broker_user_id,
154
+ "broker": broker_name
155
+ }
156
+ logger.info(f"In - validate_auth {payload =}")
157
+ response = self._get(
158
+ url=self.base_url,
159
+ endpoint=self.urls["validate_auth"].format(broker_name),
160
+ params=payload
161
+ )
162
+ logger.info(f"{response =}")
163
+ return response["data"]["auth_valid"]
164
+
165
+ def get_cdsl_redirect(self, broker_name: str, user_id: str, holdings_to_authorize: list, request_url: str = None) -> dict:
166
+ """
167
+ Generate a CDSL redirect for authorizing holdings.
168
+
169
+ Args:
170
+ broker_name (str): Broker name.
171
+ user_id (str): User ID.
172
+ holdings_to_authorize (list): Holdings to authorize.
173
+ request_url (str, optional): Redirect return URL.
174
+
175
+ Returns:
176
+ dict: Redirect information from the broker.
177
+ """
178
+ payload = {
179
+ "user_id": user_id,
180
+ "request_url": request_url,
181
+ "authorise_holdings": holdings_to_authorize
182
+ }
183
+ logger.info(f"In - get_cdsl_redirect {payload =}")
184
+ response = self._post(
185
+ url=self.base_url,
186
+ endpoint=self.urls["authorised_holdings"].format(broker_name),
187
+ json=payload
188
+ )
189
+ logger.info(f"{response =}")
190
+ return response["data"]
191
+
192
+ def user_instructions(self, broker_name: str, instructions: dict) -> dict:
193
+ """
194
+ Send user instructions to the broker.
195
+
196
+ Args:
197
+ broker_name (str): Broker name.
198
+ instructions (dict): User instructions payload.
199
+
200
+ Returns:
201
+ dict: Response data from broker service.
202
+ """
203
+ logger.info(f"In - user_instructions {instructions =}")
204
+ response = self._post(
205
+ url=self.base_url,
206
+ endpoint=self.urls["user_instructions"].format(broker_name),
207
+ json=instructions
208
+ )
209
+ logger.info(f"{response =}")
210
+ return response["data"]
211
+
212
+ def trade_details(self, broker_name: str, details: dict) -> list:
213
+ """
214
+ Send trade details to the broker.
215
+
216
+ Args:
217
+ broker_name (str): Broker name.
218
+ details (dict): Trade details payload.
219
+
220
+ Returns:
221
+ list: Broker response data.
222
+ """
223
+ logger.info(f"In - trade_details {details =}")
224
+ response = self._post(
225
+ url=self.base_url,
226
+ endpoint=self.urls["details"].format(broker_name),
227
+ json=details
228
+ )
229
+ logger.info(f"{response =}")
230
+ return response["data"]
231
+
232
+ def acknowledge_for_surveillance_orders(self, broker_name: str, surveillance_orders: list) -> None:
233
+ """
234
+ Acknowledge surveillance orders to the broker.
235
+
236
+ Args:
237
+ broker_name (str): Broker name.
238
+ surveillance_orders (list): List of validated surveillance orders.
239
+
240
+ Returns:
241
+ None
242
+ """
243
+ logger.info(f"In - acknowledge_for_surveillance_orders {surveillance_orders =}")
244
+ payload = {"surveillance_orders": surveillance_orders}
245
+ response = self._post(
246
+ url=self.base_url,
247
+ endpoint=self.urls["surveillance_orders"].format(broker_name),
248
+ json=payload
249
+ )
250
+ logger.info(f"{response =}")
@@ -0,0 +1,233 @@
1
+ """
2
+ market_pricer.py
3
+
4
+ This module provides a `MarketPricer` client class to interact with the Market Pricer service APIs.
5
+
6
+ It supports retrieval of:
7
+ - Live market prices for specified securities on a given exchange.
8
+ - End-of-day (EOD) prices for individual or multiple securities on a given date.
9
+
10
+ The `MarketPricer` class extends `ApiClient` from `bw_essentials` and utilizes its built-in
11
+ HTTP communication methods to interact with the external service.
12
+
13
+ Typical use cases include fetching live or historical prices for dashboards, analytics, or
14
+ backtesting systems.
15
+
16
+ Example:
17
+ market_pricer = MarketPricer(
18
+ service_user="system"
19
+ )
20
+
21
+ live_prices = market_pricer.get_live_prices(securities="TCS,RELIANCE", exchange="NSE")
22
+ eod_price = market_pricer.get_eod_prices(ticker="TCS", date="2023-10-03")
23
+ bulk_prices = market_pricer.get_bulk_eod_prices(tickers=["TCS", "RELIANCE"], date="2023-10-03")
24
+ """
25
+
26
+ import logging
27
+
28
+ from bw_essentials.constants.services import Services
29
+ from bw_essentials.services.api_client import ApiClient
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class MarketPricer(ApiClient):
35
+ """
36
+ This class represents a MarketPricer, which is used to retrieve live and end-of-day (EOD) market prices for
37
+ securities.
38
+
39
+ Attributes:
40
+ name (str): The name of the MarketPricer instance.
41
+ urls (dict): A dictionary containing the endpoint URLs for live and EOD prices.
42
+
43
+ Methods:
44
+ __init__(self, user):
45
+ Initializes a new MarketPricer instance.
46
+
47
+ Args:
48
+ user (User): The user object representing the authenticated user.
49
+
50
+ get_live_prices(self, securities, exchange):
51
+ Retrieves live market prices for a list of securities on a specific exchange.
52
+
53
+ Returns:
54
+ list: A list of live market price data for the specified securities.
55
+
56
+ Example:
57
+ market_pricer = MarketPricer(user)
58
+ securities = "TCS,RELIANCE"
59
+ exchange = "NSE"
60
+ live_prices = market_pricer.get_live_prices(securities, exchange)
61
+ """
62
+
63
+ def __init__(self, service_user: str):
64
+ logger.info(f"Initializing MarketPricer client for user: {service_user}")
65
+ super().__init__(user=service_user)
66
+ self.base_url = self.get_base_url(Services.MARKET_PRICER.value)
67
+ self.name = Services.MARKET_PRICER.value
68
+ self.urls = {
69
+ "live": "live",
70
+ "eod": "eod"
71
+ }
72
+
73
+ def get_live_prices(self, securities, exchange):
74
+ """
75
+ Retrieves live market prices for a list of securities on a specific exchange.
76
+
77
+ Args:
78
+ securities (str): A list of security symbols for which live prices are requested.
79
+ exchange (str): The exchange on which the securities are traded.
80
+ Returns:
81
+ list: A list of live market price data for the specified securities.
82
+
83
+ Example:
84
+ market_pricer = MarketPricer(user)
85
+ securities = "TCS,RELIANCE"
86
+ exchange = "NSE"
87
+ live_prices = market_pricer.get_live_prices(securities, exchange)
88
+
89
+ API Endpoint:
90
+ GET /pricing/live_prices
91
+
92
+ API Parameters:
93
+ - symbols (str): Comma-separated list of security symbols.
94
+ - exchange (str): The exchange on which the securities are traded.
95
+
96
+ API Response:
97
+ {
98
+ "data": [
99
+ {
100
+ "symbol": "TCS",
101
+ "price": 150.25,
102
+ "timestamp": "2023-10-04T10:30:00Z",
103
+ "exchange": "NSE"
104
+ },
105
+ {
106
+ "symbol": "RELIANCE",
107
+ "price": 2750.75,
108
+ "timestamp": "2023-10-04T10:30:00Z",
109
+ "exchange": "NSE"
110
+ }
111
+ ]
112
+ }
113
+ """
114
+ logger.info(f"In - get_live_prices {securities =}, {exchange =}")
115
+ securities = ','.join(securities)
116
+ market_pricing_live_response = self._get(url=self.base_url,
117
+ endpoint=self.urls.get("live"),
118
+ params={"symbols": securities,
119
+ "exchange": exchange})
120
+
121
+ logger.info(f"{market_pricing_live_response =}")
122
+ return market_pricing_live_response.get("data")
123
+
124
+ def get_eod_prices(self, ticker, date):
125
+ """
126
+ Retrieves end-of-day (EOD) market prices for a specific security on a given date.
127
+
128
+ Args:
129
+ ticker (str): The symbol or identifier of the security for which EOD prices are requested.
130
+ date (str): The date for which EOD prices are requested in the format 'YYYY-MM-DD'.
131
+ Returns:
132
+ dict: A dictionary containing the EOD market price data for the specified security on the given date.
133
+
134
+ Example:
135
+ market_pricer = MarketPricer(user)
136
+ security_ticker = "TCS"
137
+ eod_date = "2023-10-03"
138
+ eod_prices = market_pricer.get_eod_prices(security_ticker, eod_date)
139
+
140
+ API Endpoint:
141
+ GET /pricing/eod_prices
142
+
143
+ API Parameters:
144
+ - ticker (str): The symbol or identifier of the security.
145
+ - date (str): The date for which EOD prices are requested in the format 'YYYY-MM-DD'.
146
+
147
+ API Response:
148
+ {
149
+ "data": {
150
+ "symbol": "TCS",
151
+ "date": "2023-10-03",
152
+ "open_price": 148.5,
153
+ "close_price": 150.25,
154
+ "high_price": 151.0,
155
+ "low_price": 147.75,
156
+ "volume": 5000000,
157
+ "ri": 12
158
+ }
159
+ }
160
+ """
161
+ logger.info(f"In - get_eod_prices {ticker =}, {date =}")
162
+ market_pricing_eod_response = self._get(url=self.base_url,
163
+ endpoint=self.urls.get("eod"),
164
+ params={"ticker": ticker,
165
+ "date": date})
166
+ logger.info(f"{market_pricing_eod_response =}")
167
+ return market_pricing_eod_response.get("data")
168
+
169
+ def get_bulk_eod_prices(self, tickers, date):
170
+ """
171
+ Retrieves end-of-day (EOD) market prices for multiple securities on a given date.
172
+
173
+ Args:
174
+ tickers (list or str): List of ticker symbols or comma-separated string of
175
+ ticker symbols.
176
+ date (str): The date for which EOD prices are requested in the format
177
+ 'YYYY-MM-DD'.
178
+ Returns:
179
+ list: A list of dictionaries containing the EOD market price data for each
180
+ security.
181
+
182
+ Example:
183
+ market_pricer = MarketPricer(user)
184
+ security_tickers = ["TCS", "RELIANCE"] # or "TCS,RELIANCE"
185
+ eod_date = "2023-10-03"
186
+ eod_prices = market_pricer.get_bulk_eod_prices(security_tickers, eod_date)
187
+
188
+ API Endpoint:
189
+ GET /pricing/bulk-eod
190
+
191
+ API Parameters:
192
+ - tickers (str): Comma-separated list of ticker symbols.
193
+ - date (str): The date for which EOD prices are requested in the format
194
+ 'YYYY-MM-DD'.
195
+
196
+ API Response:
197
+ {
198
+ "data": [
199
+ {
200
+ "symbol": "TCS",
201
+ "date": "2023-10-03",
202
+ "open_price": 148.5,
203
+ "close_price": 150.25,
204
+ "high_price": 151.0,
205
+ "low_price": 147.75,
206
+ "volume": 5000000,
207
+ "ri": 12
208
+ },
209
+ {
210
+ "symbol": "RELIANCE",
211
+ "date": "2023-10-03",
212
+ "open_price": 2740.0,
213
+ "close_price": 2750.75,
214
+ "high_price": 2755.0,
215
+ "low_price": 2735.0,
216
+ "volume": 3000000,
217
+ "ri": 15
218
+ }
219
+ ]
220
+ }
221
+ """
222
+ logger.info(f"In - get_bulk_eod_prices {tickers=}, {date=}")
223
+ if isinstance(tickers, list):
224
+ tickers = ",".join(tickers)
225
+
226
+ market_pricing_eod_response = self._get(
227
+ url=self.base_url,
228
+ endpoint="bulk-eod",
229
+ params={"tickers": tickers, "date": date}
230
+ )
231
+
232
+ logger.info(f"{market_pricing_eod_response=}")
233
+ return market_pricing_eod_response.get("data")