quantplay 2.0.40__tar.gz → 2.0.41__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantplay-2.0.40 → quantplay-2.0.41}/PKG-INFO +1 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/pyproject.toml +1 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/aliceblue.py +7 -3
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/angelone.py +4 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/dhan.py +5 -11
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/five_paisa.py +12 -6
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/generics/broker.py +19 -123
- quantplay-2.0.41/quantplay/broker/generics/broker_factory.py +222 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/motilal.py +12 -14
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/noren.py +6 -3
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/upstox.py +18 -14
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/xts.py +16 -8
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/zerodha.py +9 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/model/broker.py +1 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/model/generics.py +8 -0
- quantplay-2.0.41/quantplay/utils/caching.py +35 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay.egg-info/SOURCES.txt +2 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/setup.py +1 -1
- {quantplay-2.0.40 → quantplay-2.0.41}/README.md +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/xts_utils/Connect.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/model/instrument_data.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/py.typed +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/pickle_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/wrapper/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay/wrapper/aws/s3.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/setup.cfg +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/tests/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/tests/conftest.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/tests/wrapper/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.40 → quantplay-2.0.41}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -314,8 +314,9 @@ class Aliceblue(Broker):
|
|
|
314
314
|
data: ModifyOrderRequest = {
|
|
315
315
|
"order_id": order_id,
|
|
316
316
|
"price": price,
|
|
317
|
-
"order_type": order_type,
|
|
318
317
|
}
|
|
318
|
+
if trigger_price is not None:
|
|
319
|
+
data["order_type"] = order_type
|
|
319
320
|
|
|
320
321
|
if trigger_price is not None and trigger_price > 0:
|
|
321
322
|
data["trigger_price"] = trigger_price
|
|
@@ -403,7 +404,7 @@ class Aliceblue(Broker):
|
|
|
403
404
|
if not isinstance(positions_response, list):
|
|
404
405
|
return pl.DataFrame(schema=self.positions_schema)
|
|
405
406
|
|
|
406
|
-
positions_df = pl.from_dicts(positions_response)
|
|
407
|
+
positions_df = pl.from_dicts(positions_response) # type: ignore
|
|
407
408
|
|
|
408
409
|
positions_df = positions_df.with_columns(
|
|
409
410
|
(
|
|
@@ -454,13 +455,16 @@ class Aliceblue(Broker):
|
|
|
454
455
|
self.positions_schema
|
|
455
456
|
)
|
|
456
457
|
|
|
458
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
459
|
+
return exchange
|
|
460
|
+
|
|
457
461
|
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
458
462
|
orders_response = self.invoke_aliceblue_api(self.alice.order_data)
|
|
459
463
|
|
|
460
464
|
if not isinstance(orders_response, list):
|
|
461
465
|
return pl.DataFrame(schema=self.orders_schema)
|
|
462
466
|
|
|
463
|
-
if len(orders_response) == 0:
|
|
467
|
+
if len(orders_response) == 0: # type: ignore
|
|
464
468
|
return pl.DataFrame(schema=self.orders_schema)
|
|
465
469
|
|
|
466
470
|
orders_df = pl.DataFrame(orders_response)
|
|
@@ -106,6 +106,9 @@ class AngelOne(Broker):
|
|
|
106
106
|
if load_instrument:
|
|
107
107
|
self.load_instrument()
|
|
108
108
|
|
|
109
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
110
|
+
return exchange
|
|
111
|
+
|
|
109
112
|
def load_instrument(self, file_name: str | None = None) -> None:
|
|
110
113
|
try:
|
|
111
114
|
instrument_data_instance = InstrumentData.get_instance()
|
|
@@ -282,7 +285,7 @@ class AngelOne(Broker):
|
|
|
282
285
|
|
|
283
286
|
Constants.logger.info(f"Modifying order [{order_id}] params [{order_params}]")
|
|
284
287
|
response = self.invoke_angelone_api(
|
|
285
|
-
self.wrapper.modifyOrder,
|
|
288
|
+
self.wrapper.modifyOrder, # type: ignore
|
|
286
289
|
orderparams=order_params,
|
|
287
290
|
)
|
|
288
291
|
Constants.logger.info(f"[MODIFY_ORDER_RESPONSE] {response}")
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
import pickle
|
|
3
3
|
import traceback
|
|
4
|
-
from
|
|
5
|
-
from typing import Any, Callable, Dict, Hashable, List
|
|
4
|
+
from typing import Any, Dict, Hashable, List
|
|
6
5
|
|
|
7
|
-
import pandas as pd
|
|
8
6
|
import polars as pl
|
|
9
7
|
from kiteconnect import KiteConnect, KiteTicker # type: ignore
|
|
10
8
|
from kiteconnect.exceptions import TokenException # type: ignore
|
|
11
9
|
from retrying import retry # type: ignore
|
|
12
10
|
|
|
13
11
|
from quantplay.broker.generics.broker import Broker
|
|
14
|
-
from quantplay.broker.kite_utils import KiteUtils
|
|
15
12
|
from quantplay.exception.exceptions import (
|
|
16
13
|
InvalidArgumentException,
|
|
17
14
|
QuantplayOrderPlacementException,
|
|
@@ -21,19 +18,16 @@ from quantplay.exception.exceptions import (
|
|
|
21
18
|
from quantplay.exception.exceptions import TokenException as QuantplayTokenException
|
|
22
19
|
from quantplay.model.broker import (
|
|
23
20
|
ExchangeType,
|
|
24
|
-
ModifyOrderRequest,
|
|
25
|
-
UserBrokerProfileResponse,
|
|
26
21
|
)
|
|
27
22
|
from quantplay.model.generics import (
|
|
28
23
|
DhanTypes,
|
|
29
|
-
NorenTypes,
|
|
30
24
|
OrderTypeType,
|
|
31
25
|
ProductType,
|
|
32
26
|
TransactionType,
|
|
33
27
|
)
|
|
34
28
|
from quantplay.utils.constant import Constants, OrderType
|
|
35
29
|
from quantplay.utils.pickle_utils import InstrumentData, PickleUtils
|
|
36
|
-
from dhanhq import dhanhq
|
|
30
|
+
from dhanhq import dhanhq # type:ignore
|
|
37
31
|
|
|
38
32
|
|
|
39
33
|
class Dhan(Broker):
|
|
@@ -244,7 +238,7 @@ class Dhan(Broker):
|
|
|
244
238
|
Constants.logger.info(
|
|
245
239
|
f"[PLACING_ORDER] {tradingsymbol} {exchange} {quantity} {tag}"
|
|
246
240
|
)
|
|
247
|
-
order_id = self.dhan.place_order(
|
|
241
|
+
order_id: str = self.dhan.place_order( # type:ignore
|
|
248
242
|
security_id="1333", # hdfcbank
|
|
249
243
|
exchange_segment=self.get_exchange_segment(exchange),
|
|
250
244
|
transaction_type=transaction_type,
|
|
@@ -254,7 +248,7 @@ class Dhan(Broker):
|
|
|
254
248
|
price=price,
|
|
255
249
|
)
|
|
256
250
|
|
|
257
|
-
return order_id
|
|
251
|
+
return order_id # type:ignore
|
|
258
252
|
except Exception as e:
|
|
259
253
|
raise QuantplayOrderPlacementException(str(e))
|
|
260
254
|
|
|
@@ -300,7 +294,7 @@ class Dhan(Broker):
|
|
|
300
294
|
def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
|
|
301
295
|
positions = self.dhan_w.get_positions()
|
|
302
296
|
|
|
303
|
-
positions_df = pl.DataFrame(positions.get("data", {}))
|
|
297
|
+
positions_df = pl.DataFrame(positions.get("data", {})) # type:ignore
|
|
304
298
|
|
|
305
299
|
if len(positions_df) == 0:
|
|
306
300
|
return pl.DataFrame(schema=self.positions_schema)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
import pickle
|
|
3
3
|
import traceback
|
|
4
|
-
from typing import Dict
|
|
4
|
+
from typing import Any, Dict
|
|
5
5
|
|
|
6
6
|
import polars as pl
|
|
7
7
|
import pyotp
|
|
@@ -98,6 +98,9 @@ class FivePaisa(Broker):
|
|
|
98
98
|
self.load_instrument()
|
|
99
99
|
super(FivePaisa, self).__init__()
|
|
100
100
|
|
|
101
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
102
|
+
return exchange
|
|
103
|
+
|
|
101
104
|
def set_client(self, serialized_client: str):
|
|
102
105
|
self.client: FivePaisaClient = pickle.loads(
|
|
103
106
|
codecs.decode(serialized_client.encode(), "base64")
|
|
@@ -150,7 +153,7 @@ class FivePaisa(Broker):
|
|
|
150
153
|
)
|
|
151
154
|
def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
152
155
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange)
|
|
153
|
-
exchange_name = self.
|
|
156
|
+
exchange_name = self.get_exchange_name(exchange)
|
|
154
157
|
exchange_type = self.get_exchange_type(exchange)
|
|
155
158
|
token = self.symbol_attribute(exchange, tradingsymbol, "token")
|
|
156
159
|
req_list = [
|
|
@@ -448,7 +451,10 @@ class FivePaisa(Broker):
|
|
|
448
451
|
|
|
449
452
|
return False
|
|
450
453
|
|
|
451
|
-
def
|
|
454
|
+
def get_order_type(self, order_type: OrderTypeType) -> Any:
|
|
455
|
+
return order_type
|
|
456
|
+
|
|
457
|
+
def get_exchange_name(self, exchange: ExchangeType):
|
|
452
458
|
return exchange[0]
|
|
453
459
|
|
|
454
460
|
@staticmethod
|
|
@@ -490,7 +496,7 @@ class FivePaisa(Broker):
|
|
|
490
496
|
)
|
|
491
497
|
response = self.client.place_order(
|
|
492
498
|
OrderType=transaction_type[0],
|
|
493
|
-
Exchange=self.
|
|
499
|
+
Exchange=self.get_exchange_name(exchange),
|
|
494
500
|
ExchangeType=FivePaisa.get_exchange_type(exchange),
|
|
495
501
|
ScripCode=token,
|
|
496
502
|
Qty=quantity,
|
|
@@ -551,10 +557,10 @@ class FivePaisa(Broker):
|
|
|
551
557
|
}
|
|
552
558
|
return response
|
|
553
559
|
|
|
554
|
-
def cancel_order(self, order_id: str, variety: str | None = None):
|
|
560
|
+
def cancel_order(self, order_id: str, variety: str | None = None) -> None:
|
|
555
561
|
raise NotImplementedError("Cancel Order Not Implementd")
|
|
556
562
|
|
|
557
|
-
def modify_order(self, order: ModifyOrderRequest):
|
|
563
|
+
def modify_order(self, order: ModifyOrderRequest) -> str:
|
|
558
564
|
raise NotImplementedError("Cancel Order Not Implementd")
|
|
559
565
|
|
|
560
566
|
def get_quantplay_product(self, exchange: ExchangeType, product: ProductType):
|
|
@@ -16,7 +16,7 @@ from queue import Queue
|
|
|
16
16
|
from threading import Lock
|
|
17
17
|
from typing import Any, Dict, List, Literal, Type
|
|
18
18
|
|
|
19
|
-
import pandas as pd
|
|
19
|
+
import pandas as pd # type:ignore
|
|
20
20
|
import polars as pl
|
|
21
21
|
import requests
|
|
22
22
|
from cachetools import TTLCache, cached
|
|
@@ -24,7 +24,6 @@ from cachetools import TTLCache, cached
|
|
|
24
24
|
from quantplay.exception.exceptions import (
|
|
25
25
|
FeatureNotSupported,
|
|
26
26
|
InvalidArgumentException,
|
|
27
|
-
QuantplayOrderPlacementException,
|
|
28
27
|
StaleDataFound,
|
|
29
28
|
)
|
|
30
29
|
from quantplay.model.broker import (
|
|
@@ -32,7 +31,12 @@ from quantplay.model.broker import (
|
|
|
32
31
|
ModifyOrderRequest,
|
|
33
32
|
UserBrokerProfileResponse,
|
|
34
33
|
)
|
|
35
|
-
from quantplay.model.generics import
|
|
34
|
+
from quantplay.model.generics import (
|
|
35
|
+
ProductType,
|
|
36
|
+
QuantplayOrder,
|
|
37
|
+
TransactionType,
|
|
38
|
+
OrderTypeType,
|
|
39
|
+
)
|
|
36
40
|
from quantplay.model.instrument_data import InstrumentDataType
|
|
37
41
|
from quantplay.model.order_event import OrderUpdateEvent
|
|
38
42
|
from quantplay.utils.constant import Constants
|
|
@@ -218,104 +222,6 @@ class Broker(ABC):
|
|
|
218
222
|
def cached_positions(self) -> pl.DataFrame:
|
|
219
223
|
return self.positions()
|
|
220
224
|
|
|
221
|
-
def execute_order(
|
|
222
|
-
self,
|
|
223
|
-
tradingsymbol: str,
|
|
224
|
-
exchange: ExchangeType,
|
|
225
|
-
quantity: int,
|
|
226
|
-
order_type: OrderTypeType,
|
|
227
|
-
transaction_type: TransactionType,
|
|
228
|
-
stoploss: float | None,
|
|
229
|
-
tag: str,
|
|
230
|
-
product: ProductType,
|
|
231
|
-
price: float,
|
|
232
|
-
):
|
|
233
|
-
upper_circuit = None
|
|
234
|
-
trade_price = copy.deepcopy(price)
|
|
235
|
-
if price is None:
|
|
236
|
-
live_data = self.live_data(exchange=exchange, tradingsymbol=tradingsymbol)
|
|
237
|
-
price = live_data["ltp"]
|
|
238
|
-
upper_circuit = live_data["upper_circuit"]
|
|
239
|
-
trade_price = copy.deepcopy(price)
|
|
240
|
-
try:
|
|
241
|
-
if stoploss is not None:
|
|
242
|
-
if transaction_type == "SELL":
|
|
243
|
-
sl_transaction_type = "BUY"
|
|
244
|
-
sl_trigger_price = self.round_to_tick(price * (1 + stoploss))
|
|
245
|
-
|
|
246
|
-
if exchange == "NFO":
|
|
247
|
-
price = sl_trigger_price * 1.05
|
|
248
|
-
elif exchange == "NSE":
|
|
249
|
-
price = sl_trigger_price * 1.01
|
|
250
|
-
else:
|
|
251
|
-
raise Exception("{} not supported for trading".format(exchange))
|
|
252
|
-
|
|
253
|
-
sl_price = self.round_to_tick(price)
|
|
254
|
-
elif transaction_type == "BUY":
|
|
255
|
-
sl_transaction_type = "SELL"
|
|
256
|
-
sl_trigger_price = self.round_to_tick(price * (1 - stoploss))
|
|
257
|
-
|
|
258
|
-
if exchange == self.nfo_exchange:
|
|
259
|
-
price = sl_trigger_price * 0.95
|
|
260
|
-
elif exchange == "NSE":
|
|
261
|
-
price = sl_trigger_price * 0.99
|
|
262
|
-
else:
|
|
263
|
-
raise Exception("{} not supported for trading".format(exchange))
|
|
264
|
-
|
|
265
|
-
sl_price = self.round_to_tick(price)
|
|
266
|
-
|
|
267
|
-
else:
|
|
268
|
-
raise Exception(
|
|
269
|
-
"Invalid transaction_type {}".format(transaction_type)
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
if upper_circuit is not None and sl_price > upper_circuit:
|
|
273
|
-
raise Exception(
|
|
274
|
-
f"[PRICE_BREACHED] {sl_price} is above upper circuit price [{upper_circuit}]"
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
stoploss_order_id = self.place_order(
|
|
278
|
-
tradingsymbol=tradingsymbol,
|
|
279
|
-
exchange=exchange,
|
|
280
|
-
quantity=quantity,
|
|
281
|
-
order_type=self.order_type_sl, # type: ignore
|
|
282
|
-
transaction_type=sl_transaction_type,
|
|
283
|
-
tag=tag,
|
|
284
|
-
product=product,
|
|
285
|
-
price=sl_price,
|
|
286
|
-
trigger_price=sl_trigger_price,
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
if stoploss_order_id is None:
|
|
290
|
-
Constants.logger.error(
|
|
291
|
-
"[ORDER_REJECTED] tradingsymbol {}".format(tradingsymbol)
|
|
292
|
-
)
|
|
293
|
-
raise QuantplayOrderPlacementException(
|
|
294
|
-
"Order reject for {}".format(tradingsymbol)
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
if order_type == "MARKET":
|
|
298
|
-
trade_price: float = 0
|
|
299
|
-
|
|
300
|
-
response = self.place_order(
|
|
301
|
-
tradingsymbol=tradingsymbol,
|
|
302
|
-
exchange=exchange,
|
|
303
|
-
quantity=quantity,
|
|
304
|
-
order_type=order_type,
|
|
305
|
-
transaction_type=transaction_type,
|
|
306
|
-
tag=tag,
|
|
307
|
-
product=product,
|
|
308
|
-
price=trade_price,
|
|
309
|
-
)
|
|
310
|
-
return response
|
|
311
|
-
except Exception as e:
|
|
312
|
-
raise e
|
|
313
|
-
|
|
314
|
-
"""
|
|
315
|
-
Input : quantplay symbol
|
|
316
|
-
Output : broker symbol
|
|
317
|
-
"""
|
|
318
|
-
|
|
319
225
|
def get_symbol(self, symbol: str, exchange: ExchangeType | None = None):
|
|
320
226
|
return symbol
|
|
321
227
|
|
|
@@ -324,12 +230,6 @@ class Broker(ABC):
|
|
|
324
230
|
Output : broker exchange
|
|
325
231
|
"""
|
|
326
232
|
|
|
327
|
-
def get_order_type(self, order_type):
|
|
328
|
-
return order_type
|
|
329
|
-
|
|
330
|
-
def get_exchange(self, exchange):
|
|
331
|
-
return exchange
|
|
332
|
-
|
|
333
233
|
def live_data(self, exchange: ExchangeType, tradingsymbol: str):
|
|
334
234
|
return {
|
|
335
235
|
"ltp": self.ltp(exchange, tradingsymbol),
|
|
@@ -337,7 +237,7 @@ class Broker(ABC):
|
|
|
337
237
|
"lower_circuit": None,
|
|
338
238
|
}
|
|
339
239
|
|
|
340
|
-
def basket_margin(self, basket_orders) -> Dict[str, Any]:
|
|
240
|
+
def basket_margin(self, basket_orders: List[Any]) -> Dict[str, Any]:
|
|
341
241
|
raise FeatureNotSupported("Margin calculator not supported by broker")
|
|
342
242
|
|
|
343
243
|
def verify_rms_square_off(
|
|
@@ -387,9 +287,6 @@ class Broker(ABC):
|
|
|
387
287
|
|
|
388
288
|
return quantity_in_lots * lot_size
|
|
389
289
|
|
|
390
|
-
def get_product(self, product):
|
|
391
|
-
return product
|
|
392
|
-
|
|
393
290
|
def get_lot_size(self, exchange: ExchangeType, tradingsymbol: str):
|
|
394
291
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange=exchange)
|
|
395
292
|
broker_exchange = self.get_exchange(exchange)
|
|
@@ -855,16 +752,6 @@ class Broker(ABC):
|
|
|
855
752
|
|
|
856
753
|
return orders_to_close
|
|
857
754
|
|
|
858
|
-
def add_ltp(self, orders):
|
|
859
|
-
orders.loc[:, "exchange_symbol"] = orders.exchange + ":" + orders.tradingsymbol
|
|
860
|
-
|
|
861
|
-
all_symbols = list(orders.exchange_symbol.unique())
|
|
862
|
-
symbol_ltp = {}
|
|
863
|
-
for exchange_symbol in all_symbols:
|
|
864
|
-
ltp = self.ltp(exchange_symbol.split(":")[0], exchange_symbol.split(":")[1])
|
|
865
|
-
symbol_ltp[exchange_symbol] = ltp
|
|
866
|
-
orders.loc[:, "ltp"] = orders["exchange_symbol"].map(symbol_ltp)
|
|
867
|
-
|
|
868
755
|
def modify_price(
|
|
869
756
|
self,
|
|
870
757
|
order_id: str,
|
|
@@ -889,7 +776,7 @@ class Broker(ABC):
|
|
|
889
776
|
return "BUY"
|
|
890
777
|
raise Exception(f"unknown transaction type [{transaction_type}]")
|
|
891
778
|
|
|
892
|
-
def place_large_orders(self, orders: List) -> List[str | None]:
|
|
779
|
+
def place_large_orders(self, orders: List[QuantplayOrder]) -> List[str | None]:
|
|
893
780
|
order_ids: List[str | None] = []
|
|
894
781
|
orders = sorted(orders, key=lambda d: d["transaction_type"])
|
|
895
782
|
for order in orders:
|
|
@@ -943,7 +830,7 @@ class Broker(ABC):
|
|
|
943
830
|
|
|
944
831
|
new_trading_symbol = f"{underlying}{expiry}{new_strike}{instrument_type}"
|
|
945
832
|
|
|
946
|
-
orders = [
|
|
833
|
+
orders: List[QuantplayOrder] = [
|
|
947
834
|
{
|
|
948
835
|
"tradingsymbol": tradingsymbol,
|
|
949
836
|
"exchange": exchange,
|
|
@@ -1157,3 +1044,12 @@ class Broker(ABC):
|
|
|
1157
1044
|
def margins(self) -> Dict[str, float]:
|
|
1158
1045
|
"""Returns User Margin Summary"""
|
|
1159
1046
|
...
|
|
1047
|
+
|
|
1048
|
+
@abstractmethod
|
|
1049
|
+
def get_order_type(self, order_type: OrderTypeType) -> ...: ...
|
|
1050
|
+
|
|
1051
|
+
@abstractmethod
|
|
1052
|
+
def get_exchange(self, exchange: ExchangeType) -> ...: ...
|
|
1053
|
+
|
|
1054
|
+
@abstractmethod
|
|
1055
|
+
def get_product(self, product: ProductType) -> ...: ...
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
from quantplay.broker.aliceblue import Aliceblue
|
|
7
|
+
from quantplay.broker.angelone import AngelOne
|
|
8
|
+
|
|
9
|
+
from quantplay.broker.five_paisa import FivePaisa
|
|
10
|
+
from quantplay.broker.flattrade import FlatTrade
|
|
11
|
+
from quantplay.broker.iifl_xts import IIFL as IIFL_XTS
|
|
12
|
+
from quantplay.broker.motilal import Motilal
|
|
13
|
+
from quantplay.broker.shoonya import FinvAsia
|
|
14
|
+
from quantplay.broker.upstox import Upstox
|
|
15
|
+
from quantplay.broker.zerodha import Zerodha
|
|
16
|
+
from quantplay.exception.exceptions import InvalidArgumentException
|
|
17
|
+
|
|
18
|
+
from quantplay.utils.pickle_utils import PickleUtils
|
|
19
|
+
from quantplay.utils.caching import InstrumentCache
|
|
20
|
+
|
|
21
|
+
BrokerType = (
|
|
22
|
+
Aliceblue
|
|
23
|
+
| AngelOne
|
|
24
|
+
| FlatTrade
|
|
25
|
+
| Motilal
|
|
26
|
+
| FinvAsia
|
|
27
|
+
| Upstox
|
|
28
|
+
| Zerodha
|
|
29
|
+
| IIFL_XTS
|
|
30
|
+
| FivePaisa
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
instrument_cache = InstrumentCache()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Broker:
|
|
38
|
+
ZERODHA = "Zerodha"
|
|
39
|
+
UPSTOX = "Upstox"
|
|
40
|
+
ALICEBLUE = "Aliceblue"
|
|
41
|
+
FIVEPAISA_OPENAPI = "5Paisa_OpenAPI"
|
|
42
|
+
FINVASIA = "Finvasia"
|
|
43
|
+
FLATTRADE = "Flattrade"
|
|
44
|
+
IIFL_XTS = "IIFL_XTS"
|
|
45
|
+
MOTILAL = "Motilal"
|
|
46
|
+
ANGELONE = "Angelone"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BrokerFactory:
|
|
50
|
+
broker_instruments_map = {
|
|
51
|
+
Broker.ZERODHA: "zerodha_instruments",
|
|
52
|
+
Broker.FINVASIA: "shoonya_instruments",
|
|
53
|
+
Broker.FLATTRADE: "shoonya_instruments",
|
|
54
|
+
Broker.IIFL_XTS: "xts_instruments",
|
|
55
|
+
Broker.MOTILAL: "motilal_instruments",
|
|
56
|
+
Broker.ANGELONE: "angelone_instruments",
|
|
57
|
+
Broker.ALICEBLUE: "aliceblue_instruments",
|
|
58
|
+
Broker.UPSTOX: "upstox_instruments",
|
|
59
|
+
Broker.FIVEPAISA_OPENAPI: "5paisa_instruments",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def __init__(self):
|
|
63
|
+
self.client_broker_data: Dict[str, BrokerType] = {}
|
|
64
|
+
|
|
65
|
+
def get_broker_key(self, username: str, broker_name: str) -> str:
|
|
66
|
+
return "{}:{}".format(username, broker_name)
|
|
67
|
+
|
|
68
|
+
def set_broker_instruments(self, broker_name: str, broker: BrokerType) -> None:
|
|
69
|
+
symbol_data_key = f"{broker_name}_instruments"
|
|
70
|
+
quantplay_symbol_key = f"{broker_name}_qplay_symbols"
|
|
71
|
+
broker_symbol_key = f"{broker_name}_broker_symbols"
|
|
72
|
+
|
|
73
|
+
symbol_data = instrument_cache.get(symbol_data_key)
|
|
74
|
+
quantplay_symbol_map = instrument_cache.get(quantplay_symbol_key)
|
|
75
|
+
broker_symbol_map = instrument_cache.get(broker_symbol_key)
|
|
76
|
+
|
|
77
|
+
if symbol_data is not None:
|
|
78
|
+
broker.symbol_data = symbol_data
|
|
79
|
+
|
|
80
|
+
if broker_name != "Zerodha":
|
|
81
|
+
if quantplay_symbol_map is not None and broker_symbol_map is not None:
|
|
82
|
+
broker.quantplay_symbol_map = quantplay_symbol_map
|
|
83
|
+
broker.broker_symbol_map = broker_symbol_map
|
|
84
|
+
|
|
85
|
+
else:
|
|
86
|
+
broker.initialize_broker_symbol_map()
|
|
87
|
+
instrument_cache.set(
|
|
88
|
+
quantplay_symbol_key, broker.quantplay_symbol_map
|
|
89
|
+
)
|
|
90
|
+
instrument_cache.set(broker_symbol_key, broker.broker_symbol_map)
|
|
91
|
+
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
symbol_data = PickleUtils.load_data(
|
|
96
|
+
BrokerFactory.broker_instruments_map[broker_name]
|
|
97
|
+
)
|
|
98
|
+
broker.symbol_data = symbol_data
|
|
99
|
+
|
|
100
|
+
if broker_name != "Zerodha":
|
|
101
|
+
broker.initialize_broker_symbol_map()
|
|
102
|
+
instrument_cache.set(quantplay_symbol_key, broker.quantplay_symbol_map)
|
|
103
|
+
|
|
104
|
+
instrument_cache.set(symbol_data_key, symbol_data)
|
|
105
|
+
|
|
106
|
+
except Exception:
|
|
107
|
+
traceback.print_exc()
|
|
108
|
+
|
|
109
|
+
if broker_name != "Zerodha":
|
|
110
|
+
broker.load_instrument(BrokerFactory.broker_instruments_map[broker_name])
|
|
111
|
+
else:
|
|
112
|
+
broker.initialize_symbol_data()
|
|
113
|
+
|
|
114
|
+
def store_broker_client(
|
|
115
|
+
self, broker_info: Dict[str, Any], load_instrument: bool = True
|
|
116
|
+
) -> None:
|
|
117
|
+
username = broker_info["username"]
|
|
118
|
+
nickname = broker_info["nickname"]
|
|
119
|
+
|
|
120
|
+
broker_key = self.get_broker_key(username, nickname)
|
|
121
|
+
|
|
122
|
+
broker_data = broker_info["broker_data"]
|
|
123
|
+
broker = broker_info["broker"]
|
|
124
|
+
|
|
125
|
+
broker_client: BrokerType | None = None
|
|
126
|
+
|
|
127
|
+
if broker == "Motilal":
|
|
128
|
+
motial_headers = broker_data["headers"]
|
|
129
|
+
broker_client = Motilal(
|
|
130
|
+
headers=motial_headers, load_instrument=load_instrument
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
elif broker == "Zerodha":
|
|
134
|
+
broker_client = Zerodha(
|
|
135
|
+
wrapper=broker_data["zerodha_wrapper"],
|
|
136
|
+
load_instrument=load_instrument,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
elif broker == "Angelone":
|
|
140
|
+
broker_client = AngelOne(
|
|
141
|
+
user_id=broker_data["user_id"],
|
|
142
|
+
api_key=broker_data["api_key"],
|
|
143
|
+
access_token=broker_data["access_token"],
|
|
144
|
+
refresh_token=broker_data["refresh_token"],
|
|
145
|
+
feed_token=broker_data["feed_token"],
|
|
146
|
+
load_instrument=load_instrument,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
elif broker == Broker.ALICEBLUE:
|
|
150
|
+
broker_client = Aliceblue(client=broker_data["client"])
|
|
151
|
+
|
|
152
|
+
elif broker == Broker.UPSTOX:
|
|
153
|
+
broker_client = Upstox(
|
|
154
|
+
access_token=broker_data["access_token"],
|
|
155
|
+
user_id=broker_data["user_id"],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
elif broker == Broker.FINVASIA:
|
|
159
|
+
finvasia_data = broker_data
|
|
160
|
+
broker_client = FinvAsia(
|
|
161
|
+
order_updates=None,
|
|
162
|
+
user_id=finvasia_data["user_id"],
|
|
163
|
+
password=finvasia_data["password"],
|
|
164
|
+
user_token=finvasia_data["user_token"],
|
|
165
|
+
load_instrument=load_instrument,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
elif broker == Broker.FLATTRADE:
|
|
169
|
+
flattrade_data = broker_data
|
|
170
|
+
broker_client = FlatTrade(
|
|
171
|
+
order_updates=None,
|
|
172
|
+
user_id=flattrade_data["user_id"],
|
|
173
|
+
password=flattrade_data["password"],
|
|
174
|
+
user_token=flattrade_data["user_token"],
|
|
175
|
+
load_instrument=load_instrument,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
elif broker == Broker.FIVEPAISA_OPENAPI:
|
|
179
|
+
broker_client = FivePaisa(
|
|
180
|
+
client=broker_data["client"],
|
|
181
|
+
load_instrument=load_instrument,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.client_broker_data[broker_key] = broker_client
|
|
185
|
+
|
|
186
|
+
elif broker == Broker.IIFL_XTS:
|
|
187
|
+
iifl_xts_data = broker_data
|
|
188
|
+
broker_client = IIFL_XTS(
|
|
189
|
+
wrapper=broker_data["wrapper"],
|
|
190
|
+
md_wrapper=broker_data["md_wrapper"],
|
|
191
|
+
client_id=iifl_xts_data["user_id"],
|
|
192
|
+
load_instrument=load_instrument,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
else:
|
|
196
|
+
raise InvalidArgumentException(f"Broker {broker} not supported")
|
|
197
|
+
|
|
198
|
+
self.client_broker_data[broker_key] = broker_client
|
|
199
|
+
self.set_broker_instruments(broker_name=broker, broker=broker_client)
|
|
200
|
+
|
|
201
|
+
def get_broker_client(self, broker_info: Dict[str, Any]) -> BrokerType:
|
|
202
|
+
username = broker_info["username"]
|
|
203
|
+
nickname = broker_info["nickname"]
|
|
204
|
+
|
|
205
|
+
broker_key = self.get_broker_key(username, nickname)
|
|
206
|
+
|
|
207
|
+
if broker_key in self.client_broker_data:
|
|
208
|
+
return self.client_broker_data[broker_key]
|
|
209
|
+
|
|
210
|
+
self.store_broker_client(broker_info, load_instrument=False)
|
|
211
|
+
|
|
212
|
+
if broker_key in self.client_broker_data:
|
|
213
|
+
return self.client_broker_data[broker_key]
|
|
214
|
+
else:
|
|
215
|
+
raise InvalidArgumentException("Invalid broker API configuration")
|
|
216
|
+
|
|
217
|
+
def clear_instrument_cache(self, broker: str) -> None:
|
|
218
|
+
symbol_data_key = f"{broker}_instruments"
|
|
219
|
+
instrument_cache.delete(symbol_data_key)
|
|
220
|
+
|
|
221
|
+
file_name = self.broker_instruments_map[broker]
|
|
222
|
+
os.system(f"rm /tmp/{file_name}*")
|
|
@@ -4,10 +4,9 @@ from queue import Queue
|
|
|
4
4
|
from typing import Any, Dict
|
|
5
5
|
import requests
|
|
6
6
|
import json
|
|
7
|
-
import pandas as pd
|
|
8
7
|
import polars as pl
|
|
9
8
|
|
|
10
|
-
from quantplay.model.broker import
|
|
9
|
+
from quantplay.model.broker import UserBrokerProfileResponse
|
|
11
10
|
from quantplay.model.generics import (
|
|
12
11
|
ExchangeType,
|
|
13
12
|
OrderTypeType,
|
|
@@ -153,13 +152,6 @@ class Motilal(Broker):
|
|
|
153
152
|
self.nfo_exchange = "NSEFO"
|
|
154
153
|
self.exchange_code_map = {"NFO": "NSEFO", "CDS": "NSECD", "BFO": "BSEFO"}
|
|
155
154
|
|
|
156
|
-
def load_file_by_url(self, exchange):
|
|
157
|
-
data_url = "https://openapi.motilaloswal.com/getscripmastercsv?name={}".format(
|
|
158
|
-
exchange
|
|
159
|
-
)
|
|
160
|
-
df = pd.read_csv(data_url) # type: ignore
|
|
161
|
-
self.instrument_data_by_exchange[exchange] = df
|
|
162
|
-
|
|
163
155
|
def load_instrument(self, file_name: str | None = None) -> None:
|
|
164
156
|
try:
|
|
165
157
|
self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
|
|
@@ -181,6 +173,12 @@ class Motilal(Broker):
|
|
|
181
173
|
|
|
182
174
|
return self.quantplay_symbol_map[symbol]
|
|
183
175
|
|
|
176
|
+
def get_motilal_symbol(self, symbol: str):
|
|
177
|
+
if symbol not in self.quantplay_symbol_map:
|
|
178
|
+
return symbol
|
|
179
|
+
|
|
180
|
+
return self.quantplay_symbol_map[symbol]
|
|
181
|
+
|
|
184
182
|
def get_order_type(self, order_type: OrderTypeType):
|
|
185
183
|
if order_type == OrderType.sl:
|
|
186
184
|
return "STOPLOSS"
|
|
@@ -198,9 +196,9 @@ class Motilal(Broker):
|
|
|
198
196
|
return "DELIVERY"
|
|
199
197
|
elif product == "NRML":
|
|
200
198
|
return "NORMAL"
|
|
201
|
-
elif product == "
|
|
202
|
-
return "
|
|
203
|
-
return
|
|
199
|
+
elif product == "MIS":
|
|
200
|
+
return "NORMAL"
|
|
201
|
+
return product
|
|
204
202
|
|
|
205
203
|
def place_order_quantity(
|
|
206
204
|
self, quantity: int, tradingsymbol: str, exchange: ExchangeType
|
|
@@ -210,8 +208,8 @@ class Motilal(Broker):
|
|
|
210
208
|
|
|
211
209
|
return quantity_in_lots
|
|
212
210
|
|
|
213
|
-
def get_lot_size(self, exchange, tradingsymbol: str):
|
|
214
|
-
tradingsymbol = self.get_symbol(tradingsymbol
|
|
211
|
+
def get_lot_size(self, exchange: str, tradingsymbol: str):
|
|
212
|
+
tradingsymbol = self.get_symbol(tradingsymbol)
|
|
215
213
|
if exchange == "NSEFO":
|
|
216
214
|
exchange = "NFO"
|
|
217
215
|
elif exchange == "BSEFO":
|
|
@@ -303,6 +303,9 @@ class Noren(Broker):
|
|
|
303
303
|
|
|
304
304
|
return data
|
|
305
305
|
|
|
306
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
307
|
+
return exchange
|
|
308
|
+
|
|
306
309
|
@retry(
|
|
307
310
|
wait_exponential_multiplier=3000,
|
|
308
311
|
wait_exponential_max=10000,
|
|
@@ -312,8 +315,6 @@ class Noren(Broker):
|
|
|
312
315
|
order_id = order["order_id"]
|
|
313
316
|
existing_details = self.order_history(order_id)
|
|
314
317
|
|
|
315
|
-
order["order_type"] = self.get_order_type(order["order_type"]) # type: ignore
|
|
316
|
-
|
|
317
318
|
if "trigger_price" not in order:
|
|
318
319
|
order["trigger_price"] = None
|
|
319
320
|
|
|
@@ -323,11 +324,13 @@ class Noren(Broker):
|
|
|
323
324
|
if "quantity" not in order:
|
|
324
325
|
order["quantity"] = int(existing_details["quantity"])
|
|
325
326
|
|
|
327
|
+
order["order_type"] = self.get_order_type(order["order_type"]) # type: ignore
|
|
328
|
+
|
|
326
329
|
try:
|
|
327
330
|
logger.info(f"[MODIFYING_ORDER] {order}")
|
|
328
331
|
response = self.api.modify_order( # type:ignore
|
|
329
332
|
orderno=order_id,
|
|
330
|
-
exchange=existing_details["exchange"],
|
|
333
|
+
exchange=existing_details["exchange"], # type: ignore
|
|
331
334
|
tradingsymbol=existing_details["tradingsymbol"],
|
|
332
335
|
newprice_type=order["order_type"],
|
|
333
336
|
newquantity=order["quantity"],
|
|
@@ -115,7 +115,7 @@ class Upstox(Broker):
|
|
|
115
115
|
|
|
116
116
|
return exchange
|
|
117
117
|
|
|
118
|
-
def get_quantplay_exchange(self, exchange) -> ExchangeType:
|
|
118
|
+
def get_quantplay_exchange(self, exchange: str) -> ExchangeType:
|
|
119
119
|
exchange_map: Dict[str, ExchangeType] = {
|
|
120
120
|
"NSE_FO": "NFO",
|
|
121
121
|
"NSECD": "CDS",
|
|
@@ -125,7 +125,7 @@ class Upstox(Broker):
|
|
|
125
125
|
if exchange in exchange_map:
|
|
126
126
|
return exchange_map[exchange]
|
|
127
127
|
|
|
128
|
-
return exchange
|
|
128
|
+
return exchange # type: ignore
|
|
129
129
|
|
|
130
130
|
def get_quantplay_symbol(self, symbol: str):
|
|
131
131
|
return symbol
|
|
@@ -133,6 +133,9 @@ class Upstox(Broker):
|
|
|
133
133
|
def get_lot_size(self, exchange: ExchangeType, tradingsymbol: str):
|
|
134
134
|
return int(self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["lot_size"])
|
|
135
135
|
|
|
136
|
+
def get_order_type(self, order_type: OrderTypeType) -> Any:
|
|
137
|
+
return order_type
|
|
138
|
+
|
|
136
139
|
@retry(
|
|
137
140
|
wait_exponential_multiplier=1000,
|
|
138
141
|
wait_exponential_max=10000,
|
|
@@ -158,7 +161,7 @@ class Upstox(Broker):
|
|
|
158
161
|
self.handle_exception(e)
|
|
159
162
|
Constants.logger.error("Exception when calling MarketQuoteApi->ltp: %s\n" % e)
|
|
160
163
|
|
|
161
|
-
return ltp
|
|
164
|
+
return ltp # type: ignore
|
|
162
165
|
|
|
163
166
|
@retry(
|
|
164
167
|
wait_exponential_multiplier=1000,
|
|
@@ -216,7 +219,7 @@ class Upstox(Broker):
|
|
|
216
219
|
product: ProductType,
|
|
217
220
|
price: float,
|
|
218
221
|
trigger_price: float | None = None,
|
|
219
|
-
):
|
|
222
|
+
) -> str:
|
|
220
223
|
exchange = self.get_quantplay_exchange(exchange)
|
|
221
224
|
try:
|
|
222
225
|
Constants.logger.info(
|
|
@@ -297,10 +300,10 @@ class Upstox(Broker):
|
|
|
297
300
|
profile_data = api_response.data # type:ignore
|
|
298
301
|
|
|
299
302
|
response: UserBrokerProfileResponse = {
|
|
300
|
-
"user_id": profile_data.user_id,
|
|
301
|
-
"full_name": profile_data.user_name,
|
|
302
|
-
"exchanges": profile_data.exchanges,
|
|
303
|
-
"email": profile_data.email,
|
|
303
|
+
"user_id": profile_data.user_id, # type: ignore
|
|
304
|
+
"full_name": profile_data.user_name, # type: ignore
|
|
305
|
+
"exchanges": profile_data.exchanges, # type: ignore
|
|
306
|
+
"email": profile_data.email, # type: ignore
|
|
304
307
|
}
|
|
305
308
|
self.email = response["email"]
|
|
306
309
|
self.enabled_exchanges = response["exchanges"]
|
|
@@ -370,8 +373,9 @@ class Upstox(Broker):
|
|
|
370
373
|
try:
|
|
371
374
|
# Get Positions
|
|
372
375
|
api_response = api_instance.get_positions(self.api_version) # type: ignore
|
|
373
|
-
positions = [
|
|
374
|
-
position.to_dict()
|
|
376
|
+
positions = [ # type:ignore
|
|
377
|
+
position.to_dict() # type:ignore
|
|
378
|
+
for position in api_response.data # type:ignore
|
|
375
379
|
]
|
|
376
380
|
positions_df = pl.DataFrame(positions)
|
|
377
381
|
except ApiException as e:
|
|
@@ -634,7 +638,7 @@ class Upstox(Broker):
|
|
|
634
638
|
configuration.access_token = self.access_token
|
|
635
639
|
|
|
636
640
|
# Get portfolio stream feed authorize
|
|
637
|
-
response = self.get_portfolio_stream_feed_authorize(
|
|
641
|
+
response = self.get_portfolio_stream_feed_authorize( # type: ignore
|
|
638
642
|
self.api_version, configuration
|
|
639
643
|
)
|
|
640
644
|
|
|
@@ -651,13 +655,13 @@ class Upstox(Broker):
|
|
|
651
655
|
|
|
652
656
|
def get_portfolio_stream_feed_authorize(
|
|
653
657
|
self, api_version: str, configuration: upstox_client.Configuration
|
|
654
|
-
):
|
|
658
|
+
) -> Any:
|
|
655
659
|
api_instance = upstox_client.WebsocketApi(upstox_client.ApiClient(configuration))
|
|
656
660
|
api_response = api_instance.get_portfolio_stream_feed_authorize(api_version) # type: ignore
|
|
657
661
|
|
|
658
|
-
return api_response
|
|
662
|
+
return api_response # type: ignore
|
|
659
663
|
|
|
660
|
-
def get_quantplay_product(self, exchange, product):
|
|
664
|
+
def get_quantplay_product(self, exchange: ExchangeType, product: str):
|
|
661
665
|
product_map = {"D": "CNC", "I": "MIS"}
|
|
662
666
|
|
|
663
667
|
if product in product_map:
|
|
@@ -703,14 +703,22 @@ class XTS(Broker):
|
|
|
703
703
|
trigger_price: float | None = None,
|
|
704
704
|
order_type: OrderTypeType | None = None,
|
|
705
705
|
):
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
706
|
+
data: ModifyOrderRequest = {
|
|
707
|
+
"order_id": str(order_id),
|
|
708
|
+
"price": price,
|
|
709
|
+
"trigger_price": trigger_price,
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if order_type is not None:
|
|
713
|
+
data["order_type"] = order_type
|
|
714
|
+
|
|
715
|
+
self.modify_order(data)
|
|
716
|
+
|
|
717
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
718
|
+
return exchange
|
|
719
|
+
|
|
720
|
+
def get_product(self, product: ProductType) -> Any:
|
|
721
|
+
return product
|
|
714
722
|
|
|
715
723
|
def stream_order_updates(self):
|
|
716
724
|
if self.wrapper.token is None:
|
|
@@ -197,6 +197,15 @@ class Zerodha(Broker):
|
|
|
197
197
|
def cancel_order(self, order_id: str, variety: str | None = "regular"):
|
|
198
198
|
self.wrapper.cancel_order(order_id=order_id, variety=variety) # type: ignore
|
|
199
199
|
|
|
200
|
+
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
201
|
+
return exchange
|
|
202
|
+
|
|
203
|
+
def get_order_type(self, order_type: OrderTypeType) -> Any:
|
|
204
|
+
return order_type
|
|
205
|
+
|
|
206
|
+
def get_product(self, product: ProductType) -> Any:
|
|
207
|
+
return product
|
|
208
|
+
|
|
200
209
|
@retry(
|
|
201
210
|
wait_exponential_multiplier=3000,
|
|
202
211
|
wait_exponential_max=10000,
|
|
@@ -15,7 +15,7 @@ class ModifyOrderRequest(TypedDict):
|
|
|
15
15
|
quantity: NotRequired[int]
|
|
16
16
|
exchange: NotRequired[ExchangeType]
|
|
17
17
|
trigger_price: NotRequired[float | None]
|
|
18
|
-
order_type: NotRequired[OrderTypeType
|
|
18
|
+
order_type: NotRequired[OrderTypeType]
|
|
19
19
|
price: NotRequired[float]
|
|
20
20
|
|
|
21
21
|
|
|
@@ -74,3 +74,11 @@ class XTSTypes:
|
|
|
74
74
|
PositionSqureOffModeType = Literal["DayWise", "NetWise"]
|
|
75
75
|
PositionSquareOffQuantityTypeType = Literal["Percentage", "ExactQty"]
|
|
76
76
|
DayOrNetType = Literal["DAY", "NET"]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class QuantplayOrder(TypedDict):
|
|
80
|
+
exchange: ExchangeType
|
|
81
|
+
transaction_type: TransactionType
|
|
82
|
+
tradingsymbol: str
|
|
83
|
+
quantity: int
|
|
84
|
+
product: ProductType
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from threading import Event, Thread
|
|
2
|
+
from time import sleep
|
|
3
|
+
from typing import Any, Dict, Hashable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InstrumentCache:
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
self.cache: Dict[Hashable, Any] = {}
|
|
9
|
+
self.ttl = 300
|
|
10
|
+
|
|
11
|
+
self.stop_event = Event()
|
|
12
|
+
self.stop_event.set()
|
|
13
|
+
|
|
14
|
+
clear_thread = Thread(target=self.auto_clear, daemon=True)
|
|
15
|
+
clear_thread.start()
|
|
16
|
+
|
|
17
|
+
def __del__(self) -> None:
|
|
18
|
+
self.stop_event.clear()
|
|
19
|
+
|
|
20
|
+
def set(self, key: str, data: Any) -> None:
|
|
21
|
+
self.cache[key] = data
|
|
22
|
+
|
|
23
|
+
def get(self, key: str) -> Any | None:
|
|
24
|
+
return self.cache.get(key, None)
|
|
25
|
+
|
|
26
|
+
def delete(self, key: str) -> None:
|
|
27
|
+
del self.cache[key]
|
|
28
|
+
|
|
29
|
+
def clear(self) -> None:
|
|
30
|
+
self.cache = {}
|
|
31
|
+
|
|
32
|
+
def auto_clear(self) -> None:
|
|
33
|
+
while self.stop_event.is_set():
|
|
34
|
+
sleep(self.ttl)
|
|
35
|
+
self.clear()
|
|
@@ -31,6 +31,7 @@ quantplay/broker/ft_utils/flattrade_utils.py
|
|
|
31
31
|
quantplay/broker/ft_utils/ft_noren.py
|
|
32
32
|
quantplay/broker/generics/__init__.py
|
|
33
33
|
quantplay/broker/generics/broker.py
|
|
34
|
+
quantplay/broker/generics/broker_factory.py
|
|
34
35
|
quantplay/broker/uplink/__init__.py
|
|
35
36
|
quantplay/broker/uplink/uplink_utils.py
|
|
36
37
|
quantplay/broker/xts_utils/Connect.py
|
|
@@ -45,6 +46,7 @@ quantplay/model/generics.py
|
|
|
45
46
|
quantplay/model/instrument_data.py
|
|
46
47
|
quantplay/model/order_event.py
|
|
47
48
|
quantplay/utils/__init__.py
|
|
49
|
+
quantplay/utils/caching.py
|
|
48
50
|
quantplay/utils/constant.py
|
|
49
51
|
quantplay/utils/exchange.py
|
|
50
52
|
quantplay/utils/number_utils.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|