quantplay 2.1.44__tar.gz → 2.1.46__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 (84) hide show
  1. {quantplay-2.1.44 → quantplay-2.1.46}/PKG-INFO +2 -6
  2. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/aliceblue.py +145 -0
  3. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/angelone.py +76 -0
  4. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/ft_utils/flattrade_utils.py +1 -3
  5. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay.egg-info/PKG-INFO +2 -6
  6. {quantplay-2.1.44 → quantplay-2.1.46}/setup.py +1 -1
  7. {quantplay-2.1.44 → quantplay-2.1.46}/README.md +0 -0
  8. {quantplay-2.1.44 → quantplay-2.1.46}/pyproject.toml +0 -0
  9. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/__init__.py +0 -0
  10. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/__init__.py +0 -0
  11. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/auto_login/__init__.py +0 -0
  12. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/auto_login/aliceblue.py +0 -0
  13. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/breeze/__init__.py +0 -0
  14. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/breeze/breeze_utils.py +0 -0
  15. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/broker_factory.py +0 -0
  16. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/dhan.py +0 -0
  17. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/finvasia_utils/__init__.py +0 -0
  18. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
  19. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/five_paisa.py +0 -0
  20. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/flattrade.py +0 -0
  21. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/ft_utils/__init__.py +0 -0
  22. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/ft_utils/ft_noren.py +0 -0
  23. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/generics/__init__.py +0 -0
  24. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/generics/broker.py +0 -0
  25. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/icici_direct.py +0 -0
  26. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/iifl_xts.py +0 -0
  27. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/jainam_xts.py +0 -0
  28. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/kite_utils.py +0 -0
  29. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/kotak.py +0 -0
  30. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/kotak_utils/__init__.py +0 -0
  31. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/kotak_utils/kotak_ws.py +0 -0
  32. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/kotak_utils/kotak_ws_lib.py +0 -0
  33. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/motilal.py +0 -0
  34. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/noren.py +0 -0
  35. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/shoonya.py +0 -0
  36. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/uplink/__init__.py +0 -0
  37. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/uplink/uplink_utils.py +0 -0
  38. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/upstox.py +0 -0
  39. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts.py +0 -0
  40. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils/Connect.py +0 -0
  41. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils/Exception.py +0 -0
  42. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
  43. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils/__init__.py +0 -0
  44. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils_v2/ConnectV2.py +0 -0
  45. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils_v2/InteractiveSocketClientV2.py +0 -0
  46. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/xts_utils_v2/__init__.py +0 -0
  47. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/broker/zerodha.py +0 -0
  48. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/core/__init__.py +0 -0
  49. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/core/strategy.py +0 -0
  50. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/exception/__init__.py +0 -0
  51. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/exception/exceptions.py +0 -0
  52. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/indicator/__init__.py +0 -0
  53. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/indicator/iv.py +0 -0
  54. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/__init__.py +0 -0
  55. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/broker.py +0 -0
  56. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/broker_response.py +0 -0
  57. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/generics.py +0 -0
  58. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/instrument_data.py +0 -0
  59. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/model/order_event.py +0 -0
  60. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/py.typed +0 -0
  61. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/strategy/__init__.py +0 -0
  62. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/strategy/iv_spike.py +0 -0
  63. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/strategy/obuy.py +0 -0
  64. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/__init__.py +0 -0
  65. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/caching.py +0 -0
  66. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/constant.py +0 -0
  67. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/exchange.py +0 -0
  68. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/number_utils.py +0 -0
  69. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/pickle_utils.py +0 -0
  70. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/utils/selenium_utils.py +0 -0
  71. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/wrapper/__init__.py +0 -0
  72. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/wrapper/aws/__init__.py +0 -0
  73. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/wrapper/aws/s3.py +0 -0
  74. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay/wrapper/redis.py +0 -0
  75. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay.egg-info/SOURCES.txt +0 -0
  76. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay.egg-info/dependency_links.txt +0 -0
  77. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay.egg-info/requires.txt +0 -0
  78. {quantplay-2.1.44 → quantplay-2.1.46}/quantplay.egg-info/top_level.txt +0 -0
  79. {quantplay-2.1.44 → quantplay-2.1.46}/setup.cfg +0 -0
  80. {quantplay-2.1.44 → quantplay-2.1.46}/tests/__init__.py +0 -0
  81. {quantplay-2.1.44 → quantplay-2.1.46}/tests/conftest.py +0 -0
  82. {quantplay-2.1.44 → quantplay-2.1.46}/tests/wrapper/__init__.py +0 -0
  83. {quantplay-2.1.44 → quantplay-2.1.46}/tests/wrapper/aws/__init__.py +0 -0
  84. {quantplay-2.1.44 → quantplay-2.1.46}/tests/wrapper/aws/s3_test.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.1.44
