quantplay 2.0.7__tar.gz → 2.0.9__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 (70) hide show
  1. {quantplay-2.0.7 → quantplay-2.0.9}/PKG-INFO +1 -1
  2. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/noren.py +91 -64
  3. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/xts.py +4 -0
  4. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay.egg-info/PKG-INFO +1 -1
  5. {quantplay-2.0.7 → quantplay-2.0.9}/setup.py +1 -1
  6. {quantplay-2.0.7 → quantplay-2.0.9}/README.md +0 -0
  7. {quantplay-2.0.7 → quantplay-2.0.9}/pyproject.toml +0 -0
  8. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/__init__.py +0 -0
  9. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/__init__.py +0 -0
  10. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/aliceblue.py +0 -0
  11. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/angelone.py +0 -0
  12. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/auto_login/__init__.py +0 -0
  13. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/auto_login/aliceblue.py +0 -0
  14. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/finvasia_utils/__init__.py +0 -0
  15. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
  16. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/five_paisa.py +0 -0
  17. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/flattrade.py +0 -0
  18. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/ft_utils/__init__.py +0 -0
  19. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
  20. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/ft_utils/ft_noren.py +0 -0
  21. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/generics/__init__.py +0 -0
  22. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/generics/broker.py +0 -0
  23. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/iifl_xts.py +0 -0
  24. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/kite_utils.py +0 -0
  25. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/motilal.py +0 -0
  26. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/shoonya.py +0 -0
  27. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/uplink/__init__.py +0 -0
  28. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/uplink/uplink_utils.py +0 -0
  29. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/upstox.py +0 -0
  30. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/xts_utils/Connect.py +0 -0
  31. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/xts_utils/Exception.py +0 -0
  32. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
  33. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/xts_utils/__init__.py +0 -0
  34. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/broker/zerodha.py +0 -0
  35. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/exception/__init__.py +0 -0
  36. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/exception/exceptions.py +0 -0
  37. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/model/__init__.py +0 -0
  38. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/model/broker/__init__.py +0 -0
  39. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/model/broker/generics.py +0 -0
  40. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/py.typed +0 -0
  41. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/__init__.py +0 -0
  42. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/equities/__init__.py +0 -0
  43. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/equities/intraday/__init__.py +0 -0
  44. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/equities/overnight/__init__.py +0 -0
  45. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/futures/__init__.py +0 -0
  46. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/futures/overnight/__init__.py +0 -0
  47. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/options/__init__.py +0 -0
  48. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/options/intraday/__init__.py +0 -0
  49. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/options/intraday/ladder.py +0 -0
  50. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/options/intraday/musk.py +0 -0
  51. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/strategies/options/intraday/short_straddle.py +0 -0
  52. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/__init__.py +0 -0
  53. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/constant.py +0 -0
  54. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/exchange.py +0 -0
  55. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/number_utils.py +0 -0
  56. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/pickle_utils.py +0 -0
  57. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/utils/selenium_utils.py +0 -0
  58. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/wrapper/__init__.py +0 -0
  59. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/wrapper/aws/__init__.py +0 -0
  60. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay/wrapper/aws/s3.py +0 -0
  61. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay.egg-info/SOURCES.txt +0 -0
  62. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay.egg-info/dependency_links.txt +0 -0
  63. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay.egg-info/requires.txt +0 -0
  64. {quantplay-2.0.7 → quantplay-2.0.9}/quantplay.egg-info/top_level.txt +0 -0
  65. {quantplay-2.0.7 → quantplay-2.0.9}/setup.cfg +0 -0
  66. {quantplay-2.0.7 → quantplay-2.0.9}/tests/__init__.py +0 -0
  67. {quantplay-2.0.7 → quantplay-2.0.9}/tests/conftest.py +0 -0
  68. {quantplay-2.0.7 → quantplay-2.0.9}/tests/wrapper/__init__.py +0 -0
  69. {quantplay-2.0.7 → quantplay-2.0.9}/tests/wrapper/aws/__init__.py +0 -0
  70. {quantplay-2.0.7 → quantplay-2.0.9}/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.7
3
+ Version: 2.0.9
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -1,12 +1,14 @@
1
1
  import json
2
2
  from queue import Queue
3
3
  import traceback
4
- from typing import Dict
4
+ from typing import Callable, Dict, Literal
5
5
 
6
6
  import polars as pl
7
7
  from retrying import retry
8
8
  from json.decoder import JSONDecodeError
