quantplay 2.0.2__tar.gz → 2.0.4__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.2 → quantplay-2.0.4}/PKG-INFO +1 -1
  2. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/aliceblue.py +6 -7
  3. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/angelone.py +2 -6
  4. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/five_paisa.py +1 -3
  5. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/generics/broker.py +34 -19
  6. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/motilal.py +7 -35
  7. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/noren.py +8 -5
  8. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/upstox.py +40 -47
  9. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/xts.py +9 -9
  10. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/zerodha.py +2 -2
  11. quantplay-2.0.4/quantplay/exception/__init__.py +1 -0
  12. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/constant.py +7 -7
  13. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/exchange.py +0 -1
  14. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/pickle_utils.py +4 -3
  15. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/selenium_utils.py +1 -1
  16. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay.egg-info/PKG-INFO +1 -1
  17. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay.egg-info/SOURCES.txt +1 -0
  18. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay.egg-info/requires.txt +1 -0
  19. {quantplay-2.0.2 → quantplay-2.0.4}/setup.py +2 -1
  20. {quantplay-2.0.2 → quantplay-2.0.4}/README.md +0 -0
  21. {quantplay-2.0.2 → quantplay-2.0.4}/pyproject.toml +0 -0
  22. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/__init__.py +0 -0
  23. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/__init__.py +0 -0
  24. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/auto_login/__init__.py +0 -0
  25. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/auto_login/aliceblue.py +0 -0
  26. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/finvasia_utils/__init__.py +0 -0
  27. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
  28. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/flattrade.py +0 -0
  29. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/ft_utils/__init__.py +0 -0
  30. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
  31. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/ft_utils/ft_noren.py +0 -0
  32. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/generics/__init__.py +0 -0
  33. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/iifl_xts.py +0 -0
  34. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/kite_utils.py +0 -0
  35. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/shoonya.py +0 -0
  36. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/uplink/__init__.py +0 -0
  37. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/uplink/uplink_utils.py +0 -0
  38. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/xts_utils/Connect.py +0 -0
  39. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/xts_utils/Exception.py +0 -0
  40. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
  41. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/broker/xts_utils/__init__.py +0 -0
  42. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/exception/exceptions.py +0 -0
  43. {quantplay-2.0.2/quantplay/exception → quantplay-2.0.4/quantplay/model}/__init__.py +0 -0
  44. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/model/broker/__init__.py +0 -0
  45. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/model/broker/generics.py +0 -0
  46. /quantplay-2.0.2/quantplay/model/__init__.py → /quantplay-2.0.4/quantplay/py.typed +0 -0
  47. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/__init__.py +0 -0
  48. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/equities/__init__.py +0 -0
  49. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/equities/intraday/__init__.py +0 -0
  50. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/equities/overnight/__init__.py +0 -0
  51. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/futures/__init__.py +0 -0
  52. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/futures/overnight/__init__.py +0 -0
  53. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/options/__init__.py +0 -0
  54. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/options/intraday/__init__.py +0 -0
  55. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/options/intraday/ladder.py +0 -0
  56. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/options/intraday/musk.py +0 -0
  57. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/strategies/options/intraday/short_straddle.py +0 -0
  58. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/__init__.py +0 -0
  59. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/utils/number_utils.py +0 -0
  60. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/wrapper/__init__.py +0 -0
  61. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/wrapper/aws/__init__.py +0 -0
  62. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay/wrapper/aws/s3.py +0 -0
  63. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay.egg-info/dependency_links.txt +0 -0
  64. {quantplay-2.0.2 → quantplay-2.0.4}/quantplay.egg-info/top_level.txt +0 -0
  65. {quantplay-2.0.2 → quantplay-2.0.4}/setup.cfg +0 -0
  66. {quantplay-2.0.2 → quantplay-2.0.4}/tests/__init__.py +0 -0
  67. {quantplay-2.0.2 → quantplay-2.0.4}/tests/conftest.py +0 -0
  68. {quantplay-2.0.2 → quantplay-2.0.4}/tests/wrapper/__init__.py +0 -0
  69. {quantplay-2.0.2 → quantplay-2.0.4}/tests/wrapper/aws/__init__.py +0 -0
  70. {quantplay-2.0.2 → quantplay-2.0.4}/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.2
3
+ Version: 2.0.4
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -1,14 +1,12 @@
1
1
  import traceback
2
2
  from typing import Callable, Dict, Literal
3
3
 
4
- import pandas as pd
5
4
  import polars as pl
6
5
  from pya3 import Aliceblue as Alice
7
6
  from pya3 import OrderType as AliceOrderType
8
7
  from pya3 import ProductType
9
8
  from pya3 import TransactionType
10
9
  from retrying import retry
11
- import numpy as np
12
10
  import pickle
13
11
  import copy
14
12
  import codecs
@@ -24,7 +22,7 @@ from quantplay.exception.exceptions import (
24
22
  )
25
23
 
26
24
  from quantplay.exception.exceptions import TokenException as QuantplayTokenException