3
+ Version: 2.1.46
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -36,10 +36,6 @@ Requires-Dist: python-engineio
36
36
  Requires-Dist: python-socketio
37
37
  Requires-Dist: six
38
38
  Requires-Dist: dhanhq
39
- Dynamic: description
40
- Dynamic: license
41
- Dynamic: requires-dist
42
- Dynamic: summary
43
39
 
44
40
  # Quantplay Alpha playground
45
41
 
@@ -1,6 +1,9 @@
1
1
  import codecs
2
2
  import copy
3
+ import json
3
4
  import pickle
5
+ import threading
6
+ import time
4
7
  import traceback
5
8
  from queue import Queue
6
9
  from typing import Any, Literal
@@ -10,7 +13,9 @@ from pya3 import Aliceblue as Alice
10
13
  from pya3 import OrderType as AliceOrderType
11
14
  from pya3 import ProductType as AliceProductType
12
15
  from pya3 import TransactionType as AliceTransactionType
16
+ import requests
13
17
  from retrying import retry # type: ignore
18
+ import websocket # type: ignore
14
19
 
15
20
  from quantplay.broker.generics.broker import Broker
16
21
  from quantplay.exception.exceptions import (
@@ -40,6 +45,9 @@ logger = Constants.logger
40
45
 
41
46
 
42
47
  class Aliceblue(Broker):
48
+ websocket_url = "wss://ant.aliceblueonline.com/order-notify/websocket"
49
+ create_websocket_url = "https://ant.aliceblueonline.com/order-notify/ws/createWsToken"
50
+
43
51
  @retry(
44
52
  wait_exponential_multiplier=3000,
45
53
  wait_exponential_max=10000,
@@ -637,3 +645,140 @@ class Aliceblue(Broker):
637
645
  except Exception:
638
646
  traceback.print_exc()
639
647
  raise RetryableException("Failed to fetch data from Aliceblue")
648
+
649
+ def create_websocket_token(self):
650
+ user_token = self.alice.get_session_id()
651
+ session_ID = user_token["sessionID"]
652
+ headers = {
653
+ "Authorization": "Bearer Token" + " " + session_ID,
654
+ "Content-Type": "application/json",
655
+ }
656
+ response = requests.request("GET", self.create_websocket_url, headers=headers)
657
+ parse_data = response.json()
658
+ websocket_Token = parse_data["result"][0]["orderToken"]
659
+ return websocket_Token
660
+
661
+ def handle_order_update(self, order: dict[str, str]):
662
+ try:
663
+ out_order: OrderUpdateEvent = {
664
+ "placed_by": order["remarks"],
665
+ "tag": order["remarks"],
666
+ "order_id": order["norenordno"],
667
+ "exchange": order["exch"],
668
+ "tradingsymbol": order["tsym"],
669
+ "status": order["status"],
670
+ "order_type": order["prctyp"],
671
+ "price": float(order["prc"]),
672
+ "exchange_order_id": order["norenordno"],
673
+ "transaction_type": order["trantype"],
674
+ "product": order["pcode"],
675
+ "quantity": order["qty"],
676
+ } # type: ignore
677
+
678
+ if out_order["exchange"] == "NSE":
679
+ out_order["tradingsymbol"] = out_order["tradingsymbol"].replace("-EQ", "")
680
+
681
+ elif out_order["exchange"] in ["NFO", "MCX"]:
682
+ out_order["tradingsymbol"] = self.broker_symbol_map[
683
+ out_order["tradingsymbol"]
684
+ ]
685
+
686
+ if order["prctyp"] == "LMT":
687
+ out_order["order_type"] = "LIMIT"
688
+ elif order["prctyp"] == "MKT":
689
+ out_order["order_type"] = "MARKET"
690
+ elif order["prctyp"] == "SL-LMT":
691
+ out_order["order_type"] = "SL"
692
+
693
+ if order["pcode"] == "M":
694
+ out_order["product"] = "NRML"
695
+ elif order["pcode"] == "C":
696
+ out_order["product"] = "CNC"
697
+ elif order["pcode"] == "I":
698
+ out_order["product"] = "MIS"
699
+
700
+ if order["trantype"] == "S":
701
+ out_order["transaction_type"] = "SELL"
702
+ elif order["trantype"] == "B":
703
+ out_order["transaction_type"] = "BUY"
704
+ else:
705
+ logger.error(
706
+ "[UNKNOW_VALUE] finvasia transaction type {} not supported".format(
707
+ order["trantype"]
708
+ )
709
+ )
710
+
711
+ if "trgprc" in order:
712
+ out_order["trigger_price"] = float(order["trgprc"])
713
+ else:
714
+ out_order["trigger_price"] = None
715
+
716
+ if order["status"] == "TRIGGER_PENDING":
717
+ out_order["status"] = "TRIGGER PENDING"
718
+ elif order["status"] == "CANCELED":
719
+ out_order["status"] = "CANCELLED"
720
+
721
+ logger.info(f"[ORDER_FEED] {order}")
722
+ if self.order_updates:
723
+ self.order_updates.put(out_order)
724
+
725
+ except Exception as e:
726
+ logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
727
+ traceback.print_exc()
728
+
729
+ def start_order_websocket(self):
730
+ ws_token = self.create_websocket_token()
731
+ print(f"{self.user_id}: WebSocket connection established.")
732
+ headers = {"Content-Type": "application/json"}
733
+ payload = {"orderToken": ws_token, "userId": self.user_id}
734
+ session_data = json.dumps(payload)
735
+
736
+ def on_message(ws: websocket.WebSocket, message: Any):
737
+ order = json.loads(message)
738
+
739
+ if order["t"] == "om":
740
+ self.handle_order_update(order)
741
+
742
+ def on_error(ws: websocket.WebSocket, error: Any):
743
+ if type(ws) is not websocket.WebSocketApp: # type: ignore
744
+ error = ws
745
+
746
+ logger.error(f"{self.user_id}: WebSocket Closed. Reason: {error}")
747
+
748
+ def on_close(ws: websocket.WebSocket, close_status_code: int, close_msg: str):
749
+ logger.error(
750
+ f"{self.user_id}: WebSocket Closed. Status code: {close_status_code}, Reason: {close_msg}"
751
+ )
752
+
753
+ def on_open(ws: websocket.WebSocket):
754
+ print(f"{self.user_id}: WebSocket Connection Opened")
755
+ ws.send(session_data)
756
+ threading.Thread(
757
+ target=heart_beat_connection, args=(ws,), daemon=True
758
+ ).start()
759
+
760
+ def heart_beat_connection(ws: websocket.WebSocket):
761
+ while True:
762
+ try:
763
+ payload = {"heartbeat": "h", "userId": self.user_id}
764
+ hearbeat_data = json.dumps(payload)
765
+ ws.send(hearbeat_data)
766
+ time.sleep(55)
767
+
768
+ except websocket.WebSocketConnectionClosedException:
769
+ break
770
+
771
+ # Create the WebSocket connection
772
+ ws = websocket.WebSocketApp(
773
+ self.websocket_url,
774
+ on_message=on_message,
775
+ on_error=on_error,
776
+ on_close=on_close,
777
+ on_open=on_open,
778
+ header=headers, # Pass headers if required
779
+ )
780
+ ws.run_forever() # type: ignore
781
+
782
+ def stream_order_data_ws(self):
783
+ thread = threading.Thread(target=self.start_order_websocket, daemon=True)
784
+ thread.start()
@@ -1,6 +1,7 @@
1
1
  import binascii
2
2
  import copy
3
3
  import json
4
+ import threading
4
5
  import traceback
5
6
  from queue import Queue
6
7
  from typing import Any
@@ -12,7 +13,9 @@ from requests.exceptions import ConnectionError, ConnectTimeout
12
13
  from retrying import retry # type: ignore
13
14
  from SmartApi import SmartConnect # type: ignore
14
15
  from SmartApi.smartExceptions import DataException # type: ignore
16
+ import websocket # type: ignore
15
17
 
18
+ from quantplay.broker.angelone_utils.angeloneWS import AngelOneOrderUpdateWS
16
19
  from quantplay.broker.generics.broker import Broker
17
20
  from quantplay.exception import RateLimitExceeded
18
21
  from quantplay.exception.exceptions import (
@@ -42,6 +45,7 @@ from quantplay.utils.pickle_utils import InstrumentData
42
45
  from quantplay.wrapper.aws.s3 import S3Utils
43
46
 
44
47
  logzero.logger.disabled = True
48
+ logger = Constants.logger
45
49
 
46
50
 
47
51
  class AngelOne(Broker):
@@ -583,3 +587,75 @@ class AngelOne(Broker):
583
587
  except Exception as e:
584
588
  traceback.print_exc()
585
589
  raise RetryableException(str(e))
590
+
591
+ def handle_order_update(self, wsapp: websocket.WebSocket, message: str | bytes):
592
+ if not isinstance(message, str):
593
+ return
594
+
595
+ order: dict[str, str] | None = json.loads(message).get("orderData", None)
596
+
597
+ if order is None or order["orderid"] == "":
598
+ return
599
+
600
+ try:
601
+ out_order: OrderUpdateEvent = {
602
+ "placed_by": order["ordertag"],
603
+ "tag": order["ordertag"],
604
+ "order_id": order["orderid"],
605
+ "exchange_order_id": order["orderid"],
606
+ "exchange": order["exchange"],
607
+ "tradingsymbol": order["tradingsymbol"],
608
+ "status": order["status"],
609
+ "order_type": order["ordertype"],
610
+ "price": float(order["price"]),
611
+ "transaction_type": order["transactiontype"],
612
+ "product": order["producttype"],
613
+ "quantity": int(order["quantity"]),
614
+ "trigger_price": float(order["stoploss"]),
615
+ } # type: ignore
616
+
617
+ out_order["tradingsymbol"] = self.broker_symbol_map[
618
+ out_order["tradingsymbol"]
619
+ ]
620
+
621
+ if order["status"] == "open":
622
+ out_order["status"] = "OPEN"
623
+ elif order["status"] == "cancelled":
624
+ out_order["status"] = "CANCELLED"
625
+ elif order["status"] == "trigger pending":
626
+ out_order["status"] = "TRIGGER PENDING"
627
+ elif order["status"] == "complete":
628
+ out_order["status"] = "COMPLETE"
629
+ elif order["status"] == "rejected":
630
+ out_order["status"] = "REJECTED"
631
+
632
+ if order["producttype"] == "DELIVERY":
633
+ out_order["product"] = "CNC"
634
+ elif order["producttype"] == "CARRYFORWARD":
635
+ out_order["product"] = "NRML"
636
+ elif order["producttype"] == "INTRADAY":
637
+ out_order["product"] = "MIS"
638
+
639
+ if order["ordertype"] == AngelOne.order_sl:
640
+ out_order["order_type"] = OrderType.sl
641
+
642
+ elif order["ordertype"] == AngelOne.order_slm:
643
+ out_order["order_type"] = OrderType.sl
644
+
645
+ except Exception as e:
646
+ logger.error(f"[ORDER_UPDATE_PROCESSING_FAILED] {e}")
647
+ traceback.print_exc()
648
+
649
+ def start_order_websocket(self) -> None:
650
+ ws_client = AngelOneOrderUpdateWS(
651
+ auth_token=self.wrapper.access_token, # type: ignore
652
+ api_key=self.api_key, # type: ignore
653
+ client_code=self.user_id, # type: ignore
654
+ feed_token=self.wrapper.getfeedToken(), # type: ignore
655
+ )
656
+ ws_client.on_message = self.handle_order_update
657
+ ws_client.connect()
658
+
659
+ def stream_order_data_ws(self):
660
+ thread = threading.Thread(target=self.start_order_websocket, daemon=True)
661
+ thread.start()
@@ -56,8 +56,7 @@ class FlatTradeUtils:
56
56
  login_attempt = driver.find_element("xpath", FlatTradeUtils.login_xpath)
57
57
  login_attempt.click()
58
58
 
59
- time.sleep(2)
60
-
59
+ time.sleep(10)
61
60
  url = driver.current_url
62
61
 
63
62
  if url.split("/")[2] == flattrade_url.split("/")[2]:
@@ -89,7 +88,6 @@ class FlatTradeUtils:
89
88
  "RESETPASSWORD",
90
89
  "Invalid API key",
91
90
  "Invalid Input : User Blocked due to multiple wrong attempts",
92
- "",
93
91
  ]:
94
92
  Constants.logger.info(f"Flattrade error : {error_message}")
95
93
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.1.44
3
+ Version: 2.1.46
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -36,10 +36,6 @@ Requires-Dist: python-engineio
36
36
  Requires-Dist: python-socketio
37
37
  Requires-Dist: six
38
38
  Requires-Dist: dhanhq
39
- Dynamic: description
40
- Dynamic: license
41
- Dynamic: requires-dist
42
- Dynamic: summary
43
39
 
44
40
  # Quantplay Alpha playground
45
41
 
@@ -21,7 +21,7 @@ requirements = [
21
21
  setup(
22
22
  name="quantplay",
23
23
  long_description=Path("README.md").read_text(),
24
- version="2.1.44",
24
+ version="2.1.46",
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