9
9
 
10
+ from quantplay.broker.finvasia_utils.fa_noren import FA_NorenApi
11
+ from quantplay.broker.ft_utils.ft_noren import FT_NorenApi
10
12
  from quantplay.broker.generics.broker import Broker
11
13
  from quantplay.exception.exceptions import (
12
14
  InvalidArgumentException,
@@ -47,7 +49,9 @@ class Noren(Broker):
47
49
  self.order_type_sl = "SL-LMT"
48
50
  self.trigger_pending_status = "TRIGGER_PENDING"
49
51
 
50
- def set_attributes(self, response):
52
+ self.api: FA_NorenApi | FT_NorenApi
53
+
54
+ def set_attributes(self, response: Dict[str, str | None]) -> None:
51
55
  self.email = response["email"]
52
56
  self.user_id = response["actid"]
53
57
  self.full_name = response["uname"]
@@ -72,9 +76,10 @@ class Noren(Broker):
72
76
  def get_symbol(self, symbol, exchange=None):
73
77
  if symbol not in self.quantplay_symbol_map:
74
78
  return symbol
79
+
75
80
  return self.quantplay_symbol_map[symbol]
76
81
 
77
- def get_transaction_type(self, transaction_type):
82
+ def get_transaction_type(self, transaction_type: Literal["BUY", "SELL"]):
78
83
  if transaction_type == "BUY":
79
84
  return "B"
80
85
  elif transaction_type == "SELL":
@@ -87,16 +92,19 @@ class Noren(Broker):
87
92
  def get_order_type(self, order_type):
88
93
  if order_type == OrderType.market:
89
94
  return "MKT"
95
+
90
96
  elif order_type == OrderType.sl:
91
97
  return "SL-LMT"
98
+
92
99
  elif order_type == OrderType.slm:
93
100
  return "SL-MKT"
101
+
94
102
  elif order_type == OrderType.limit:
95
103
  return "LMT"
96
104
 
97
105
  return order_type
98
106
 
99
- def get_product(self, product):
107
+ def get_product(self, product: str):
100
108
  if product == "NRML":
101
109
  return "M"
102
110
  elif product == "CNC":
@@ -106,9 +114,7 @@ class Noren(Broker):
106
114
  elif product in ["M", "C", "I"]:
107
115
  return product
108
116
 
109
- raise InvalidArgumentException(
110
- "Product {} not supported for trading".format(product)
111
- )
117
+ raise InvalidArgumentException(f"Product {product} not supported for trading")
112
118
 
113
119
  def event_handler_order_update(self, order):
114
120
  try:
@@ -210,7 +216,7 @@ class Noren(Broker):
210
216
  "remarks": tag,
211
217
  }
212
218
  Constants.logger.info("[PLACING_ORDER] {}".format(json.dumps(data)))