27
- from quantplay.utils.constant import Constants, OrderType, OrderStatus, timeit
25
+ from quantplay.utils.constant import Constants, OrderType, timeit
28
26
  from quantplay.utils.pickle_utils import InstrumentData
29
27
 
30
28
  logger = Constants.logger
@@ -55,6 +53,7 @@ class Aliceblue(Broker):
55
53
  api_key=api_key,
56
54
  )
57
55
  response = self.alice.get_session_id()
56
+
58
57
  if "sessionID" not in response or response["sessionID"] is None:
59
58
  if "emsg" in response:
60
59
  if response["emsg"].lower() == "invalid input":
@@ -248,14 +247,14 @@ class Aliceblue(Broker):
248
247
  if "trigger_price" in data and data["trigger_price"] is not None:
249
248
  trigger_price = float(data["trigger_price"])
250
249
 
251
- response = self.alice.modify_order(
250
+ self.alice.modify_order(
252
251
  instrument=self.alice.get_instrument_by_token(exchange, token),
253
252
  transaction_type=transaction_type,
254
253
  order_id=order_id,
255
254
  product_type=product_type,
256
255
  order_type=order_type,
257
256
  price=float(data["price"]),
258
- trigger_price=trigger_price, # type:ignore
257
+ trigger_price=trigger_price, # type: ignore
259
258
  quantity=quantity,
260
259
  )
261
260
  logger.info("[MODIFY_ORDER_RESPONSE] [{order_id}] response [{response}]")
@@ -277,9 +276,9 @@ class Aliceblue(Broker):
277
276
  else:
278
277
  data["trigger_price"] = None
279
278
 
280
- self.modify_order(data) # type:ignore
279
+ self.modify_order(data) # type: ignore
281
280
 
282
- def cancel_order(self, order_id: str) -> None:
281
+ def cancel_order(self, order_id: str, variety=None) -> None:
283
282
  self.invoke_aliceblue_api(self.alice.cancel_order, nestordernmbr=order_id)
284
283
 
285
284
  def profile(self):
@@ -109,7 +109,7 @@ class AngelOne(Broker):
109
109
  raise TokenException(f"{self.user_id}: Invalid Token")
110
110
 
111
111
  @timeit(MetricName="Angelone:load_instrument")
112
- def load_instrument(self, file_name=None):
112
+ def load_instrument(self, file_name: str | None = None) -> None:
113
113
  try:
114
114
  instrument_data_instance = InstrumentData.get_instance()
115
115
  if instrument_data_instance is not None:
@@ -170,7 +170,7 @@ class AngelOne(Broker):
170
170
  wait_exponential_max=10000,
171
171
  stop_max_attempt_number=3,
172
172
  )
173
- def get_ltp(self, exchange=None, tradingsymbol=None):
173
+ def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
174
174
  if tradingsymbol in MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP:
175
175
  tradingsymbol = MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP[
176
176
  tradingsymbol
@@ -584,10 +584,6 @@ class AngelOne(Broker):
584
584
  api_margins = api_margins["data"]
585
585
 
586
586
  try:
587
- collateral = 0
588
- if "collateral" in api_margins:
589
- collateral = float(api_margins["collateral"])
590
-
591
587
  margins = {
592
588
  "margin_used": float(api_margins["net"]),
593
589
  "margin_available": float(api_margins["net"]),
@@ -1,4 +1,3 @@
1
- import numpy as np
2
1
  import polars as pl
3
2
  import pyotp
4
3
  import pickle
@@ -6,7 +5,6 @@ import codecs
6
5
  from py5paisa import FivePaisaClient
7
6
  from retrying import retry
8
7
  import traceback
9
- from datetime import datetime
10
8
 
11
9
  from quantplay.broker.generics.broker import Broker
12
10
  from quantplay.exception.exceptions import (
@@ -138,7 +136,7 @@ class FivePaisa(Broker):
138
136
  retry_on_exception=retry_exception,
139
137
  )
140
138
  @timeit(MetricName="5Paisa:get_ltp")
141
- def get_ltp(self, exchange, tradingsymbol):
139
+ def get_ltp(self, exchange, tradingsymbol) -> float:
142
140
  tradingsymbol = self.get_symbol(tradingsymbol, exchange)
143
141
  exchange_name = self.get_exchange(exchange)
144
142
  exchange_type = self.get_exchange_type(exchange)
@@ -5,7 +5,7 @@ import time
5
5
  from collections import defaultdict
6
6
  import threading
7
7
  from threading import Lock
8
- from typing import Any, Dict
8
+ from typing import Any, Dict, List, Literal
9
9
 
10
10
  from cachetools import TTLCache, cached
11
11
  import numpy as np
@@ -52,6 +52,10 @@ class Broker:
52
52
  self.instrument_id_to_exchange_map = dict()
53
53
  self.instrument_id_to_security_type_map = dict()
54
54
  self.exchange_symbol_to_instrument_id_map = defaultdict(dict)
55
+
56
+ self.quantplay_symbol_map: Dict[str, str] = {}
57
+ self.broker_symbol_map: Dict[str, str] = {}
58
+
55
59
  self.order_type_sl = "SL"
56
60
  self.nfo_exchange = "NFO"
57
61
 
@@ -118,6 +122,7 @@ class Broker:
118
122
 
119
123
  self.trigger_pending_status = "TRIGGER PENDING"
120
124
  self.lock = Lock()
125
+ self.square_off_lock = Lock()
121
126
 
122
127
  def validate_exchange(self, exchange):
123
128
  if exchange not in ["NSE", "NFO", "BFO", "BSE"]:
@@ -137,7 +142,7 @@ class Broker:
137
142
  )
138
143
  raise StaleDataFound(f"Couldn't find symbol data for [{exchange}:{symbol}]")
139
144
 
140
- def initialize_symbol_data(self, save_as=None):
145
+ def initialize_symbol_data(self, save_as: str | None = None) -> None:
141
146
  instruments = self.instrument_data
142
147
  instruments = instruments.to_dict("records")
143
148
  self.symbol_data = {}
@@ -148,7 +153,8 @@ class Broker:
148
153
  instrument_data = copy.deepcopy(instrument)
149
154
  self.symbol_data["{}:{}".format(exchange, tradingsymbol)] = instrument_data
150
155
 
151
- PickleUtils.save_data(self.symbol_data, save_as)
156
+ if save_as:
157
+ PickleUtils.save_data(self.symbol_data, save_as)
152
158
 
153
159
  @timeit(MetricName="Broker:initialize_broker_symbol_map")
154
160
  def initialize_broker_symbol_map(self):
@@ -163,7 +169,7 @@ class Broker:
163
169
  and platform_version[0] == "3"
164
170
  and platform_version[1] == "8"
165
171
  ):
