quantplay 2.0.91__tar.gz → 2.0.92__tar.gz
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.
- {quantplay-2.0.91 → quantplay-2.0.92}/PKG-INFO +26 -1
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/motilal.py +16 -31
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/noren.py +0 -16
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/xts.py +99 -43
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/xts_utils/Connect.py +266 -408
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/wrapper/aws/s3.py +6 -1
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay.egg-info/PKG-INFO +26 -1
- {quantplay-2.0.91 → quantplay-2.0.92}/setup.py +1 -1
- {quantplay-2.0.91 → quantplay-2.0.92}/README.md +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/pyproject.toml +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/aliceblue.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/angelone.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/broker_factory.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/dhan.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/five_paisa.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/generics/broker.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/kotak.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/upstox.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/broker/zerodha.py +1 -1
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/model/broker.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/model/instrument_data.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/py.typed +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/caching.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/pickle_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/wrapper/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay.egg-info/SOURCES.txt +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/setup.cfg +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/tests/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/tests/conftest.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/tests/wrapper/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.91 → quantplay-2.0.92}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: quantplay
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.92
|
|
4
4
|
Summary: This python package will be stored in AWS CodeArtifact
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
7
7
|
Author-email:
|
|
8
8
|
License: MIT
|
|
9
|
+
Requires-Dist: setuptools
|
|
10
|
+
Requires-Dist: path
|
|
11
|
+
Requires-Dist: pyotp
|
|
12
|
+
Requires-Dist: retrying
|
|
13
|
+
Requires-Dist: boto3
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: websocket-client
|
|
16
|
+
Requires-Dist: smartapi-python
|
|
17
|
+
Requires-Dist: logzero
|
|
18
|
+
Requires-Dist: selenium
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
Requires-Dist: pandas
|
|
21
|
+
Requires-Dist: pyarrow
|
|
22
|
+
Requires-Dist: polars
|
|
23
|
+
Requires-Dist: kiteconnect
|
|
24
|
+
Requires-Dist: pya3
|
|
25
|
+
Requires-Dist: py5paisa
|
|
26
|
+
Requires-Dist: upstox-python-sdk
|
|
27
|
+
Requires-Dist: undetected-chromedriver
|
|
28
|
+
Requires-Dist: cachetools
|
|
29
|
+
Requires-Dist: py_vollib
|
|
30
|
+
Requires-Dist: python-engineio
|
|
31
|
+
Requires-Dist: python-socketio
|
|
32
|
+
Requires-Dist: six
|
|
33
|
+
Requires-Dist: dhanhq
|
|
9
34
|
|
|
10
35
|
# Quantplay Alpha playground
|
|
11
36
|
|
|
@@ -285,53 +285,38 @@ class Motilal(Broker):
|
|
|
285
285
|
Constants.logger.info(f"[GET_LTP_RESPONSE] response {response}")
|
|
286
286
|
return response["data"]["ltp"] / 100.0
|
|
287
287
|
|
|
288
|
-
def
|
|
289
|
-
|
|
288
|
+
def modify_price(
|
|
289
|
+
self,
|
|
290
|
+
order_id: str,
|
|
291
|
+
price: float,
|
|
292
|
+
trigger_price: float | None = None,
|
|
293
|
+
order_type: OrderTypeType | None = None,
|
|
294
|
+
):
|
|
290
295
|
orders = self.orders()
|
|
291
296
|
orders = orders.filter(pl.col("order_id") == order_id)
|
|
292
297
|
|
|
293
298
|
if len(orders) != 1:
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
Constants.logger.error(
|
|
300
|
+
"[ORDER_NOT_FOUND] invalid modify request for {}".format(order_id)
|
|
296
301
|
)
|
|
302
|
+
return
|
|
297
303
|
|
|
298
304
|
order = orders.to_dicts()[0]
|
|
299
305
|
order["last_modified_time"] = str(order["last_modified_time"])
|
|
300
306
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
"trigger_price" in order_to_modify
|
|
305
|
-
and order_to_modify["trigger_price"] is not None # type:ignore
|
|
306
|
-
):
|
|
307
|
-
order["trigger_price"] = order_to_modify["trigger_price"]
|
|
307
|
+
order["price"] = price
|
|
308
|
+
if trigger_price is not None:
|
|
309
|
+
order["trigger_price"] = trigger_price
|
|
308
310
|
|
|
309
|
-
if
|
|
311
|
+
if order["order_type"] == "SL":
|
|
310
312
|
order["order_type"] = "STOPLOSS"
|
|
311
313
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
def modify_price(
|
|
315
|
-
self,
|
|
316
|
-
order_id: str,
|
|
317
|
-
price: float,
|
|
318
|
-
trigger_price: float | None = None,
|
|
319
|
-
order_type: OrderTypeType | None = None,
|
|
320
|
-
):
|
|
321
|
-
order_to_modify = {
|
|
322
|
-
"order_id": order_id,
|
|
323
|
-
"price": price,
|
|
324
|
-
"trigger_price": trigger_price,
|
|
325
|
-
"order_type": order_type,
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
self.modify_order(order_to_modify) # type: ignore
|
|
314
|
+
print(order)
|
|
315
|
+
self.modify_order(order) # type: ignore
|
|
329
316
|
|
|
330
317
|
# TODO
|
|
331
318
|
def modify_order(self, order: Any) -> str:
|
|
332
319
|
order = copy.deepcopy(order) # type:ignore
|
|
333
|
-
order = self.add_existing_order_details(order)
|
|
334
|
-
|
|
335
320
|
data = {
|
|
336
321
|
"uniqueorderid": order["order_id"],
|
|
337
322
|
"newordertype": order["order_type"].upper(),
|
|
@@ -250,11 +250,6 @@ class Noren(Broker):
|
|
|
250
250
|
f"[PLACE_ORDER_FAILED] {exception_message}"
|
|
251
251
|
)
|
|
252
252
|
|
|
253
|
-
@retry(
|
|
254
|
-
wait_exponential_multiplier=3000,
|
|
255
|
-
wait_exponential_max=10000,
|
|
256
|
-
stop_max_attempt_number=3,
|
|
257
|
-
)
|
|
258
253
|
def ltp(self, exchange: ExchangeType, tradingsymbol: str):
|
|
259
254
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange)
|
|
260
255
|
|
|
@@ -380,11 +375,6 @@ class Noren(Broker):
|
|
|
380
375
|
|
|
381
376
|
return response
|
|
382
377
|
|
|
383
|
-
@retry(
|
|
384
|
-
wait_exponential_multiplier=3000,
|
|
385
|
-
wait_exponential_max=10000,
|
|
386
|
-
stop_max_attempt_number=3,
|
|
387
|
-
)
|
|
388
378
|
def holdings(self):
|
|
389
379
|
holdings = self.invoke_noren_api(self.api.get_holdings)
|
|
390
380
|
if holdings is None or len(holdings) == 0:
|
|
@@ -695,12 +685,6 @@ class Noren(Broker):
|
|
|
695
685
|
traceback.print_exc()
|
|
696
686
|
raise RetryableException("Failed to Receive Data from broker. Retrying Again")
|
|
697
687
|
|
|
698
|
-
@retry(
|
|
699
|
-
wait_exponential_multiplier=1000,
|
|
700
|
-
wait_exponential_max=10000,
|
|
701
|
-
stop_max_attempt_number=3,
|
|
702
|
-
retry_on_exception=retry_exception,
|
|
703
|
-
)
|
|
704
688
|
def margins(self) -> MarginsResponse:
|
|
705
689
|
api_margins = self.invoke_noren_api(self.api.get_limits)
|
|
706
690
|
|
|
@@ -12,6 +12,12 @@ from retrying import retry # type: ignore
|
|
|
12
12
|
|
|
13
13
|
from quantplay.broker.generics.broker import Broker
|
|
14
14
|
from quantplay.broker.xts_utils.Connect import XTSConnect
|
|
15
|
+
from quantplay.broker.xts_utils.Exception import (
|
|
16
|
+
XTSDataException,
|
|
17
|
+
XTSGeneralException,
|
|
18
|
+
XTSNetworkException,
|
|
19
|
+
XTSTokenException,
|
|
20
|
+
)
|
|
15
21
|
from quantplay.broker.xts_utils.InteractiveSocketClient import OrderSocket_io
|
|
16
22
|
from quantplay.exception.exceptions import (
|
|
17
23
|
BrokerException,
|
|
@@ -76,8 +82,11 @@ class XTS(Broker):
|
|
|
76
82
|
self.load_instrument()
|
|
77
83
|
|
|
78
84
|
def set_wrapper(self, serialized_wrapper: str, serialized_md_wrapper: str):
|
|
79
|
-
self.wrapper = pickle.loads(
|
|
80
|
-
|
|
85
|
+
self.wrapper: XTSConnect = pickle.loads(
|
|
86
|
+
codecs.decode(serialized_wrapper.encode(), "base64")
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self.md_wrapper: XTSConnect = pickle.loads(
|
|
81
90
|
codecs.decode(serialized_md_wrapper.encode(), "base64")
|
|
82
91
|
)
|
|
83
92
|
|
|
@@ -101,29 +110,34 @@ class XTS(Broker):
|
|
|
101
110
|
|
|
102
111
|
ins_type = instrument["instrument_type"]
|
|
103
112
|
name = instrument["name"]
|
|
113
|
+
|
|
104
114
|
if ins_type in ["CE", "PE"]:
|
|
105
115
|
expiry = datetime.strftime(
|
|
106
116
|
datetime.strptime(str(instrument["expiry"]), "%Y-%m-%d"),
|
|
107
117
|
"%d%b%Y",
|
|
108
118
|
).upper()
|
|
109
119
|
strike = str(instrument["strike"]).rstrip("0")
|
|
120
|
+
|
|
110
121
|
if strike[-1] == ".":
|
|
111
122
|
strike = strike[:-1]
|
|
123
|
+
|
|
112
124
|
instrument["broker_symbol"] = f"{name} {expiry} {ins_type} {strike}"
|
|
125
|
+
|
|
113
126
|
elif ins_type == "FUT":
|
|
114
127
|
expiry = datetime.strftime(
|
|
115
128
|
datetime.strptime(str(instrument["expiry"]), "%Y-%m-%d"),
|
|
116
129
|
"%d%b%Y",
|
|
117
130
|
).upper()
|
|
118
131
|
instrument["broker_symbol"] = f"{name} {expiry}"
|
|
132
|
+
|
|
119
133
|
else:
|
|
120
134
|
instrument["broker_symbol"] = tradingsymbol
|
|
121
135
|
|
|
122
|
-
# TODO: Types
|
|
123
136
|
self.symbol_data[f"{exchange}:{tradingsymbol}"] = instrument # type: ignore
|
|
124
137
|
|
|
125
138
|
PickleUtils.save_data(self.symbol_data, "xts_instruments")
|
|
126
139
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from server")
|
|
140
|
+
|
|
127
141
|
self.initialize_broker_symbol_map()
|
|
128
142
|
|
|
129
143
|
def login(self, api_key: str, api_secret: str, md_api_key: str, md_api_secret: str):
|
|
@@ -133,48 +147,53 @@ class XTS(Broker):
|
|
|
133
147
|
secretKey=api_secret,
|
|
134
148
|
root=self.root_url,
|
|
135
149
|
)
|
|
136
|
-
xt_core_response = self.wrapper.interactive_login
|
|
150
|
+
xt_core_response = self.invoke_xts_api(self.wrapper.interactive_login)
|
|
151
|
+
|
|
137
152
|
self.md_wrapper = XTSConnect(
|
|
138
153
|
apiKey=md_api_key,
|
|
139
154
|
secretKey=md_api_secret,
|
|
140
155
|
root=self.root_url,
|
|
141
156
|
)
|
|
142
|
-
md_response = self.md_wrapper.marketdata_login
|
|
157
|
+
md_response = self.invoke_xts_api(self.md_wrapper.marketdata_login)
|
|
158
|
+
|
|
143
159
|
if "type" not in xt_core_response or xt_core_response["type"] != "success":
|
|
144
160
|
print(f"api login response {xt_core_response}")
|
|
145
161
|
raise TokenException("Api key credentials are incorrect")
|
|
162
|
+
|
|
146
163
|
if "type" not in md_response or md_response["type"] != "success":
|
|
147
164
|
print(f"market data login response {md_response}")
|
|
148
165
|
raise TokenException("Market data api credentials are invalid")
|
|
166
|
+
|
|
149
167
|
self.ClientID = xt_core_response["result"]["userID"]
|
|
168
|
+
|
|
150
169
|
except TokenException:
|
|
151
170
|
raise
|
|
171
|
+
|
|
152
172
|
except Exception:
|
|
153
173
|
raise InvalidArgumentException("Invalid api key/secret")
|
|
154
174
|
|
|
155
175
|
def handle_exception(self, response: Dict[str, Any]):
|
|
156
176
|
if "data" in response and "description" in response["data"]:
|
|
157
177
|
data = response["data"]
|
|
178
|
+
|
|
158
179
|
if "max limit" in data["description"].lower():
|
|
159
180
|
user_id = self.profile()["user_id"]
|
|
160
181
|
print("Rate limit problem")
|
|
161
182
|
raise RetryableException(f"{user_id}: Request limit exceeded")
|
|
183
|
+
|
|
162
184
|
if (
|
|
163
185
|
"description" in response
|
|
164
186
|
and "Authorization not found" in response["description"]
|
|
165
187
|
):
|
|
166
188
|
raise TokenException(response["description"])
|
|
189
|
+
|
|
167
190
|
if "type" in response and response["type"] == "error":
|
|
168
191
|
raise Exception(f"[XTS_Error]: {response['description']}")
|
|
169
192
|
|
|
170
|
-
@retry(
|
|
171
|
-
wait_exponential_multiplier=3000,
|
|
172
|
-
wait_exponential_max=10000,
|
|
173
|
-
stop_max_attempt_number=3,
|
|
174
|
-
retry_on_exception=retry_exception,
|
|
175
|
-
)
|
|
176
193
|
def margins(self) -> MarginsResponse:
|
|
177
|
-
api_response = self.
|
|
194
|
+
api_response = self.invoke_xts_api(
|
|
195
|
+
self.wrapper.get_balance, clientID=self.ClientID
|
|
196
|
+
)
|
|
178
197
|
self.handle_exception(api_response)
|
|
179
198
|
|
|
180
199
|
if not api_response:
|
|
@@ -194,14 +213,10 @@ class XTS(Broker):
|
|
|
194
213
|
"cash": 0,
|
|
195
214
|
}
|
|
196
215
|
|
|
197
|
-
@retry(
|
|
198
|
-
wait_exponential_multiplier=3000,
|
|
199
|
-
wait_exponential_max=10000,
|
|
200
|
-
stop_max_attempt_number=3,
|
|
201
|
-
retry_on_exception=retry_exception,
|
|
202
|
-
)
|
|
203
216
|
def profile(self) -> UserBrokerProfileResponse:
|
|
204
|
-
api_response = self.
|
|
217
|
+
api_response = self.invoke_xts_api(
|
|
218
|
+
self.wrapper.get_profile, clientID=self.ClientID
|
|
219
|
+
)
|
|
205
220
|
self.handle_exception(api_response)
|
|
206
221
|
api_response = api_response["result"]
|
|
207
222
|
|
|
@@ -213,14 +228,10 @@ class XTS(Broker):
|
|
|
213
228
|
|
|
214
229
|
return response
|
|
215
230
|
|
|
216
|
-
@retry(
|
|
217
|
-
wait_exponential_multiplier=3000,
|
|
218
|
-
wait_exponential_max=10000,
|
|
219
|
-
stop_max_attempt_number=3,
|
|
220
|
-
retry_on_exception=retry_exception,
|
|
221
|
-
)
|
|
222
231
|
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
223
|
-
api_response = self.
|
|
232
|
+
api_response = self.invoke_xts_api(
|
|
233
|
+
self.wrapper.get_order_book, clientID=self.ClientID
|
|
234
|
+
)
|
|
224
235
|
self.handle_exception(api_response)
|
|
225
236
|
|
|
226
237
|
api_response = api_response["result"]
|
|
@@ -345,22 +356,13 @@ class XTS(Broker):
|
|
|
345
356
|
|
|
346
357
|
return orders_df[list(self.orders_schema.keys())].cast(self.orders_schema)
|
|
347
358
|
|
|
348
|
-
@retry(
|
|
349
|
-
wait_exponential_multiplier=3000,
|
|
350
|
-
wait_exponential_max=10000,
|
|
351
|
-
stop_max_attempt_number=3,
|
|
352
|
-
)
|
|
353
359
|
def holdings(self):
|
|
354
360
|
return pl.DataFrame(schema=self.holidings_schema)
|
|
355
361
|
|
|
356
|
-
@retry(
|
|
357
|
-
wait_exponential_multiplier=3000,
|
|
358
|
-
wait_exponential_max=10000,
|
|
359
|
-
stop_max_attempt_number=3,
|
|
360
|
-
retry_on_exception=retry_exception,
|
|
361
|
-
)
|
|
362
362
|
def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
|
|
363
|
-
api_response = self.
|
|
363
|
+
api_response = self.invoke_xts_api(
|
|
364
|
+
self.wrapper.get_position_daywise, clientID=self.ClientID
|
|
365
|
+
)
|
|
364
366
|
self.handle_exception(api_response)
|
|
365
367
|
|
|
366
368
|
api_response = api_response["result"]["positionList"]
|
|
@@ -478,7 +480,9 @@ class XTS(Broker):
|
|
|
478
480
|
}
|
|
479
481
|
for x in symbols
|
|
480
482
|
]
|
|
481
|
-
|
|
483
|
+
|
|
484
|
+
api_response = self.invoke_xts_api(
|
|
485
|
+
self.md_wrapper.get_quote,
|
|
482
486
|
Instruments=instruments,
|
|
483
487
|
xtsMessageCode=1512,
|
|
484
488
|
publishFormat="JSON",
|
|
@@ -486,6 +490,7 @@ class XTS(Broker):
|
|
|
486
490
|
|
|
487
491
|
if "type" in api_response and api_response["type"] == "error":
|
|
488
492
|
raise TokenException(api_response["description"])
|
|
493
|
+
|
|
489
494
|
api_response = api_response["result"]
|
|
490
495
|
|
|
491
496
|
ltp_json = api_response["listQuotes"]
|
|
@@ -541,7 +546,8 @@ class XTS(Broker):
|
|
|
541
546
|
"", # TODO
|
|
542
547
|
)
|
|
543
548
|
|
|
544
|
-
api_response = self.
|
|
549
|
+
api_response = self.invoke_xts_api(
|
|
550
|
+
self.md_wrapper.get_quote,
|
|
545
551
|
Instruments=[
|
|
546
552
|
{
|
|
547
553
|
"exchangeSegment": exchange_code,
|
|
@@ -561,6 +567,7 @@ class XTS(Broker):
|
|
|
561
567
|
|
|
562
568
|
try:
|
|
563
569
|
ltp_json = api_response["listQuotes"][0]
|
|
570
|
+
|
|
564
571
|
except IndexError as e:
|
|
565
572
|
print(api_response, e)
|
|
566
573
|
raise BrokerException("Broker Provided Invalid Response")
|
|
@@ -593,7 +600,8 @@ class XTS(Broker):
|
|
|
593
600
|
if tag is None:
|
|
594
601
|
tag = ""
|
|
595
602
|
|
|
596
|
-
api_response = self.
|
|
603
|
+
api_response = self.invoke_xts_api(
|
|
604
|
+
self.wrapper.place_order,
|
|
597
605
|
exchangeSegment=exchange_name,
|
|
598
606
|
exchangeInstrumentID=exchange_token,
|
|
599
607
|
orderType=xts_order_type,
|
|
@@ -626,7 +634,8 @@ class XTS(Broker):
|
|
|
626
634
|
|
|
627
635
|
tag = order_data["tag"]
|
|
628
636
|
|
|
629
|
-
api_response = self.
|
|
637
|
+
api_response = self.invoke_xts_api(
|
|
638
|
+
self.wrapper.cancel_order,
|
|
630
639
|
appOrderID=int(order_id),
|
|
631
640
|
clientID=order_data["user_id"],
|
|
632
641
|
orderUniqueIdentifier=tag,
|
|
@@ -678,7 +687,8 @@ class XTS(Broker):
|
|
|
678
687
|
time_in_force = "DAY"
|
|
679
688
|
disclosed_quantity = 0
|
|
680
689
|
|
|
681
|
-
api_response = self.
|
|
690
|
+
api_response = self.invoke_xts_api(
|
|
691
|
+
self.wrapper.modify_order,
|
|
682
692
|
appOrderID=int(order_id),
|
|
683
693
|
modifiedTimeInForce=time_in_force,
|
|
684
694
|
modifiedDisclosedQuantity=disclosed_quantity,
|
|
@@ -806,3 +816,49 @@ class XTS(Broker):
|
|
|
806
816
|
except Exception as e:
|
|
807
817
|
print(e)
|
|
808
818
|
Constants.logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
|
|
819
|
+
|
|
820
|
+
@retry(
|
|
821
|
+
wait_exponential_multiplier=3000,
|
|
822
|
+
wait_exponential_max=10000,
|
|
823
|
+
stop_max_attempt_number=3,
|
|
824
|
+
retry_on_exception=retry_exception,
|
|
825
|
+
)
|
|
826
|
+
def invoke_xts_api(self, fn: Any, *args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
827
|
+
try:
|
|
828
|
+
response = fn(*args, **kwargs)
|
|
829
|
+
|
|
830
|
+
if "data" in response and "description" in response["data"]:
|
|
831
|
+
data = response["data"]
|
|
832
|
+
|
|
833
|
+
if "max limit" in data["description"].lower():
|
|
834
|
+
user_id = self.profile()["user_id"]
|
|
835
|
+
print("Rate limit problem")
|
|
836
|
+
raise RetryableException(f"{user_id}: Request limit exceeded")
|
|
837
|
+
|
|
838
|
+
if (
|
|
839
|
+
"description" in response
|
|
840
|
+
and "Authorization not found" in response["description"]
|
|
841
|
+
):
|
|
842
|
+
raise TokenException(response["description"])
|
|
843
|
+
|
|
844
|
+
if "type" in response and response["type"] == "error":
|
|
845
|
+
raise Exception(f"[XTS_Error]: {response['description']}")
|
|
846
|
+
|
|
847
|
+
return response
|
|
848
|
+
|
|
849
|
+
except XTSTokenException as e:
|
|
850
|
+
raise TokenException(str(e))
|
|
851
|
+
|
|
852
|
+
except (TokenException, RetryableException):
|
|
853
|
+
raise
|
|
854
|
+
|
|
855
|
+
except (
|
|
856
|
+
XTSGeneralException,
|
|
857
|
+
XTSDataException,
|
|
858
|
+
XTSNetworkException,
|
|
859
|
+
) as e:
|
|
860
|
+
raise BrokerException(str(e))
|
|
861
|
+
|
|
862
|
+
except Exception:
|
|
863
|
+
traceback.print_exc()
|
|
864
|
+
raise
|