213
- response = self.api.place_order( # type:ignore
219
+ response = self.api.place_order(
214
220
  buy_or_sell=noren_transaction_type,
215
221
  product_type=noren_product,
216
222
  exchange=exchange,
@@ -227,10 +233,12 @@ class Noren(Broker):
227
233
  Constants.logger.info(
228
234
  "[PLACE_ORDER_RESPONSE] {} input {}".format(response, json.dumps(data))
229
235
  )
230
- if "norenordno" in response:
236
+
237
+ if response is not None and "norenordno" in response:
231
238
  return response["norenordno"]
232
239
  else:
233
240
  raise Exception(response)
241
+
234
242
  except Exception as e:
235
243
  traceback.print_exc()
236
244
  exception_message = "Order placement failed with error [{}]".format(str(e))
@@ -248,12 +256,21 @@ class Noren(Broker):
248
256
  tradingsymbol = self.get_symbol(tradingsymbol)
249
257
 
250
258
  token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
251
- return float(self.api.get_quotes(exchange, str(token))["lp"]) # type:ignore
259
+ quote = self.api.get_quotes(exchange, str(token))
260
+
261
+ if quote is None:
262
+ raise BrokerException("Response from Broker for LTP is Invalid")
263
+
264
+ return float(quote["lp"])
252
265
 
253
266
  def live_data(self, exchange, tradingsymbol):
254
267
  tradingsymbol = self.get_symbol(tradingsymbol)
255
268
  token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
256
- data = self.api.get_quotes(exchange, str(token)) # type:ignore
269
+ data = self.api.get_quotes(exchange, str(token))
270
+
271
+ if data is None:
272
+ raise BrokerException("Response from Broker for Market Data is Invalid")
273
+
257
274
  return {
258
275
  "ltp": float(data["lp"]),
259
276
  "upper_circuit": float(data["uc"]),
@@ -261,15 +278,20 @@ class Noren(Broker):
261
278
  }
262
279
 
263
280
  def order_history(self, order_id):
264
- order_history = self.api.single_order_history(order_id) # type:ignore
281
+ order_history = self.api.single_order_history(order_id)
282
+
283
+ if order_history is None:
284
+ raise BrokerException("Response from Broker for Order History is Invalid")
285
+
265
286
  order_details = order_history[0]
266
287
 
267
- data = {}
268
- data["order_id"] = order_id
269
- data["order_type"] = order_details["prctyp"]
270
- data["exchange"] = order_details["exch"]
271
- data["quantity"] = order_details["qty"]
272
- data["tradingsymbol"] = order_details["tsym"]
288
+ data = {
289
+ "order_id": order_id,
290
+ "order_type": order_details["prctyp"],
291
+ "exchange": order_details["exch"],
292
+ "quantity": order_details["qty"],
293
+ "tradingsymbol": order_details["tsym"],
294
+ }
273
295
 
274
296
  return data
275
297
 
@@ -289,6 +311,7 @@ class Noren(Broker):
289
311
 
290
312
  if "order_type" not in order_to_modify:
291
313
  order_to_modify["order_type"] = existing_details["order_type"]
314
+
292
315
  if "quantity" not in order_to_modify:
293
316
  order_to_modify["quantity"] = existing_details["quantity"]
294
317
 
@@ -308,33 +331,39 @@ class Noren(Broker):
308
331
  order_to_modify["order_id"], response
309
332
  )
310
333
  )
334
+
335
+ if response is None:
336
+ raise Exception("Response is None")
337
+
311
338
  return response
339
+
312
340
  except Exception as e:
313
- exception_message = (
314
- "OrderModificationFailed for {} failed with exception {}".format(
315
- order_to_modify["order_id"], e
316
- )
317
- )
318
- Constants.logger.error("{}".format(exception_message))
341
+ exception_message = f"OrderModificationFailed for {order_to_modify['order_id']} failed with exception {e}"
342
+ Constants.logger.error(f"{exception_message}")
319
343
  raise e
320
344
 
321
345
  @timeit(MetricName="Finvasia:cancel_order")
322
346
  def cancel_order(self, order_id, variety=None):
323
- self.api.cancel_order(order_id) # type:ignore
347
+ self.api.cancel_order(order_id)
324
348
 
325
349
  def stream_order_data(self):
326
- self.api.start_websocket( # type:ignore
327
- order_update_callback=self.event_handler_order_update
328
- )
350
+ self.api.start_websocket(order_update_callback=self.event_handler_order_update)
329
351
 
330
352
  @timeit(MetricName="Finvasia:profile")
331
353
  def profile(self) -> UserBrokerProfileResponse:
354
+ if self.user_id is None:
355
+ raise Exception("User ID is unknown")
356
+
332
357
  response: UserBrokerProfileResponse = {
333
- "user_id": self.user_id, # type:ignore
334
- "full_name": self.full_name,
335
- "email": self.email,
358
+ "user_id": self.user_id,
336
359
  }
337
360
 
361
+ if self.full_name is not None:
362
+ response["full_name"] = self.full_name
363
+
364
+ if self.email is not None:
365
+ response["email"] = self.email
366
+
338
367
  return response
339
368
 
340
369
  @timeit(MetricName="Noren:holdings")
@@ -344,7 +373,7 @@ class Noren(Broker):
344
373
  stop_max_attempt_number=3,
345
374
  )
346
375
  def holdings(self):
347
- holdings = self.api.get_holdings() # type:ignore
376
+ holdings = self.api.get_holdings()
348
377
  if holdings is None or len(holdings) == 0:
349
378
  return pl.DataFrame(schema=self.holidings_schema)
350
379
 
@@ -387,27 +416,13 @@ class Noren(Broker):
387
416
  return holdings_df[list(self.holidings_schema.keys())].cast(self.holidings_schema)
388
417
 
389
418
  @timeit(MetricName="Finvasia:positions")
390
- @retry(
391
- wait_exponential_multiplier=3000,
392
- wait_exponential_max=10000,
393
- stop_max_attempt_number=3,
394
- retry_on_exception=retry_exception,
395
- )
396
419
  def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