166
- self.quantplay_symbol_map = {}
172
+ self.quantplay_symbol_map: Dict[str, str] = {}
167
173
  for k in self.broker_symbol_map:
168
174
  v = self.broker_symbol_map[k]
169
175
  self.quantplay_symbol_map[v] = k
@@ -171,7 +177,7 @@ class Broker:
171
177
  self.quantplay_symbol_map = {v: k for k, v in self.broker_symbol_map.items()}
172
178
 
173
179
  @timeit(MetricName="Broker:load_instrument")
174
- def load_instrument(self, file_name):
180
+ def load_instrument(self, file_name: str) -> None:
175
181
  try:
176
182
  instrument_data_instance = InstrumentData.get_instance()
177
183
 
@@ -391,7 +397,7 @@ class Broker:
391
397
  tradingsymbol=tradingsymbol,
392
398
  exchange=exchange,
393
399
  quantity=quantity,
394
- order_type=self.order_type_sl, # type:ignore
400
+ order_type=self.order_type_sl, # type: ignore
395
401
  transaction_type=sl_transaction_type,
396
402
  tag=tag,
397
403
  product=product,
@@ -600,18 +606,25 @@ class Broker:
600
606
  stoploss_order["price"] = self.round_to_tick(ltp)
601
607
  stoploss_order["trigger_price"] = None
602
608
 
603
- self.modify_order(stoploss_order) # type:ignore
609
+ self.modify_order(stoploss_order) # type: ignore
604
610
  time.sleep(0.1)
605
611
 
606
612
  self.modify_orders_till_complete(orders_to_close, sleep_time=modify_sleep_time)
607
613
  Constants.logger.info("All order have been closed successfully")
608
614
 
609
- def market_protection_price(self, price, transaction_type, market_protection=0.02):
615
+ def market_protection_price(
616
+ self,
617
+ price: float,
618
+ transaction_type: Literal["BUY", "SELL"],
619
+ market_protection: float = 0.02,
620
+ ) -> float:
610
621
  if transaction_type == "BUY":
611
622
  price = self.round_to_tick(price * (1 + market_protection))
612
623
  return price + 1
624
+
613
625
  elif transaction_type == "SELL":
614
626
  return self.round_to_tick(price * (1 - market_protection))
627
+
615
628
  return price
616
629
 
617
630
  def split_order(self, exchange, tradingsymbol, quantity, max_qty=None):
@@ -883,7 +896,9 @@ class Broker:
883
896
  logger.error(f"Couldn't compute freeze quantity for {exchange} {symbol}")
884
897
  return 25
885
898
 
886
- def square_off_by_tag(self, tag, dry_run=True, sleep_time=0.05):
899
+ def square_off_by_tag(
900
+ self, tag: str, dry_run=True, sleep_time=0.05
901
+ ) -> List[Dict[str, Any]]:
887
902
  self.exit_all_trigger_orders(tag=tag)
888
903
  orders = self.orders(tag=tag)
889
904
 
@@ -906,7 +921,7 @@ class Broker:
906
921
  ]
907
922
  )
908
923
 
909
- orders_to_close = []
924
+ orders_to_close: List[Dict[str, Any]] = []
910
925
  exit_orders = exit_orders.filter(pl.col("exit_quantity") != 0)
911
926
  positions = exit_orders.to_dicts()
912
927
  for position in positions:
@@ -978,13 +993,13 @@ class Broker:
978
993
  orders.loc[:, "ltp"] = orders["exchange_symbol"].map(symbol_ltp)
