quantplay 2.0.25__tar.gz → 2.0.27__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.
Files changed (71) hide show
  1. {quantplay-2.0.25 → quantplay-2.0.27}/PKG-INFO +1 -1
  2. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/aliceblue.py +14 -9
  3. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/angelone.py +19 -66
  4. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/five_paisa.py +3 -1
  5. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/generics/broker.py +16 -9
  6. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/motilal.py +39 -49
  7. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/noren.py +7 -3
  8. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/upstox.py +4 -2
  9. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts.py +3 -1
  10. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/zerodha.py +8 -3
  11. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/PKG-INFO +1 -1
  12. {quantplay-2.0.25 → quantplay-2.0.27}/setup.py +1 -1
  13. {quantplay-2.0.25 → quantplay-2.0.27}/README.md +0 -0
  14. {quantplay-2.0.25 → quantplay-2.0.27}/pyproject.toml +0 -0
  15. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/__init__.py +0 -0
  16. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/__init__.py +0 -0
  17. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/auto_login/__init__.py +0 -0
  18. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/auto_login/aliceblue.py +0 -0
  19. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/finvasia_utils/__init__.py +0 -0
  20. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
  21. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/flattrade.py +0 -0
  22. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/__init__.py +0 -0
  23. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
  24. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/ft_noren.py +0 -0
  25. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/generics/__init__.py +0 -0
  26. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/iifl_xts.py +0 -0
  27. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/kite_utils.py +0 -0
  28. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/shoonya.py +0 -0
  29. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/uplink/__init__.py +0 -0
  30. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/uplink/uplink_utils.py +0 -0
  31. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/Connect.py +0 -0
  32. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/Exception.py +0 -0
  33. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
  34. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/__init__.py +0 -0
  35. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/exception/__init__.py +0 -0
  36. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/exception/exceptions.py +0 -0
  37. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/__init__.py +0 -0
  38. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/broker.py +0 -0
  39. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/generics.py +0 -0
  40. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/order_event.py +0 -0
  41. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/py.typed +0 -0
  42. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/__init__.py +0 -0
  43. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/__init__.py +0 -0
  44. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/intraday/__init__.py +0 -0
  45. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/overnight/__init__.py +0 -0
  46. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/futures/__init__.py +0 -0
  47. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/futures/overnight/__init__.py +0 -0
  48. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/__init__.py +0 -0
  49. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/__init__.py +0 -0
  50. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/ladder.py +0 -0
  51. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/musk.py +0 -0
  52. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/short_straddle.py +0 -0
  53. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/__init__.py +0 -0
  54. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/constant.py +0 -0
  55. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/exchange.py +0 -0
  56. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/number_utils.py +0 -0
  57. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/pickle_utils.py +0 -0
  58. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/selenium_utils.py +0 -0
  59. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/__init__.py +0 -0
  60. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/aws/__init__.py +0 -0
  61. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/aws/s3.py +0 -0
  62. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/SOURCES.txt +0 -0
  63. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/dependency_links.txt +0 -0
  64. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/requires.txt +0 -0
  65. {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/top_level.txt +0 -0
  66. {quantplay-2.0.25 → quantplay-2.0.27}/setup.cfg +0 -0
  67. {quantplay-2.0.25 → quantplay-2.0.27}/tests/__init__.py +0 -0
  68. {quantplay-2.0.25 → quantplay-2.0.27}/tests/conftest.py +0 -0
  69. {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/__init__.py +0 -0
  70. {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/aws/__init__.py +0 -0
  71. {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/aws/s3_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.25
3
+ Version: 2.0.27
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -213,8 +213,10 @@ class Aliceblue(Broker):
213
213
  exception_message = f"Order placement failed [{str(e)}]"
214
214
  raise QuantplayOrderPlacementException(exception_message)
215
215
 
216
- def get_ltp(self, exchange, tradingsymbol: str):
217
- inst = self.alice.get_instrument_by_symbol(exchange, tradingsymbol)
216
+ def ltp(self, exchange, tradingsymbol: str):
217
+ inst = self.invoke_aliceblue_api(
218
+ self.alice.get_instrument_by_symbol, exchange=exchange, symbol=tradingsymbol
219
+ )
218
220
  info = self.invoke_aliceblue_api(self.alice.get_scrip_info, instrument=inst)
219
221
 
220
222
  return float(info["LTP"])
@@ -327,7 +329,7 @@ class Aliceblue(Broker):
327
329
  holdings_df = holdings_df.with_columns(
328
330
  pl.struct(["exchange", "tradingsymbol"])
329
331
  .map_elements(
330
- lambda x: int(self.get_ltp(x["exchange"], x["tradingsymbol"])),
332
+ lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
331
333
  return_dtype=pl.Float64,
332
334
  )
333
335
  .alias("price")
@@ -453,6 +455,8 @@ class Aliceblue(Broker):
453
455
  orders_df = orders_df.join(
454
456
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
455
457
  )
458
+ else:
459
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
456
460
 
457
461
  if "rorgqty" in orders_df.columns:
458
462
  orders_df = orders_df.with_columns(
@@ -547,10 +551,6 @@ class Aliceblue(Broker):
547
551
  def margins(self):
548
552
  margins = self.invoke_aliceblue_api(self.alice.get_balance)
549
553
 
550
- if "emsg" in margins and "expired" in margins["emsg"].lower():
551
- raise TokenException("Session expired")
552
- if "stat" in margins and "not_ok" in margins["stat"].lower():
553
- raise TokenException(f"Aliceblue broker error: {margins['emsg']}")
554
554
  margins = [a for a in margins if a["segment"] == "ALL"][0]
555
555
 
556
556
  response = {
@@ -570,6 +570,11 @@ class Aliceblue(Broker):
570
570
  def invoke_aliceblue_api(self, fn: Callable, *args, **kwargs) -> Any:
571
571
  try:
572
572
  response = fn(*args, **kwargs)
573
+
574
+ if "emsg" in response and "expired" in response["emsg"].lower():
575
+ raise TokenException("Session expired")
576
+ if "stat" in response and response["stat"].lower() == "not_ok":
577
+ raise InvalidArgumentException(response["emsg"])
573
578
  if response is None:
574
579
  raise InvalidArgumentException(
575
580
  "Invalid data response. Aliceblue sent incorrect data, Please check."
@@ -577,8 +582,8 @@ class Aliceblue(Broker):
577
582
 
578
583
  return response
579
584
 
580
- except TokenException:
581
- raise QuantplayTokenException("Token Expired")
585
+ except (TokenException, InvalidArgumentException):
586
+ raise
582
587
 
583
588
  except Exception:
584
589
  traceback.print_exc()
@@ -23,7 +23,6 @@ from quantplay.exception.exceptions import (
23
23
  TokenException,
24
24
  retry_exception,
25
25
  )
26
- from quantplay.exception.exceptions import TokenException as QuantplayTokenException
27
26
  from quantplay.model.broker import ModifyOrderRequest, UserBrokerProfileResponse
28
27
  from quantplay.model.order_event import OrderUpdateEvent
29
28
  from quantplay.utils.constant import Constants, OrderType
@@ -162,12 +161,7 @@ class AngelOne(Broker):
162
161
  "Product {} not supported for trading".format(product)
163
162
  )
164
163
 
165
- @retry(
166
- wait_exponential_multiplier=3000,
167
- wait_exponential_max=10000,
168
- stop_max_attempt_number=3,
169
- )
170
- def get_ltp(self, exchange, tradingsymbol: str) -> float:
164
+ def ltp(self, exchange, tradingsymbol: str) -> float:
171
165
  if tradingsymbol in MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP:
172
166
  tradingsymbol = MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP[
173
167
  tradingsymbol
@@ -185,11 +179,6 @@ class AngelOne(Broker):
185
179
  symboltoken=symboltoken,
186
180
  )
187
181
 
188
- if "status" in response and response["status"] is False:
189
- raise InvalidArgumentException(
190
- "Failed to fetch ltp broker error {}".format(response)
191
- )
192
-
193
182
  return response["data"]["ltp"]
194
183
 
195
184
  def place_order(
@@ -310,18 +299,8 @@ class AngelOne(Broker):
310
299
  def cancel_order(self, order_id, variety="NORMAL"):
311
300
  self.wrapper.cancelOrder(order_id=order_id, variety=variety)
312
301
 
313
- @retry(
314
- wait_exponential_multiplier=3000,
315
- wait_exponential_max=15000,
316
- stop_max_attempt_number=5,
317
- retry_on_exception=retry_exception,
318
- )
319
302
  def holdings(self):
320
- try:
321
- holdings = self.invoke_angelone_api(self.wrapper.holding)
322
- except Exception:
323
- raise RetryableException("Access Denied retrying")
324
- self.handle_exception(holdings)
303
+ holdings = self.invoke_angelone_api(self.wrapper.holding)
325
304
 
326
305
  if holdings["data"] is None or len(holdings["data"]) == 0:
327
306
  return pl.DataFrame(schema=self.holidings_schema)
@@ -350,15 +329,11 @@ class AngelOne(Broker):
350
329
  @retry(
351
330
  wait_exponential_multiplier=3000,
352
331
  wait_exponential_max=15000,
353
- stop_max_attempt_number=5,
332
+ stop_max_attempt_number=2,
354
333
  retry_on_exception=retry_exception,
355
334
  )
356
335
  def positions(self, drop_cnc: bool = True):
357
- try:
358
- positions = self.invoke_angelone_api(self.wrapper.position)
359
- except Exception:
360
- raise RetryableException("Access Denied retrying")
361
- self.handle_exception(positions)
336
+ positions = self.invoke_angelone_api(self.wrapper.position)
362
337
 
363
338
  if positions["data"] is None:
364
339
  return pl.DataFrame(schema=self.positions_schema)
@@ -427,18 +402,8 @@ class AngelOne(Broker):
427
402
  self.positions_schema
428
403
  )
429
404
 
430
- @retry(
431
- wait_exponential_multiplier=3000,
432
- wait_exponential_max=10000,
433
- stop_max_attempt_number=3,
434
- retry_on_exception=retry_exception,
435
- )
436
405
  def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
437
- try:
438
- order_book = self.invoke_angelone_api(self.wrapper.orderBook)
439
- except Exception:
440
- raise RetryableException("Access Denied retrying")
441
- self.handle_exception(order_book)
406
+ order_book = self.invoke_angelone_api(self.wrapper.orderBook)
442
407
 
443
408
  if order_book["data"]:
444
409
  orders_df = pl.DataFrame(order_book["data"])
@@ -534,21 +499,10 @@ class AngelOne(Broker):
534
499
  traceback.print_exc()
535
500
  raise ServiceException("Unknown error while fetching order book [{}]")
536
501
 
537
- @retry(
538
- wait_exponential_multiplier=3000,
539
- wait_exponential_max=10000,
540
- stop_max_attempt_number=3,
541
- retry_on_exception=retry_exception,
542
- )
543
502
  def profile(self):
544
- try:
545
- profile_data = self.invoke_angelone_api(
546
- self.wrapper.getProfile, refreshToken=self.refresh_token
547
- )
548
- except Exception:
549
- raise RetryableException("Access Denied retrying")
550
-
551
- self.handle_exception(profile_data)
503
+ profile_data = self.invoke_angelone_api(
504
+ self.wrapper.getProfile, refreshToken=self.refresh_token
505
+ )
552
506
 
553
507
  profile_data = profile_data["data"]
554
508
  response: UserBrokerProfileResponse = {
@@ -559,21 +513,14 @@ class AngelOne(Broker):
559
513
 
560
514
  return response
561
515
 
562
- @retry(
563
- wait_exponential_multiplier=3000,
564
- wait_exponential_max=10000,
565
- stop_max_attempt_number=3,
566
- retry_on_exception=retry_exception,
567
- )
568
516
  def margins(self):
569
517
  api_margins = self.invoke_angelone_api(self.wrapper.rmsLimit)
570
- self.handle_exception(api_margins)
571
518
 
572
519
  if "data" in api_margins and api_margins["data"] is None:
573
520
  if "errorcode" in api_margins and api_margins["errorcode"] == "AB1004":
574
521
  raise TokenException("Angelone server not not responding")
575
522
 
576
- return {"margin_used": 0, "margin_available": 0, "total_balance": 0}
523
+ return {"margin_used": 0.0, "margin_available": 0.0, "total_balance": 0.0}
577
524
 
578
525
  api_margins = api_margins["data"]
579
526
 
@@ -610,19 +557,25 @@ class AngelOne(Broker):
610
557
 
611
558
  return response
612
559
 
560
+ @retry(
561
+ wait_exponential_multiplier=3000,
562
+ wait_exponential_max=10000,
563
+ stop_max_attempt_number=3,
564
+ retry_on_exception=retry_exception,
565
+ )
613
566
  def invoke_angelone_api(self, fn: Callable, *args, **kwargs) -> Dict:
614
567
  try:
615
568
  response = fn(*args, **kwargs)
569
+ if "errorCode" in response and response["errorCode"] == "AG8001":
570
+ raise TokenException(f"{self.user_id}: Invalid Token")
616
571
  if isinstance(response, bytes):
617
572
  raise InvalidArgumentException(
618
573
  "Invalid data response. AngelOne sent incorrect data, Please check."
619
574
  )
620
575
 
621
576
  return response
622
-
623
- except TokenException:
624
- raise QuantplayTokenException("Token Expired")
625
-
577
+ except (TokenException, InvalidArgumentException):
578
+ raise
626
579
  except Exception:
627
580
  traceback.print_exc()
628
581
  raise RetryableException("Failed to fetch user profile")
@@ -134,7 +134,7 @@ class FivePaisa(Broker):
134
134
  stop_max_attempt_number=3,
135
135
  retry_on_exception=retry_exception,
136
136
  )
137
- def get_ltp(self, exchange, tradingsymbol: str) -> float:
137
+ def ltp(self, exchange, tradingsymbol: str) -> float:
138
138
  tradingsymbol = self.get_symbol(tradingsymbol, exchange)
139
139
  exchange_name = self.get_exchange(exchange)
140
140
  exchange_type = self.get_exchange_type(exchange)
@@ -413,6 +413,8 @@ class FivePaisa(Broker):
413
413
  .then(-pl.col("pnl"))
414
414
  .otherwise(pl.col("pnl"))
415
415
  )
416
+ else:
417
+ orders = orders.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
416
418
 
417
419
  return orders
418
420
 
@@ -456,7 +456,7 @@ class Broker:
456
456
 
457
457
  def live_data(self, exchange, tradingsymbol):
458
458
  return {
459
- "ltp": self.get_ltp(exchange, tradingsymbol),
459
+ "ltp": self.ltp(exchange, tradingsymbol),
460
460
  "upper_circuit": None,
461
461
  "lower_circuit": None,
462
462
  }
@@ -606,7 +606,7 @@ class Broker:
606
606
  exchange = stoploss_order["exchange"]
607
607
  tradingsymbol = stoploss_order["tradingsymbol"]
608
608
 
609
- ltp = self.get_ltp(exchange, tradingsymbol)
609
+ ltp = self.ltp(exchange, tradingsymbol)
610
610
  stoploss_order["order_type"] = "LIMIT"
611
611
  stoploss_order["price"] = self.round_to_tick(ltp)
612
612
  stoploss_order["trigger_price"] = None
@@ -713,7 +713,7 @@ class Broker:
713
713
  positions = positions.with_columns(
714
714
  pl.struct(["exchange", "tradingsymbol"])
715
715
  .map_elements(
716
- lambda x: int(self.get_ltp(x["exchange"], x["tradingsymbol"])),
716
+ lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
717
717
  return_dtype=pl.Float64,
718
718
  )
719
719
  .alias("price")
@@ -895,7 +895,7 @@ class Broker:
895
895
  elif "SENSEX" in symbol and exchange == "BFO":
896
896
  return self.underlying_config("SENSEX")["max_lots"]
897
897
  elif exchange == "NSE":
898
- max_qty = int(500000 / self.get_ltp(exchange, symbol))
898
+ max_qty = int(500000 / self.ltp(exchange, symbol))
899
899
  if max_qty == 0:
900
900
  return 1
901
901
  return max_qty
@@ -995,9 +995,7 @@ class Broker:
995
995
  all_symbols = list(orders.exchange_symbol.unique())
996
996
  symbol_ltp = {}
997
997
  for exchange_symbol in all_symbols:
998
- ltp = self.get_ltp(
999
- exchange_symbol.split(":")[0], exchange_symbol.split(":")[1]
1000
- )
998
+ ltp = self.ltp(exchange_symbol.split(":")[0], exchange_symbol.split(":")[1])
1001
999
  symbol_ltp[exchange_symbol] = ltp
1002
1000
  orders.loc[:, "ltp"] = orders["exchange_symbol"].map(symbol_ltp)
1003
1001
 
@@ -1130,7 +1128,7 @@ class Broker:
1130
1128
  else:
1131
1129
  modification_count[order_id] += 1
1132
1130
 
1133
- ltp = self.get_ltp(order["exchange"], order["tradingsymbol"])
1131
+ ltp = self.ltp(order["exchange"], order["tradingsymbol"])
1134
1132
 
1135
1133
  market_protection = 0.02
1136
1134
  if modification_count[order_id] > 5:
@@ -1237,7 +1235,7 @@ class Broker:
1237
1235
  """
1238
1236
  raise NotImplementedError
1239
1237
 
1240
- def get_ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
1238
+ def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
1241
1239
  """_summary_
1242
1240
 
1243
1241
  Args:
@@ -1284,3 +1282,12 @@ class Broker:
1284
1282
  UserBrokerProfile: contains exchange user_id full_name and email
1285
1283
  """
1286
1284
  raise NotImplementedError
1285
+
1286
+ def margins(self) -> Dict[str, float]:
1287
+ """Returns User Margin Summary
1288
+
1289
+ Raises:
1290
+ NotImplementedError: if Function Not Implemented by Sub class
1291
+
1292
+ """
1293
+ raise NotImplementedError
@@ -19,6 +19,7 @@ import binascii
19
19
  from quantplay.utils.pickle_utils import InstrumentData
20
20
  from quantplay.exception.exceptions import (
21
21
  QuantplayOrderPlacementException,
22
+ TokenException,
22
23
  retry_exception,
23
24
  RetryableException,
24
25
  )
@@ -88,7 +89,7 @@ class Motilal(Broker):
88
89
  self.verify_otp_url = (
89
90
  "https://{}openapi.motilaloswal.com/rest/login/v3/verifyotp".format(uat)
90
91
  )
91
- self.ltp_utl = (
92
+ self.ltp_url = (
92
93
  "https://{}openapi.motilaloswal.com/rest/report/v1/getltpdata".format(uat)
93
94
  )
94
95
  self.place_order_url = (
@@ -133,8 +134,7 @@ class Motilal(Broker):
133
134
  else:
134
135
  raise Exception("Missing Arguments")
135
136
 
136
- profile = self.profile()
137
- self.user_id = profile["user_id"]
137
+ self.user_id = self.headers["vendorinfo"]
138
138
  except binascii.Error:
139
139
  raise InvalidArgumentException("Invalid TOTP key provided")
140
140
  except InvalidArgumentException:
@@ -263,12 +263,7 @@ class Motilal(Broker):
263
263
  Constants.logger.info(response)
264
264
  return response
265
265
 
266
- @retry(
267
- wait_exponential_multiplier=3000,
268
- wait_exponential_max=10000,
269
- stop_max_attempt_number=3,
270
- )
271
- def get_ltp(self, exchange, tradingsymbol: str) -> float:
266
+ def ltp(self, exchange, tradingsymbol: str) -> float:
272
267
  tradingsymbol = self.get_symbol(tradingsymbol)
273
268
  token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
274
269
  exchange = self.get_exchange(exchange)
@@ -279,9 +274,7 @@ class Motilal(Broker):
279
274
  }
280
275
 
281
276
  Constants.logger.info("[GET_LTP_REQUEST] response {}".format(data))
282
- response = requests.post(
283
- self.ltp_utl, headers=self.headers, data=json.dumps(data)
284
- )
277
+ response = self.__post_request(self.ltp_url, json.dumps(data))
285
278
  Constants.logger.info("[GET_LTP_RESPONSE] response {}".format(response.json()))
286
279
  return response.json()["data"]["ltp"] / 100.0
287
280
 
@@ -383,36 +376,15 @@ class Motilal(Broker):
383
376
  )
384
377
  Constants.logger.error(exception_message)
385
378
 
386
- def get_profile(self):
387
- response = requests.post(
388
- self.get_profile_url,
389
- headers=self.headers,
390
- data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
391
- ).json()
392
- if response["status"] == "ERROR":
393
- raise Exception(response["message"])
394
-
395
- return response["data"]
396
-
397
- @retry(
398
- wait_exponential_multiplier=3000,
399
- wait_exponential_max=10000,
400
- stop_max_attempt_number=3,
401
- )
402
379
  def margins(self):
403
- response = requests.post(
404
- self.margin_summary_url,
405
- headers=self.headers,
406
- data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
407
- ).json()
408
- if response["status"] == "ERROR":
409
- raise Exception(response["message"])
380
+ response = self.__post_request(self.margin_summary_url, {})
410
381
  margin_summary = response["data"]
411
382
 
412
- margins = {"margin_used": 0, "margin_available": 0}
383
+ margins = {"margin_used": 0.0, "margin_available": 0.0}
413
384
  for margin_particular in margin_summary:
414
385
  if margin_particular["srno"] in [103]:
415
386
  margins["margin_available"] += margin_particular["amount"]
387
+
416
388
  if margin_particular["srno"] in [301, 321, 340, 360]:
417
389
  margins["margin_used"] += margin_particular["amount"]
418
390
 
@@ -554,14 +526,35 @@ class Motilal(Broker):
554
526
 
555
527
  return response
556
528
 
557
- def profile(self) -> UserBrokerProfileResponse:
529
+ @retry(
530
+ wait_exponential_multiplier=3000,
531
+ wait_exponential_max=10000,
532
+ stop_max_attempt_number=3,
533
+ retry_on_exception=retry_exception,
534
+ )
535
+ def __post_request(self, url, data):
558
536
  api_response = requests.post(
559
- self.get_profile_url,
537
+ url,
560
538
  headers=self.headers,
561
539
  data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
562
540
  ).json()
563
- if api_response["status"] == "ERROR":
564
- raise Exception(api_response["message"])
541
+
542
+ if api_response["status"] == "ERROR" and api_response["errorcode"] in [
543
+ "MO8001",
544
+ "MO8002",
545
+ "MO8003",
546
+ "MO8050",
547
+ "MO8051",
548
+ "MO8052",
549
+ ]:
550
+ raise TokenException("Motilal token expired")
551
+ elif api_response["status"] == "ERROR":
552
+ raise RetryableException(api_response["message"])
553
+
554
+ return api_response
555
+
556
+ def profile(self) -> UserBrokerProfileResponse:
557
+ api_response = self.__post_request(self.get_profile_url, {})
565
558
 
566
559
  api_response = api_response["data"]
567
560
  response: UserBrokerProfileResponse = {
@@ -578,7 +571,7 @@ class Motilal(Broker):
578
571
  stop_max_attempt_number=3,
579
572
  )
580
573
  def holdings(self):
581
- response = (requests.post(self.holdings_url, headers=self.headers)).json()
574
+ response = self.__post_request(self.holdings_url, {})
582
575
  if response["status"] == "ERROR":
583
576
  Constants.logger.info(
584
577
  "Error while fetching order book [{}]".format(response["message"])
@@ -605,7 +598,7 @@ class Motilal(Broker):
605
598
  holdings_df = holdings_df.with_columns(
606
599
  pl.struct(["exchange", "tradingsymbol"])
607
600
  .map_elements(
608
- lambda x: int(self.get_ltp(x["exchange"], x["tradingsymbol"])),
601
+ lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
609
602
  return_dtype=pl.Float64,
610
603
  )
611
604
  .alias("price")
@@ -624,13 +617,8 @@ class Motilal(Broker):
624
617
 
625
618
  return holdings_df[list(self.holidings_schema.keys())].cast(self.holidings_schema)
626
619
 
627
- @retry(
628
- wait_exponential_multiplier=3000,
629
- wait_exponential_max=10000,
630
- stop_max_attempt_number=3,
631
- )
632
620
  def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
633
- response = (requests.post(self.positions_url, headers=self.headers)).json()
621
+ response = self.__post_request(self.positions_url, {})
634
622
  if response["status"] == "ERROR":
635
623
  Constants.logger.info(
636
624
  "Error while fetching positions [{}]".format(response["message"])
@@ -699,7 +687,7 @@ class Motilal(Broker):
699
687
  )
700
688
 
701
689
  def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
702
- response = (requests.post(self.order_book_url, headers=self.headers)).json()
690
+ response = self.__post_request(self.order_book_url, {})
703
691
  if response["status"] == "ERROR":
704
692
  Constants.logger.info(
705
693
  "Error while fetching order book [{}]".format(response["message"])
@@ -742,6 +730,8 @@ class Motilal(Broker):
742
730
  orders_df = orders_df.join(
743
731
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
744
732
  )
733
+ else:
734
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
745
735
 
746
736
  orders_df = orders_df.with_columns(
747
737
  pl.when(pl.col("product") == "VALUEPLUS")
@@ -75,6 +75,8 @@ class Noren(Broker):
75
75
  def get_symbol(self, symbol: str, exchange: ExchangeType | None = None):
76
76
  if symbol not in self.quantplay_symbol_map:
77
77
  return symbol
78
+ if exchange == "NSE" and "-EQ" not in symbol:
79
+ return f"{symbol}-EQ"
78
80
 
79
81
  return self.quantplay_symbol_map[symbol]
80
82
 
@@ -246,8 +248,8 @@ class Noren(Broker):
246
248
  wait_exponential_max=10000,
247
249
  stop_max_attempt_number=3,
248
250
  )
249
- def get_ltp(self, exchange, tradingsymbol):
250
- tradingsymbol = self.get_symbol(tradingsymbol)
251
+ def ltp(self, exchange, tradingsymbol):
252
+ tradingsymbol = self.get_symbol(tradingsymbol, exchange)
251
253
 
252
254
  token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
253
255
  quote = self.api.get_quotes(exchange, str(token))
@@ -387,7 +389,7 @@ class Noren(Broker):
387
389
  holdings_df = holdings_df.with_columns(
388
390
  pl.struct(["exchange", "tradingsymbol"])
389
391
  .map_elements(
390
- lambda x: int(self.get_ltp(x["exchange"], x["tradingsymbol"])),
392
+ lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
391
393
  return_dtype=pl.Float64,
392
394
  )
393
395
  .alias("price")
@@ -523,6 +525,8 @@ class Noren(Broker):
523
525
  orders_df = orders_df.join(
524
526
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
525
527
  )
528
+ else:
529
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
526
530
 
527
531
  orders_df = orders_df.with_columns(pl.lit(None).alias("variety"))
528
532
 
@@ -126,7 +126,7 @@ class Upstox(Broker):
126
126
  stop_max_attempt_number=3,
127
127
  retry_on_exception=retry_exception,
128
128
  )
129
- def get_ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
129
+ def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
130
130
  api_instance = upstox_client.MarketQuoteApi(self.api_client)
131
131
 
132
132
  ltp = 0
@@ -315,7 +315,7 @@ class Upstox(Broker):
315
315
  holdings_df = holdings_df.with_columns(
316
316
  pl.struct(["exchange", "tradingsymbol"])
317
317
  .map_elements(
318
- lambda x: int(self.get_ltp(x["exchange"], x["tradingsymbol"])),
318
+ lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
319
319
  return_dtype=pl.Float64,
320
320
  )
321
321
  .alias("price")
@@ -465,6 +465,8 @@ class Upstox(Broker):
465
465
  orders_df = orders_df.join(
466
466
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
467
467
  )
468
+ else:
469
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
468
470
  orders_df = orders_df.rename({"placed_by": "user_id"})
469
471
 
470
472
  orders_df = orders_df.with_columns(
@@ -275,6 +275,8 @@ class XTS(Broker):
275
275
  orders_df = orders_df.join(
276
276
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
277
277
  )
278
+ else:
279
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
278
280
 
279
281
  orders_df = orders_df.with_columns(
280
282
  pl.col("filled_quantity").cast(pl.Int64).alias("filled_quantity"),
@@ -538,7 +540,7 @@ class XTS(Broker):
538
540
 
539
541
  return exchange_code_map[exchange]
540
542
 
541
- def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
543
+ def ltp(self, exchange=None, tradingsymbol=None) -> float:
542
544
  exchange_code = self.get_exchange_code(exchange)
543
545
  exchange_token = self.symbol_data[f"{exchange}:{tradingsymbol}"]["exchange_token"]
544
546
 
@@ -132,8 +132,9 @@ class Zerodha(Broker):
132
132
  wait_exponential_multiplier=3000,
133
133
  wait_exponential_max=10000,
134
134
  stop_max_attempt_number=3,
135
+ retry_on_exception=retry_exception,
135
136
  )
136
- def get_ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
137
+ def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
137
138
  try:
138
139
  key = "{}:".format(exchange) + tradingsymbol
139
140
  response = self.wrapper.ltp([key])
@@ -149,11 +150,13 @@ class Zerodha(Broker):
149
150
  )
150
151
 
151
152
  return api_response[key]["last_price"]
153
+ except TokenException:
154
+ raise QuantplayTokenException("Zerodha token expired")
152
155
  except Exception as e:
153
156
  exception_message = "GetLtp call failed for [{}] with error [{}]".format(
154
157
  tradingsymbol, str(e)
155
158
  )
156
- raise InvalidArgumentException(exception_message)
159
+ raise RetryableException(exception_message)
157
160
 
158
161
  @retry(
159
162
  wait_exponential_multiplier=3000,
@@ -211,7 +214,7 @@ class Zerodha(Broker):
211
214
  exchange = order["exchange"]
212
215
  tradingsymbol = order["tradingsymbol"]
213
216
 
214
- return self.get_ltp(exchange, tradingsymbol)
217
+ return self.ltp(exchange, tradingsymbol)
215
218
 
216
219
  @retry(
217
220
  wait_exponential_multiplier=3000,
@@ -492,6 +495,8 @@ class Zerodha(Broker):
492
495
  orders_df = orders_df.join(
493
496
  positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
494
497
  )
498
+ else:
499
+ orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
495
500
 
496
501
  orders_df = orders_df.with_columns(
497
502
  (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.25
3
+ Version: 2.0.27
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -21,7 +21,7 @@ requirements = [
21
21
  setup(
22
22
  name="quantplay",
23
23
  long_description=Path("README.md").read_text(),
24
- version="2.0.25",
24
+ version="2.0.27",
25
25
  setup_requires=["pytest-runner"],
26
26
  install_requires=requirements,
27
27
  tests_require=[],
File without changes
File without changes
File without changes
File without changes
File without changes