397
- positions_df = pl.DataFrame()
398
- try:
399
- positions = self.api.get_positions() # type:ignore
400
- if positions is None or len(positions) == 0:
401
- return pl.DataFrame(schema=self.positions_schema)
402
- except BrokerException:
403
- raise
404
- except JSONDecodeError:
405
- raise BrokerException("Broker failed to send positions")
406
- except Exception:
407
- raise
420
+ positions = self.invoke_noren_api(self.api.get_positions)
408
421
 
409
- positions_df = pl.DataFrame(positions)
422
+ if positions is None or len(positions) == 0:
423
+ return pl.DataFrame(schema=self.positions_schema)
410
424
 
425
+ positions_df = pl.DataFrame(positions)
411
426
  positions_df = positions_df.rename(
412
427
  {
413
428
  "tsym": "tradingsymbol",
@@ -488,16 +503,9 @@ class Noren(Broker):
488
503
 
489
504
  @timeit(MetricName="Finvasia:orders")
490
505
  def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
491
- try:
492
- orders = self.api.get_order_book() # type:ignore
493
- if orders is None or len(orders) == 0:
494
- return pl.DataFrame(schema=self.orders_schema)
495
- except BrokerException:
496
- raise
497
- except JSONDecodeError:
498
- raise BrokerException("Broker failed to send orders")
499
- except Exception:
500
- raise
506
+ orders = self.invoke_noren_api(self.api.get_order_book)
507
+ if orders is None or len(orders) == 0:
508
+ return pl.DataFrame(schema=self.orders_schema)
501
509
 
502
510
  orders_df = pl.DataFrame(orders)
503
511
 
@@ -632,6 +640,26 @@ class Noren(Broker):
632
640
 
633
641
  return orders_df[list(self.orders_schema.keys())].cast(self.orders_schema)
634
642
 
643
+ @retry(
644
+ wait_exponential_multiplier=3000,
645
+ wait_exponential_max=10000,
646
+ stop_max_attempt_number=3,
647
+ retry_on_exception=retry_exception,
648
+ )
649
+ def invoke_noren_api(self, fn: Callable, *args, **kwargs) -> Dict:
650
+ try:
651
+ response = fn(*args, **kwargs)
652
+ if "stat" in response and "not_ok" == response["stat"].lower():
653
+ raise TokenException(response["emsg"])
654
+ return response
655
+
656
+ except JSONDecodeError:
657
+ raise BrokerException("Failed to data from broker")
658
+
659
+ except Exception:
660
+ traceback.print_exc()
661
+ raise RetryableException("Failed to data from broker")
662
+
635
663
  @timeit(MetricName="Finvasia:positions")
636
664
  @retry(
637
665
  wait_exponential_multiplier=1000,
@@ -640,9 +668,7 @@ class Noren(Broker):
640
668
  retry_on_exception=retry_exception,
641
669
  )
642
670
  def margins(self) -> Dict:
643
- api_margins = self.api.get_limits() # type:ignore
644
- if "stat" in api_margins and "not_ok" == api_margins["stat"].lower():
645
- raise TokenException(api_margins["emsg"])
671
+ api_margins = self.invoke_noren_api(self.api.get_limits)
646
672
 
647
673
  try:
648
674
  collateral = 0
@@ -679,9 +705,10 @@ class Noren(Broker):
679
705
 
680
706
  @timeit(MetricName="Finvasia:account_summary")
681
707
  def account_summary(self):
682
- margins: Dict = self.margins()
683
708
  pnl = 0
709
+ margins: Dict = self.margins()
684
710
  positions = self.positions()
711
+
685
712
  if len(positions) > 0:
686
713
  pnl = positions["pnl"].sum()
687
714
 
@@ -311,6 +311,10 @@ class XTS(Broker):
311
311
  .then(pl.lit("NSE"))
312
312
  .when(pl.col("exchange") == "NSEFO")
313
313
  .then(pl.lit("NFO"))
314
+ .when(pl.col("exchange") == "BSECM")
315
+ .then(pl.lit("BSE"))
316
+ .when(pl.col("exchange") == "BSEFO")
317
+ .then(pl.lit("BFO"))
314
318
  .otherwise(pl.col("exchange"))
315
319
  .alias("exchange"),
316
320
  pl.when(pl.col("status") == "Rejected")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.7
3
+ Version: 2.0.9
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.7",
24
+ version="2.0.9",
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
File without changes