979
994
 
980
995
  def modify_price(self, order_id, price, trigger_price=None, order_type=None):
981
- data = {} # ignore:type
982
-
983
- data["order_id"] = order_id
984
- data["price"] = price
985
- data["order_type"] = order_type
986
- data["variety"] = "regular"
987
- data["trigger_price"] = trigger_price
996
+ data = {
997
+ "order_id": order_id,
998
+ "price": price,
999
+ "order_type": order_type,
1000
+ "variety": "regular",
1001
+ "trigger_price": trigger_price,
1002
+ }
988
1003
 
989
1004
  self.modify_order(data) # type: ignore
990
1005
 
@@ -1111,7 +1126,7 @@ class Broker:
1111
1126
  )
1112
1127
 
1113
1128
  order["order_type"] = "LIMIT"
1114
- self.modify_order(order) # type:ignore
1129
+ self.modify_order(order) # type: ignore
1115
1130
 
1116
1131
  time.sleep(0.1)
1117
1132
 
@@ -1222,7 +1237,7 @@ class Broker:
1222
1237
  """
1223
1238
  raise NotImplementedError
1224
1239
 
1225
- def cancel_order(self, order_id: str) -> None:
1240
+ def cancel_order(self, order_id: str, variety: str | None = None) -> None:
1226
1241
  """Cancels order for the given order_id
1227
1242
 
1228
1243
  Args:
@@ -147,9 +147,9 @@ class Motilal(Broker):
147
147
  self.instrument_data_by_exchange[exchange] = df
148
148
 
149
149
  @timeit(MetricName="Motilal:load_instrument")
150
- def load_instrument(self, file_name=None):
150
+ def load_instrument(self, file_name: str | None = None) -> None:
151
151
  try:
152
- self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
152
+ self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
153
153
  "motilal_instruments"
154
154
  )
155
155
  Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
@@ -256,7 +256,7 @@ class Motilal(Broker):
256
256
  wait_exponential_max=10000,
257
257
  stop_max_attempt_number=3,
258
258
  )
259
- def get_ltp(self, exchange=None, tradingsymbol=None):
259
+ def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
260
260
  tradingsymbol = self.get_symbol(tradingsymbol)
261
261
  token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
262
262
  exchange = self.get_exchange(exchange)
@@ -273,35 +273,6 @@ class Motilal(Broker):
273
273
  Constants.logger.info("[GET_LTP_RESPONSE] response {}".format(response.json()))
274
274
  return response.json()["data"]["ltp"] / 100.0
275
275
 
276
- def get_orders(self, order_status=None, order_type=None):
277
- response = (requests.post(self.order_book_url, headers=self.headers)).json()
278
- if response["status"] == "ERROR":
279
- Constants.logger.info(
280
- "Error while fetching order book [{}]".format(response["message"])
281
- )
282
- raise Exception(response["message"])
283
- orders = response["data"]
284
-
285
- if order_status:
286
- orders = [a for a in orders if a["orderstatus"] == order_status]
287
-
288
- if order_type:
289
- orders = [a for a in orders if a["ordertype"] == order_type]
290
-
291
- return orders
292
-
293
- def get_positions(self):
294
- response = (requests.post(self.positions_url, headers=self.headers)).json()
295
- if response["status"] == "ERROR":
296
- Constants.logger.info(
297
- "Error while fetching order book [{}]".format(response["message"])
298
- )
299
- raise Exception(response["message"])
300
-
301
- positions = response["data"]
302
-
303
- return positions
304
-
305
276
  @timeit(MetricName="Motilal:modify_price")
306
277
  def modify_price(self, order_id, price, trigger_price=None, order_type=None):
307
278
  orders = self.orders()
@@ -369,7 +340,7 @@ class Motilal(Broker):
369
340
  stop_max_attempt_number=2,
370
341
  retry_on_exception=retry_exception,
371
342
  )
372
- def cancel_order(self, order_id: str) -> None:
343
+ def cancel_order(self, order_id: str, variety=None) -> None:
373
344
  data = {"uniqueorderid": order_id}
374
345
 
375
346
  try:
@@ -566,8 +537,8 @@ class Motilal(Broker):
566
537
  @timeit(MetricName="Motilal:account_summary")
567
538
  def account_summary(self):
568
539
  response = self.margins()
540
+ response["pnl"] = float(self.positions()["pnl"].sum()) # type: ignore
569
541
 
570
- response["pnl"] = self.positions()["pnl"].sum() # type:ignore
571
542
  return response
572
543
 
573
544
  @timeit(MetricName="Motilal:profile")
@@ -720,7 +691,8 @@ class Motilal(Broker):
720
691
  )
721
692
  raise Exception(response["message"])
722
693
  orders = response["data"]
723
- if len(orders) == 0:
694
+
695
+ if orders is None or len(orders) == 0:
724
696
  return pl.DataFrame(schema=self.orders_schema)
725
697
 
726
698
  orders_df = pl.DataFrame(orders)
@@ -39,7 +39,7 @@ class Noren(Broker):
39
39
  ):
40
40
  super(Noren, self).__init__()
41
41
 
42
- self.order_updates: Queue = order_updates # type:ignore
42
+ self.order_updates: Queue | None = order_updates
43
43
 
44
44
  if load_instrument:
45
45
  self.load_instrument()
@@ -53,9 +53,9 @@ class Noren(Broker):
53
53
  self.full_name = response["uname"]
54
54
  self.user_token = response["susertoken"]
55
55
 
56
- def load_instrument(self, file_name=None):
56
+ def load_instrument(self, file_name: str | None = None) -> None:
57
57
  try:
58
- self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
58
+ self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
59
59
  "shoonya_instruments"
60
60
  )
61
61
  Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
@@ -169,7 +169,10 @@ class Noren(Broker):
169
169
  order["status"] = "CANCELLED"
170
170
 
171
171
  print(f"order feed {order}")
172
- self.order_updates.put(order)
172
+
173
+ if self.order_updates:
174
+ self.order_updates.put(order)
175
+
173
176
  except Exception as e:
174
177
  logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
175
178
 
@@ -316,7 +319,7 @@ class Noren(Broker):
316
319
  raise e
317
320
 
318
321
  @timeit(MetricName="Finvasia:cancel_order")
319
- def cancel_order(self, order_id):
322
+ def cancel_order(self, order_id, variety=None):
320
323
  self.api.cancel_order(order_id) # type:ignore
321
324
 
322
325
  def stream_order_data(self):
@@ -62,17 +62,22 @@ class Upstox(Broker):
62
62
 
63
63
  self.configuration = upstox_client.Configuration()
64
64
  self.configuration.access_token = self.access_token
65
- self.profile()
65
+
66
66
  except Exception as e:
67
67
  raise e
68
68
 
69
69
  self.configuration = upstox_client.Configuration()
70
70
  self.configuration.access_token = self.access_token
71
+ self.api_version = "2.0"
72
+
73
+ self.api_client = upstox_client.ApiClient(self.configuration)
74
+
71
75
  if load_instrument:
72
76
  self.load_instrument()
77
+
73
78
  super(Upstox, self).__init__()
74
79
 
75
- def load_instrument(self, file_name=None):
80
+ def load_instrument(self, file_name: str | None = None) -> None:
76
81
  super().load_instrument("upstox_instruments")
77
82
 
78
83
  def handle_exception(self, e):
@@ -92,6 +97,7 @@ class Upstox(Broker):
92
97
  return "D"
93
98
  elif product == "MIS":
94
99
  return "I"
100
+
95
101
  return product
96
102
 
97
103
  def get_exchange(self, exchange):
@@ -125,17 +131,15 @@ class Upstox(Broker):
125
131
  )
126
132
  @timeit(MetricName="Upstox:get_ltp")
127
133
  def get_ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
128
- # create an instance of the API class
129
- api_instance = upstox_client.MarketQuoteApi(
130
- upstox_client.ApiClient(self.configuration)
131
- )
132
- api_version = "2.0" # str | API Version Header
134
+ api_instance = upstox_client.MarketQuoteApi(self.api_client)
133
135
 
134
136
  ltp = 0
135
137
  try:
136
138
  symbol_info = self.symbol_data[f"{exchange}:{tradingsymbol}"]
137
139
  # Market quotes and instruments - LTP quotes.
138
- api_response = api_instance.ltp(symbol_info["instrument_key"], api_version)
140
+ api_response = api_instance.ltp(
141
+ symbol_info["instrument_key"], self.api_version
142
+ )
139
143
 
140
144
  ltp: float = api_response.data[ # type:ignore
141
145
  f"{self.get_exchange(symbol_info['exchange'])}:{tradingsymbol}"
@@ -158,8 +162,7 @@ class Upstox(Broker):
158
162
  or order_to_modify["trigger_price"] is None
159
163
  ):
160
164
  order_to_modify["trigger_price"] = 0
161
- # create an instance of the API class
162
- api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
165
+ api_instance = upstox_client.OrderApi(self.api_client)
163
166
  body = upstox_client.ModifyOrderRequest(
164
167
  validity="DAY",
165
168
  price=order_to_modify["price"],
@@ -167,11 +170,10 @@ class Upstox(Broker):
167
170
  order_type=order_to_modify["order_type"],
168
171
  trigger_price=order_to_modify["trigger_price"],
169
172
  )
170
- api_version = "2.0" # str | API Version Header
171
173
 
172
174
  try:
173
175
  # Modify order
174
- api_response = api_instance.modify_order(body, api_version)
176
+ api_response = api_instance.modify_order(body, self.api_version)
175
177
  return api_response.status # type:ignore
176
178
  except ApiException as e:
177
179
  Constants.logger.error(
@@ -187,14 +189,12 @@ class Upstox(Broker):
187
189
  return order_to_modify["order_id"]
188
190
 
189
191
  @timeit(MetricName="Upstox:cancel_order")
190
- def cancel_order(self, order_id: str) -> None:
191
- # create an instance of the API class
192
- api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
193
- api_version = "2.0" # str | API Version Header
192
+ def cancel_order(self, order_id: str, variety=None) -> None:
193
+ api_instance = upstox_client.OrderApi(self.api_client)
194
194
 
195
195
  try:
196
196
  # Cancel order
197
- api_response = api_instance.cancel_order(order_id, api_version)
197
+ api_response = api_instance.cancel_order(order_id, self.api_version)
198
198
  return api_response.status # type:ignore
199
199
  except ApiException as e:
200
200
  if self.handle_exception(e) == 400:
@@ -224,10 +224,7 @@ class Upstox(Broker):
224
224
  if trigger_price is None:
225
225
  trigger_price = 0
226
226
 
227
- # create an instance of the API class
228
- api_instance = upstox_client.OrderApi(
229
- upstox_client.ApiClient(self.configuration)
230
- )
227
+ api_instance = upstox_client.OrderApi(self.api_client)
231
228
  body = upstox_client.PlaceOrderRequest(
232
229
  quantity=quantity,
233
230
  product=product,
@@ -240,8 +237,8 @@ class Upstox(Broker):
240
237
  trigger_price=trigger_price,
241
238
  is_amo=False,
242
239
  )
243
- api_version = "2.0" # str | API Version Header
244
- api_response = api_instance.place_order(body, api_version)
240
+
241
+ api_response = api_instance.place_order(body, self.api_version)
245
242
  return api_response.data.order_id # type:ignore
246
243
  except Exception as e:
247
244
  raise QuantplayOrderPlacementException(str(e))
@@ -283,14 +280,12 @@ class Upstox(Broker):
283
280
  )
284
281
  @timeit(MetricName="Upstox:profile")
285
282
  def profile(self) -> UserBrokerProfileResponse:
286
- # create an instance of the API class
287
- api_instance = upstox_client.UserApi(upstox_client.ApiClient(self.configuration))
288
- api_version = "2.0" # str | API Version Header
283
+ api_instance = upstox_client.UserApi(self.api_client)
289
284
 
290
285
  response: UserBrokerProfileResponse = {"user_id": "", "full_name": ""}
291
286
  try:
292
287
  # Get profile
293
- api_response = api_instance.get_profile(api_version)
288
+ api_response = api_instance.get_profile(self.api_version)
294
289
  profile_data = api_response.data # type:ignore
295
290
  response: UserBrokerProfileResponse = {
296
291
  "user_id": profile_data.user_id,
@@ -308,15 +303,12 @@ class Upstox(Broker):
308
303
  return response
309
304
 
310
305
  def holdings(self):
311
- api_instance = upstox_client.PortfolioApi(
312
- upstox_client.ApiClient(self.configuration)
313
- )
306
+ api_instance = upstox_client.PortfolioApi(self.api_client)
314
307
 
315
- api_version = "2.0" # str | API Version Header
316
308
  holdings_df = pl.DataFrame()
317
309
  try:
318
310
  # Get Holdings
319
- api_response = api_instance.get_holdings(api_version)
311
+ api_response = api_instance.get_holdings(self.api_version)
320
312
  holdings = [holding.to_dict() for holding in api_response.data] # type:ignore
321
313
  holdings_df = pl.DataFrame(holdings)
322
314
  except ApiException as e:
@@ -364,15 +356,12 @@ class Upstox(Broker):
364
356
  )
365
357
  @timeit(MetricName="Upstox:positions")
366
358
  def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
367
- # create an instance of the API class
368
- api_instance = upstox_client.PortfolioApi(
369
- upstox_client.ApiClient(self.configuration)
370
- )
371
- api_version = "2.0" # str | API Version Header
359
+ api_instance = upstox_client.PortfolioApi(self.api_client)
360
+
372
361
  positions_df = pl.DataFrame(schema=self.positions_schema)
373
362
  try:
374
363
  # Get Positions
375
- api_response = api_instance.get_positions(api_version)
364
+ api_response = api_instance.get_positions(self.api_version)
376
365
  positions = [
377
366
  position.to_dict() for position in api_response.data # type:ignore
378
367
  ]
@@ -462,14 +451,12 @@ class Upstox(Broker):
462
451
  )
463
452
  @timeit(MetricName="Upstox:orders")
464
453
  def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
465
- # create an instance of the API class
466
- api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
467
- api_version = "2.0" # str | API Version Header
454
+ api_instance = upstox_client.OrderApi(self.api_client)
468
455
 
469
456
  orders_df = pl.DataFrame()
470
457
  try:
471
458
  # Get order book
472
- api_response = api_instance.get_order_book(api_version)
459
+ api_response = api_instance.get_order_book(self.api_version)
473
460
  orders = [order.to_dict() for order in api_response.data] # type:ignore
474
461
  orders_df = pl.DataFrame(orders)
475
462
  except ApiException as e:
@@ -569,15 +556,17 @@ class Upstox(Broker):
569
556
  )
570
557
  @timeit(MetricName="Upstox:margins")
571
558
  def margins(self):
572
- api_instance = upstox_client.UserApi(upstox_client.ApiClient(self.configuration))
573
- api_version = "2.0" # str | API Version Header
559
+ api_instance = upstox_client.UserApi(self.api_client)
560
+
574
561
  segment = "SEC" # str | (optional)
575
562
 
576
563
  margin_used = 0
577
564
  margin_available = 0
578
565
  try:
579
566
  # Get User Fund And Margin
580
- api_response = api_instance.get_user_fund_margin(api_version, segment=segment)
567
+ api_response = api_instance.get_user_fund_margin(
568
+ self.api_version, segment=segment
569
+ )
581
570
  margin_used: float = float(
582
571
  api_response.data["equity"].used_margin # type:ignore
583
572
  )
@@ -630,11 +619,13 @@ class Upstox(Broker):
630
619
  # Configure OAuth2 access token for authorization: OAUTH2
631
620
  configuration = upstox_client.Configuration()
632
621
 
633
- api_version = "2.0"
622
+ self.api_version = "2.0"
634
623
  configuration.access_token = self.access_token
635
624
 
636
625
  # Get portfolio stream feed authorize
637
- response = self.get_portfolio_stream_feed_authorize(api_version, configuration)
626
+ response = self.get_portfolio_stream_feed_authorize(
627
+ self.api_version, configuration
628
+ )
638
629
 
639
630
  async with websockets.connect(
640
631
  response.data.authorized_redirect_uri, # type:ignore
@@ -650,6 +641,7 @@ class Upstox(Broker):
650
641
  def get_portfolio_stream_feed_authorize(self, api_version, configuration):
651
642
  api_instance = upstox_client.WebsocketApi(upstox_client.ApiClient(configuration))
652
643
  api_response = api_instance.get_portfolio_stream_feed_authorize(api_version)
644
+
653
645
  return api_response
654
646
 
655
647
  def get_quantplay_product(self, exchange, product):
@@ -673,6 +665,7 @@ class Upstox(Broker):
673
665
  )
674
666
  Constants.logger.info("[UPDATE_RECEIVED] {}".format(order))
675
667
  self.order_updates.put(order)
668
+
676
669
  except Exception as e:
677
670
  traceback.print_exc()
678
671
  Constants.logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
@@ -33,7 +33,7 @@ from quantplay.model.broker.generics import OrderType as NewOrderType
33
33
  from quantplay.model.broker.generics import ProductType as NewProductType
34
34
 
35
35
  # -# Suppress only the single warning from urllib3 needed.
36
- requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) # type:ignore
36
+ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) # type: ignore
37
37
 
38
38
 
39
39
  class XTS(Broker):
@@ -54,7 +54,7 @@ class XTS(Broker):
54
54
  load_instrument=True,
55
55
  ):
56
56
  super(XTS, self).__init__()
57
- self.order_updates: Queue = order_updates # type:ignore
57
+ self.order_updates: Queue | None = order_updates
58
58
  self.root_url = root_url
59
59
 
60
60
  try:
@@ -78,12 +78,10 @@ class XTS(Broker):
78
78
  codecs.decode(serialized_md_wrapper.encode(), "base64")
79
79
  )
80
80
 
81
- def load_instrument(self, file_name=None):
81
+ def load_instrument(self, file_name: str | None = None) -> None:
82
82
  # TODO: Check for Futures
83
83
  try:
84
- self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
85
- "xts_instruments"
86
- )
84
+ self.symbol_data = InstrumentData.get_instance().load_data("xts_instruments") # type: ignore
87
85
  Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
88
86
  except Exception:
89
87
  instruments = pd.read_csv(
@@ -522,7 +520,7 @@ class XTS(Broker):
522
520
 
523
521
  return exchange_code_map[exchange]
524
522
 
525
- def get_ltp(self, exchange=None, tradingsymbol=None):
523
+ def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
526
524
  exchange_code = self.get_exchange_code(exchange)
527
525
  exchange_token = self.symbol_data[f"{exchange}:{tradingsymbol}"]["exchange_token"]
528
526
 
@@ -585,7 +583,7 @@ class XTS(Broker):
585
583
 
586
584
  return api_response["result"]["AppOrderID"]
587
585
 
588
- def cancel_order(self, order_id):
586
+ def cancel_order(self, order_id, variety=None):
589
587
  orders = self.orders()
590
588
 
591
589
  order_data = orders.filter(pl.col("order_id").eq(str(order_id)))
@@ -745,7 +743,9 @@ class XTS(Broker):
745
743
 
746
744
  if new_ord["status"] == "TRIGGER PENDING" and new_ord["trigger_price"] == 0:
747
745
  return
748
- self.order_updates.put(new_ord)
746
+
747
+ if self.order_updates:
748
+ self.order_updates.put(new_ord)
749
749
 
750
750
  except Exception as e:
751
751
  print(e)
@@ -64,9 +64,9 @@ class Zerodha(Broker):
64
64
  def set_wrapper(self, serialized_wrapper):
65
65
  self.wrapper = pickle.loads(codecs.decode(serialized_wrapper.encode(), "base64"))
66
66
 
67
- def initialize_symbol_data(self, save_as=None):
67
+ def initialize_symbol_data(self, save_as: str | None = None) -> None:
68
68
  try:
69
- self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
69
+ self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
70
70
  "zerodha_instruments"
71
71
  )
72
72
  Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
@@ -0,0 +1 @@
1
+ from .exceptions import * # noqa: F403
@@ -2,6 +2,7 @@ import logging
2
2
  import decimal
3
3
  from functools import wraps
4
4
  import time
5
+ from typing import Any
5
6
  import numpy as np
6
7
  import json
7
8
  from datetime import datetime
@@ -14,13 +15,15 @@ formatter = logging.Formatter(
14
15
 
15
16
  class LoggerUtils:
16
17
  @staticmethod
17
- def get_log_file_path(file_name):
18
+ def get_log_file_path(file_name: str) -> str:
18
19
  today_date = datetime.now()
19
20
 
20
21
  return "/tmp/" + file_name + "-" + today_date.strftime("%Y-%m-%d:01") + ".log"
21
22
 
22
23
  @staticmethod
23
- def setup_logger(logger_name, log_file, level=logging.DEBUG):
24
+ def setup_logger(
25
+ logger_name: str, log_file: str, level=logging.DEBUG
26
+ ) -> logging.Logger:
24
27
  log_file = LoggerUtils.get_log_file_path(log_file)
25
28
  """Function setup as many loggers as you want"""
26
29
 
@@ -48,19 +51,16 @@ class Constants:
48
51
  tick_logger = LoggerUtils.setup_logger("tick_logger", "tick")
49
52
 
50
53
  @staticmethod
51
- def myconverter(o):
54
+ def myconverter(o: Any):
52
55
  if isinstance(o, datetime):
53
56
  return o.__str__()
54
57
  if isinstance(o, decimal.Decimal):
55
- # wanted a simple yield str(o) in the next line,
56
- # but that would mean a yield on the line with super(...),
57
- # which wouldn't work (see my comment below), so...
58
58
  return float(o)
59
59
  if isinstance(o, np.int64): # type:ignore
60
60
  return int(o)
61
61
 
62
62
  @staticmethod
63
- def round_to_tick(number):
63
+ def round_to_tick(number: int | float) -> float:
64
64
  return round(number * 20) / 20
65
65
 
66
66
 
@@ -1,5 +1,4 @@
1
1
  class Market:
2
-
3
2
  TIMINGS = {
4
3
  # In (hour, minute) format
5
4
  "NSE": {
@@ -1,17 +1,18 @@
1
1
  import pickle
2
+ from typing import Any
2
3
  from quantplay.utils.constant import timeit
3
4
  from threading import Lock
4
5
 
5
6
 
6
7
  class PickleUtils:
7
8
  @staticmethod
8
- def save_data(data, file_name):
9
+ def save_data(data: Any, file_name: str):
9
10
  with open("/tmp/{}.pickle".format(file_name), "wb") as handle:
10
11
  pickle.dump(data, handle, protocol=pickle.HIGHEST_PROTOCOL)
11
12
 
12
13
  @staticmethod
13
14
  @timeit(MetricName="PickleUtils:load_data")
14
- def load_data(file_name):
15
+ def load_data(file_name: str):
15
16
  with open("/tmp/{}.pickle".format(file_name), "rb") as disk_data:
16
17
  unserialized_data = pickle.load(disk_data)
17
18
 
@@ -39,7 +40,7 @@ class InstrumentData:
39
40
  InstrumentData.__instance = self
40
41
 
41
42
  @timeit(MetricName="InstrumentData:load_data")
42
- def load_data(self, file_name):
43
+ def load_data(self, file_name: str):
43
44
  if file_name in self.instrument_data:
44
45
  return self.instrument_data[file_name]
45
46
 
@@ -18,7 +18,7 @@ class Selenium:
18
18
  wait_exponential_max=10000,
19
19
  stop_max_attempt_number=3,
20
20
  )
21
- def get_browser(headless=True):
21
+ def get_browser(headless: bool = True):
22
22
  options = Options()
23
23
  if headless:
24
24
  options.add_argument("--headless")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -2,6 +2,7 @@ README.md
2
2
  pyproject.toml
3
3
  setup.py
4
4
  quantplay/__init__.py
5
+ quantplay/py.typed
5
6
  quantplay.egg-info/PKG-INFO
6
7
  quantplay.egg-info/SOURCES.txt
7
8
  quantplay.egg-info/dependency_links.txt
@@ -6,6 +6,7 @@ boto3
6
6
  numpy
7
7
  websocket-client
8
8
  smartapi-python
9
+ logzero
9
10
  selenium
10
11
  requests
11
12
  pandas
@@ -21,12 +21,13 @@ requirements = [
21
21
  setup(
22
22
  name="quantplay",
23
23
  long_description=Path("README.md").read_text(),
24
- version="2.0.2",
24
+ version="2.0.4",
25
25
  setup_requires=["pytest-runner"],
26
26
  install_requires=requirements,
27
27
  tests_require=[],
28
28
  packages=find_packages(),
29
29
  url="",
30
+ package_data={"quantplay": ["py.typed"]},
30
31
  license="MIT",
31
32
  author="",
32
33
  author_email="",
File without changes
File without changes
File without changes
File without changes
File without changes