quantplay 2.0.32__tar.gz → 2.0.33__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.32 → quantplay-2.0.33}/PKG-INFO +1 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/aliceblue.py +5 -4
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/angelone.py +4 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/auto_login/aliceblue.py +0 -2
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/finvasia_utils/fa_noren.py +16 -10
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/ft_utils/flattrade_utils.py +9 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/ft_utils/ft_noren.py +19 -13
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/generics/broker.py +83 -68
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/kite_utils.py +0 -2
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/motilal.py +17 -16
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/noren.py +2 -3
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/uplink/uplink_utils.py +0 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/upstox.py +5 -5
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/xts.py +14 -12
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/xts_utils/Connect.py +52 -44
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/zerodha.py +5 -4
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/model/broker.py +19 -0
- quantplay-2.0.33/quantplay/model/instrument_data.py +16 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/utils/pickle_utils.py +3 -2
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/utils/selenium_utils.py +1 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/wrapper/aws/s3.py +3 -6
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay.egg-info/SOURCES.txt +1 -11
- {quantplay-2.0.32 → quantplay-2.0.33}/setup.py +1 -1
- quantplay-2.0.32/quantplay/strategies/options/__init__.py +0 -0
- quantplay-2.0.32/quantplay/strategies/options/intraday/__init__.py +0 -0
- quantplay-2.0.32/quantplay/strategies/options/intraday/ladder.py +0 -65
- quantplay-2.0.32/quantplay/strategies/options/intraday/musk.py +0 -72
- quantplay-2.0.32/quantplay/strategies/options/intraday/short_straddle.py +0 -10
- quantplay-2.0.32/quantplay/utils/__init__.py +0 -0
- quantplay-2.0.32/quantplay/wrapper/__init__.py +0 -0
- quantplay-2.0.32/quantplay/wrapper/aws/__init__.py +0 -0
- quantplay-2.0.32/tests/__init__.py +0 -0
- quantplay-2.0.32/tests/wrapper/__init__.py +0 -0
- quantplay-2.0.32/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/README.md +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/pyproject.toml +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/five_paisa.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/py.typed +0 -0
- {quantplay-2.0.32/quantplay/strategies → quantplay-2.0.33/quantplay/utils}/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.32/quantplay/strategies/equities → quantplay-2.0.33/quantplay/wrapper}/__init__.py +0 -0
- {quantplay-2.0.32/quantplay/strategies/equities/intraday → quantplay-2.0.33/quantplay/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/setup.cfg +0 -0
- {quantplay-2.0.32/quantplay/strategies/equities/overnight → quantplay-2.0.33/tests}/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/tests/conftest.py +0 -0
- {quantplay-2.0.32/quantplay/strategies/futures → quantplay-2.0.33/tests/wrapper}/__init__.py +0 -0
- {quantplay-2.0.32/quantplay/strategies/futures/overnight → quantplay-2.0.33/tests/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.32 → quantplay-2.0.33}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -110,15 +110,16 @@ class Aliceblue(Broker):
|
|
|
110
110
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
|
|
111
111
|
except Exception:
|
|
112
112
|
self.instrument_data = S3Utils.read_csv(
|
|
113
|
-
"quantplay-market-data",
|
|
114
|
-
"symbol_data/aliceblue_instruments.csv",
|
|
115
|
-
read_from_local=False,
|
|
113
|
+
"quantplay-market-data", "symbol_data/aliceblue_instruments.csv"
|
|
116
114
|
)
|
|
117
115
|
self.initialize_symbol_data(save_as="aliceblue_instruments")
|
|
118
116
|
|
|
119
117
|
self.initialize_broker_symbol_map()
|
|
120
118
|
|
|
121
|
-
def get_transaction_type(
|
|
119
|
+
def get_transaction_type(
|
|
120
|
+
self,
|
|
121
|
+
transaction_type: Literal["BUY", "SELL", "B", "S"],
|
|
122
|
+
):
|
|
122
123
|
if (
|
|
123
124
|
transaction_type == "BUY"
|
|
124
125
|
or transaction_type == TransactionType.Buy.value
|
|
@@ -7,6 +7,7 @@ from queue import Queue
|
|
|
7
7
|
import traceback
|
|
8
8
|
from typing import Callable, Dict
|
|
9
9
|
|
|
10
|
+
from SmartApi.smartExceptions import DataException
|
|
10
11
|
import polars as pl
|
|
11
12
|
import pyotp
|
|
12
13
|
from requests.exceptions import ConnectionError, ConnectTimeout
|
|
@@ -118,7 +119,6 @@ class AngelOne(Broker):
|
|
|
118
119
|
self.instrument_data = S3Utils.read_csv(
|
|
119
120
|
"quantplay-market-data",
|
|
120
121
|
"symbol_data/angelone_instruments.csv",
|
|
121
|
-
read_from_local=False,
|
|
122
122
|
)
|
|
123
123
|
self.initialize_symbol_data(save_as="angelone_instruments")
|
|
124
124
|
|
|
@@ -576,6 +576,9 @@ class AngelOne(Broker):
|
|
|
576
576
|
return response
|
|
577
577
|
except (TokenException, InvalidArgumentException):
|
|
578
578
|
raise
|
|
579
|
+
except DataException as e:
|
|
580
|
+
# TODO: Catch and Check for Others Exceptions
|
|
581
|
+
raise BrokerException(str(e))
|
|
579
582
|
except Exception:
|
|
580
583
|
traceback.print_exc()
|
|
581
584
|
raise RetryableException("Failed to fetch user profile")
|
|
@@ -79,8 +79,6 @@ class AliceblueLogin:
|
|
|
79
79
|
try:
|
|
80
80
|
driver = Selenium().get_browser(headless=True)
|
|
81
81
|
|
|
82
|
-
# TODO api should be fetched from configuration
|
|
83
|
-
|
|
84
82
|
driver.get("https://ant.aliceblueonline.com/")
|
|
85
83
|
AliceblueLogin.click_on_login(driver)
|
|
86
84
|
AliceblueLogin.enter_user_id(driver, user_id)
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
import urllib.parse
|
|
8
8
|
from datetime import datetime as dt
|
|
9
9
|
from time import sleep
|
|
10
|
-
from typing import Literal
|
|
10
|
+
from typing import Dict, Literal, TypedDict
|
|
11
11
|
|
|
12
12
|
import requests
|
|
13
13
|
import websocket
|
|
@@ -55,20 +55,26 @@ class BuyorSell:
|
|
|
55
55
|
Sell = "S"
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def reportmsg(msg):
|
|
58
|
+
def reportmsg(msg: str):
|
|
59
59
|
logger.debug(msg)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def reporterror(msg):
|
|
62
|
+
def reporterror(msg: str):
|
|
63
63
|
logger.error(msg)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def reportinfo(msg):
|
|
66
|
+
def reportinfo(msg: str):
|
|
67
67
|
logger.info(msg)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
class FAServiceConfig(TypedDict):
|
|
71
|
+
host: str
|
|
72
|
+
routes: Dict[str, str]
|
|
73
|
+
websocket_endpoint: str
|
|
74
|
+
|
|
75
|
+
|
|
70
76
|
class FA_NorenApi:
|
|
71
|
-
__service_config = {
|
|
77
|
+
__service_config: FAServiceConfig = {
|
|
72
78
|
"host": "http://wsapihost/",
|
|
73
79
|
"routes": {
|
|
74
80
|
"authorize": "/QuickAuth",
|
|
@@ -123,18 +129,18 @@ class FA_NorenApi:
|
|
|
123
129
|
|
|
124
130
|
while not self.__stop_event.is_set():
|
|
125
131
|
try:
|
|
126
|
-
self.__websocket.run_forever(ping_interval=3, ping_payload='{"t":"h"}')
|
|
132
|
+
self.__websocket.run_forever(ping_interval=3, ping_payload='{"t":"h"}') # type: ignore
|
|
127
133
|
except Exception:
|
|
128
134
|
pass
|
|
129
135
|
|
|
130
136
|
sleep(0.1)
|
|
131
137
|
|
|
132
|
-
def __ws_send(self,
|
|
138
|
+
def __ws_send(self, data: str | bytes):
|
|
133
139
|
while not self.__websocket_connected or self.__websocket is None:
|
|
134
140
|
sleep(0.05)
|
|
135
141
|
|
|
136
142
|
with self.__ws_mutex:
|
|
137
|
-
ret = self.__websocket.send(
|
|
143
|
+
ret = self.__websocket.send(data)
|
|
138
144
|
|
|
139
145
|
return ret
|
|
140
146
|
|
|
@@ -235,7 +241,7 @@ class FA_NorenApi:
|
|
|
235
241
|
|
|
236
242
|
self.__stop_event.set()
|
|
237
243
|
self.__websocket_connected = False
|
|
238
|
-
self.__websocket.close()
|
|
244
|
+
self.__websocket.close() # type: ignore
|
|
239
245
|
self.__ws_thread.join()
|
|
240
246
|
|
|
241
247
|
def login(
|
|
@@ -941,7 +947,7 @@ class FA_NorenApi:
|
|
|
941
947
|
|
|
942
948
|
headers = {"Content-Type": "application/json; charset=utf-8"}
|
|
943
949
|
res = requests.post(url, data=payload, headers=headers)
|
|
944
|
-
reportmsg(res)
|
|
950
|
+
reportmsg(str(res))
|
|
945
951
|
|
|
946
952
|
if res.status_code != 200:
|
|
947
953
|
return None
|
|
@@ -12,6 +12,7 @@ from quantplay.exception.exceptions import (
|
|
|
12
12
|
WrongLibrarySetup,
|
|
13
13
|
retry_exception,
|
|
14
14
|
)
|
|
15
|
+
from quantplay.utils.constant import Constants
|
|
15
16
|
from quantplay.utils.selenium_utils import Selenium
|
|
16
17
|
|
|
17
18
|
|
|
@@ -56,7 +57,14 @@ class FlatTradeUtils:
|
|
|
56
57
|
time.sleep(2)
|
|
57
58
|
|
|
58
59
|
url = driver.current_url
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
# TODO: IndexError: list index out of range
|
|
63
|
+
request_token = url.split("code=")[1].split("&")[0]
|
|
64
|
+
except Exception as e:
|
|
65
|
+
Constants.logger.error(f"Flattrade Selenium Error for {url}")
|
|
66
|
+
traceback.print_exc()
|
|
67
|
+
raise e
|
|
60
68
|
|
|
61
69
|
driver.close()
|
|
62
70
|
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
import urllib.parse
|
|
8
8
|
from datetime import datetime as dt
|
|
9
9
|
from time import sleep
|
|
10
|
-
from typing import Literal
|
|
10
|
+
from typing import Dict, Literal, TypedDict
|
|
11
11
|
|
|
12
12
|
import requests
|
|
13
13
|
import websocket
|
|
@@ -55,20 +55,26 @@ class BuyorSell:
|
|
|
55
55
|
Sell = "S"
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def reportmsg(msg):
|
|
58
|
+
def reportmsg(msg: str):
|
|
59
59
|
logger.debug(msg)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def reporterror(msg):
|
|
62
|
+
def reporterror(msg: str):
|
|
63
63
|
logger.error(msg)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def reportinfo(msg):
|
|
66
|
+
def reportinfo(msg: str):
|
|
67
67
|
logger.info(msg)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
class FTServiceConfig(TypedDict):
|
|
71
|
+
host: str
|
|
72
|
+
routes: Dict[str, str]
|
|
73
|
+
websocket_endpoint: str
|
|
74
|
+
|
|
75
|
+
|
|
70
76
|
class FT_NorenApi:
|
|
71
|
-
__service_config = {
|
|
77
|
+
__service_config: FTServiceConfig = {
|
|
72
78
|
"host": "http://wsapihost/",
|
|
73
79
|
"routes": {
|
|
74
80
|
"authorize": "/QuickAuth",
|
|
@@ -102,7 +108,7 @@ class FT_NorenApi:
|
|
|
102
108
|
"websocket_endpoint": "wss://wsendpoint/",
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
def __init__(self, host, websocket_endpoint):
|
|
111
|
+
def __init__(self, host: str, websocket_endpoint: str):
|
|
106
112
|
self.__service_config["host"] = host
|
|
107
113
|
self.__service_config["websocket_endpoint"] = websocket_endpoint
|
|
108
114
|
|
|
@@ -121,18 +127,18 @@ class FT_NorenApi:
|
|
|
121
127
|
|
|
122
128
|
while not self.__stop_event.is_set():
|
|
123
129
|
try:
|
|
124
|
-
self.__websocket.run_forever(ping_interval=3, ping_payload='{"t":"h"}')
|
|
130
|
+
self.__websocket.run_forever(ping_interval=3, ping_payload='{"t":"h"}') # type: ignore
|
|
125
131
|
except Exception:
|
|
126
132
|
pass
|
|
127
133
|
|
|
128
134
|
sleep(0.1)
|
|
129
135
|
|
|
130
|
-
def __ws_send(self,
|
|
136
|
+
def __ws_send(self, data: bytes | str):
|
|
131
137
|
while not self.__websocket_connected or self.__websocket is None:
|
|
132
138
|
sleep(0.05)
|
|
133
139
|
|
|
134
140
|
with self.__ws_mutex:
|
|
135
|
-
ret = self.__websocket.send(
|
|
141
|
+
ret = self.__websocket.send(data)
|
|
136
142
|
return ret
|
|
137
143
|
|
|
138
144
|
def __on_close_callback(self, wsapp, close_status_code, close_msg):
|
|
@@ -232,7 +238,7 @@ class FT_NorenApi:
|
|
|
232
238
|
|
|
233
239
|
self.__stop_event.set()
|
|
234
240
|
self.__websocket_connected = False
|
|
235
|
-
self.__websocket.close()
|
|
241
|
+
self.__websocket.close() # type: ignore
|
|
236
242
|
self.__ws_thread.join()
|
|
237
243
|
|
|
238
244
|
def login(
|
|
@@ -336,7 +342,7 @@ class FT_NorenApi:
|
|
|
336
342
|
|
|
337
343
|
return resDict
|
|
338
344
|
|
|
339
|
-
def subscribe(self, instrument, feed_type=FeedType.TOUCHLINE):
|
|
345
|
+
def subscribe(self, instrument, feed_type: int = FeedType.TOUCHLINE):
|
|
340
346
|
values = {}
|
|
341
347
|
|
|
342
348
|
if feed_type == FeedType.TOUCHLINE:
|
|
@@ -355,7 +361,7 @@ class FT_NorenApi:
|
|
|
355
361
|
|
|
356
362
|
self.__ws_send(data)
|
|
357
363
|
|
|
358
|
-
def unsubscribe(self, instrument, feed_type=FeedType.TOUCHLINE):
|
|
364
|
+
def unsubscribe(self, instrument, feed_type: int = FeedType.TOUCHLINE):
|
|
359
365
|
values = {}
|
|
360
366
|
|
|
361
367
|
if feed_type == FeedType.TOUCHLINE:
|
|
@@ -934,7 +940,7 @@ class FT_NorenApi:
|
|
|
934
940
|
|
|
935
941
|
headers = {"Content-Type": "application/json; charset=utf-8"}
|
|
936
942
|
res = requests.post(url, data=payload, headers=headers)
|
|
937
|
-
reportmsg(res)
|
|
943
|
+
reportmsg(str(res))
|
|
938
944
|
|
|
939
945
|
if res.status_code != 200:
|
|
940
946
|
return None
|
|
@@ -30,10 +30,10 @@ from quantplay.exception.exceptions import (
|
|
|
30
30
|
)
|
|
31
31
|
from quantplay.model.broker import (
|
|
32
32
|
ExchangeType,
|
|
33
|
-
ModifyOrderRequest,
|
|
34
33
|
UserBrokerProfileResponse,
|
|
35
34
|
)
|
|
36
35
|
from quantplay.model.generics import ProductType, TransactionType, OrderTypeType
|
|
36
|
+
from quantplay.model.instrument_data import InstrumentDataType
|
|
37
37
|
from quantplay.model.order_event import OrderUpdateEvent
|
|
38
38
|
from quantplay.utils.constant import Constants
|
|
39
39
|
from quantplay.utils.exchange import Market as MarketConstants
|
|
@@ -158,15 +158,16 @@ class Broker:
|
|
|
158
158
|
raise StaleDataFound(f"Couldn't find symbol data for [{exchange}:{symbol}]")
|
|
159
159
|
|
|
160
160
|
def initialize_symbol_data(self, save_as: str | None = None) -> None:
|
|
161
|
-
|
|
162
|
-
instruments =
|
|
163
|
-
|
|
161
|
+
instruments_df = self.instrument_data
|
|
162
|
+
instruments: List[InstrumentDataType] = instruments_df.to_dict("records") # type: ignore
|
|
163
|
+
|
|
164
|
+
self.symbol_data: Dict[str, InstrumentDataType] = {}
|
|
164
165
|
for instrument in instruments:
|
|
165
166
|
exchange = instrument["exchange"]
|
|
166
167
|
tradingsymbol = instrument["broker_symbol"]
|
|
167
168
|
|
|
168
169
|
instrument_data = copy.deepcopy(instrument)
|
|
169
|
-
self.symbol_data["{}:{}"
|
|
170
|
+
self.symbol_data[f"{exchange}:{tradingsymbol}"] = instrument_data
|
|
170
171
|
|
|
171
172
|
if save_as:
|
|
172
173
|
PickleUtils.save_data(self.symbol_data, save_as)
|
|
@@ -202,13 +203,12 @@ class Broker:
|
|
|
202
203
|
self.instrument_data = S3Utils.read_csv(
|
|
203
204
|
"quantplay-market-data",
|
|
204
205
|
f"symbol_data/{file_name}.csv",
|
|
205
|
-
read_from_local=False,
|
|
206
206
|
)
|
|
207
207
|
self.initialize_symbol_data(save_as=file_name)
|
|
208
208
|
|
|
209
209
|
self.initialize_broker_symbol_map()
|
|
210
210
|
|
|
211
|
-
def round_to_tick(self, number):
|
|
211
|
+
def round_to_tick(self, number: int | float) -> float:
|
|
212
212
|
return round(number * 20) / 20
|
|
213
213
|
|
|
214
214
|
def get_user_id(self):
|
|
@@ -236,64 +236,66 @@ class Broker:
|
|
|
236
236
|
seg_condition = [
|
|
237
237
|
(
|
|
238
238
|
(self.instrument_data["instrument"].str.contains("FUT"))
|
|
239
|
-
& (self.instrument_data
|
|
239
|
+
& (self.instrument_data["instrument"] != "OPTFUT")
|
|
240
240
|
)
|
|
241
241
|
]
|
|
242
242
|
|
|
243
243
|
tradingsymbol = [
|
|
244
|
-
self.instrument_data
|
|
245
|
-
+ self.instrument_data
|
|
246
|
-
+ self.instrument_data
|
|
244
|
+
self.instrument_data["tradingsymbol"]
|
|
245
|
+
+ self.instrument_data["expiry_year"]
|
|
246
|
+
+ self.instrument_data["month"]
|
|
247
247
|
+ "FUT"
|
|
248
248
|
]
|
|
249
249
|
|
|
250
250
|
self.instrument_data.loc[:, "tradingsymbol"] = np.select(
|
|
251
|
-
seg_condition, tradingsymbol, default=self.instrument_data
|
|
251
|
+
seg_condition, tradingsymbol, default=self.instrument_data["tradingsymbol"]
|
|
252
252
|
)
|
|
253
253
|
|
|
254
254
|
def add_quantplay_opt_tradingsymbol(self):
|
|
255
255
|
seg_condition = self.instrument_data["strike_price"] > 0
|
|
256
256
|
weekly_option_condition = (
|
|
257
|
-
self.instrument_data
|
|
258
|
-
== self.instrument_data
|
|
259
|
-
) & (self.instrument_data
|
|
257
|
+
self.instrument_data["expiry"].dt.month
|
|
258
|
+
== self.instrument_data["next_expiry"].dt.month
|
|
259
|
+
) & (self.instrument_data["exchange"] == "NFO")
|
|
260
260
|
month_option_condition = (
|
|
261
|
-
self.instrument_data
|
|
262
|
-
!= self.instrument_data
|
|
263
|
-
) | (self.instrument_data
|
|
261
|
+
self.instrument_data["expiry"].dt.month
|
|
262
|
+
!= self.instrument_data["next_expiry"].dt.month
|
|
263
|
+
) | (self.instrument_data["exchange"] == "MCX")
|
|
264
264
|
|
|
265
265
|
self.instrument_data.loc[:, "tradingsymbol"] = np.where(
|
|
266
266
|
seg_condition,
|
|
267
|
-
self.instrument_data
|
|
268
|
-
self.instrument_data
|
|
267
|
+
self.instrument_data["tradingsymbol"] + self.instrument_data["expiry_year"],
|
|
268
|
+
self.instrument_data["tradingsymbol"],
|
|
269
269
|
)
|
|
270
270
|
|
|
271
271
|
self.instrument_data.loc[:, "tradingsymbol"] = np.where(
|
|
272
272
|
seg_condition & weekly_option_condition,
|
|
273
|
-
self.instrument_data
|
|
274
|
-
self.instrument_data
|
|
273
|
+
self.instrument_data["tradingsymbol"]
|
|
274
|
+
+ self.instrument_data["week_option_prefix"],
|
|
275
|
+
self.instrument_data["tradingsymbol"],
|
|
275
276
|
)
|
|
276
277
|
|
|
277
278
|
self.instrument_data.loc[:, "tradingsymbol"] = np.where(
|
|
278
279
|
seg_condition & month_option_condition,
|
|
279
|
-
self.instrument_data
|
|
280
|
-
self.instrument_data
|
|
280
|
+
self.instrument_data["tradingsymbol"] + self.instrument_data["month"],
|
|
281
|
+
self.instrument_data["tradingsymbol"],
|
|
281
282
|
)
|
|
282
283
|
|
|
283
284
|
self.instrument_data.loc[:, "tradingsymbol"] = np.where(
|
|
284
285
|
seg_condition,
|
|
285
|
-
self.instrument_data
|
|
286
|
-
+ self.instrument_data
|
|
286
|
+
self.instrument_data["tradingsymbol"]
|
|
287
|
+
+ self.instrument_data["strike_price"]
|
|
288
|
+
.astype(float)
|
|
287
289
|
.astype(str)
|
|
288
290
|
.str.split(".")
|
|
289
291
|
.str[0],
|
|
290
|
-
self.instrument_data
|
|
292
|
+
self.instrument_data["tradingsymbol"],
|
|
291
293
|
)
|
|
292
294
|
|
|
293
295
|
self.instrument_data.loc[:, "tradingsymbol"] = np.where(
|
|
294
296
|
seg_condition,
|
|
295
|
-
self.instrument_data
|
|
296
|
-
self.instrument_data
|
|
297
|
+
self.instrument_data["tradingsymbol"] + self.instrument_data["option_type"],
|
|
298
|
+
self.instrument_data["tradingsymbol"],
|
|
297
299
|
)
|
|
298
300
|
|
|
299
301
|
def get_df_from_zip(self, url: str):
|
|
@@ -308,11 +310,11 @@ class Broker:
|
|
|
308
310
|
return pd.read_csv("/tmp/{}.csv".format(file_name))
|
|
309
311
|
|
|
310
312
|
def initialize_expiry_fields(self):
|
|
311
|
-
self.instrument_data.loc[:, "tradingsymbol"] =
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
self.instrument_data.loc[:, "tradingsymbol"] = self.instrument_data[
|
|
314
|
+
"instrument_symbol"
|
|
315
|
+
]
|
|
314
316
|
self.instrument_data.loc[:, "expiry"] = pd.to_datetime(
|
|
315
|
-
self.instrument_data
|
|
317
|
+
self.instrument_data["instrument_expiry"]
|
|
316
318
|
)
|
|
317
319
|
|
|
318
320
|
self.instrument_data.loc[:, "expiry_year"] = (
|
|
@@ -326,42 +328,42 @@ class Broker:
|
|
|
326
328
|
self.instrument_data["expiry"].dt.strftime("%m").astype(float).astype(str)
|
|
327
329
|
)
|
|
328
330
|
self.instrument_data.loc[:, "month_number"] = np.where(
|
|
329
|
-
self.instrument_data
|
|
331
|
+
self.instrument_data["month_number"] == "nan",
|
|
330
332
|
np.nan,
|
|
331
|
-
self.instrument_data
|
|
333
|
+
self.instrument_data["month_number"].str.split(".").str[0],
|
|
332
334
|
)
|
|
333
335
|
|
|
334
336
|
self.instrument_data.loc[:, "week_option_prefix"] = np.where(
|
|
335
|
-
self.instrument_data
|
|
336
|
-
self.instrument_data
|
|
337
|
+
self.instrument_data["month_number"].astype(float) >= 10,
|
|
338
|
+
self.instrument_data["month"].str[0]
|
|
337
339
|
+ self.instrument_data["expiry"].dt.strftime("%d").astype(str),
|
|
338
|
-
self.instrument_data
|
|
340
|
+
self.instrument_data["month_number"]
|
|
339
341
|
+ self.instrument_data["expiry"].dt.strftime("%d").astype(str),
|
|
340
342
|
)
|
|
341
343
|
|
|
342
|
-
self.instrument_data.loc[:, "next_expiry"] =
|
|
343
|
-
|
|
344
|
-
)
|
|
344
|
+
self.instrument_data.loc[:, "next_expiry"] = self.instrument_data[
|
|
345
|
+
"expiry"
|
|
346
|
+
] + pd.DateOffset(days=7)
|
|
345
347
|
|
|
346
|
-
@cached(cache=TTLCache(maxsize=1, ttl=2))
|
|
348
|
+
@cached(cache=TTLCache(maxsize=1, ttl=2)) # type: ignore
|
|
347
349
|
def cached_orders(self) -> pl.DataFrame:
|
|
348
350
|
return self.orders()
|
|
349
351
|
|
|
350
|
-
@cached(cache=TTLCache(maxsize=1, ttl=2))
|
|
352
|
+
@cached(cache=TTLCache(maxsize=1, ttl=2)) # type: ignore
|
|
351
353
|
def cached_positions(self) -> pl.DataFrame:
|
|
352
354
|
return self.positions()
|
|
353
355
|
|
|
354
356
|
def execute_order(
|
|
355
357
|
self,
|
|
356
|
-
tradingsymbol,
|
|
357
|
-
exchange,
|
|
358
|
-
quantity,
|
|
359
|
-
order_type,
|
|
360
|
-
transaction_type,
|
|
361
|
-
stoploss,
|
|
362
|
-
tag,
|
|
363
|
-
product,
|
|
364
|
-
price,
|
|
358
|
+
tradingsymbol: str,
|
|
359
|
+
exchange: ExchangeType,
|
|
360
|
+
quantity: int,
|
|
361
|
+
order_type: OrderTypeType,
|
|
362
|
+
transaction_type: TransactionType,
|
|
363
|
+
stoploss: float | None,
|
|
364
|
+
tag: str,
|
|
365
|
+
product: ProductType,
|
|
366
|
+
price: float,
|
|
365
367
|
):
|
|
366
368
|
upper_circuit = None
|
|
367
369
|
trade_price = copy.deepcopy(price)
|
|
@@ -396,6 +398,7 @@ class Broker:
|
|
|
396
398
|
raise Exception("{} not supported for trading".format(exchange))
|
|
397
399
|
|
|
398
400
|
sl_price = self.round_to_tick(price)
|
|
401
|
+
|
|
399
402
|
else:
|
|
400
403
|
raise Exception(
|
|
401
404
|
"Invalid transaction_type {}".format(transaction_type)
|
|
@@ -427,7 +430,7 @@ class Broker:
|
|
|
427
430
|
)
|
|
428
431
|
|
|
429
432
|
if order_type == "MARKET":
|
|
430
|
-
trade_price = 0
|
|
433
|
+
trade_price: float = 0
|
|
431
434
|
|
|
432
435
|
response = self.place_order(
|
|
433
436
|
tradingsymbol=tradingsymbol,
|
|
@@ -462,7 +465,7 @@ class Broker:
|
|
|
462
465
|
def get_exchange(self, exchange):
|
|
463
466
|
return exchange
|
|
464
467
|
|
|
465
|
-
def live_data(self, exchange, tradingsymbol):
|
|
468
|
+
def live_data(self, exchange: ExchangeType, tradingsymbol: str):
|
|
466
469
|
return {
|
|
467
470
|
"ltp": self.ltp(exchange, tradingsymbol),
|
|
468
471
|
"upper_circuit": None,
|
|
@@ -472,7 +475,13 @@ class Broker:
|
|
|
472
475
|
def basket_margin(self, basket_orders) -> Dict[str, Any]:
|
|
473
476
|
raise FeatureNotSupported("Margin calculator not supported by broker")
|
|
474
477
|
|
|
475
|
-
def verify_rms_square_off(
|
|
478
|
+
def verify_rms_square_off(
|
|
479
|
+
self,
|
|
480
|
+
stoploss: float | None,
|
|
481
|
+
target: float | None,
|
|
482
|
+
keep_hedges: bool = False,
|
|
483
|
+
ticks: int = 1,
|
|
484
|
+
):
|
|
476
485
|
positions = self.positions()
|
|
477
486
|
pnl = positions["pnl"].sum()
|
|
478
487
|
|
|
@@ -514,31 +523,37 @@ class Broker:
|
|
|
514
523
|
def get_product(self, product):
|
|
515
524
|
return product
|
|
516
525
|
|
|
517
|
-
def get_lot_size(self, exchange, tradingsymbol):
|
|
526
|
+
def get_lot_size(self, exchange: ExchangeType, tradingsymbol: str):
|
|
518
527
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange=exchange)
|
|
519
|
-
|
|
520
|
-
if
|
|
528
|
+
broker_exchange = self.get_exchange(exchange)
|
|
529
|
+
if broker_exchange == "BSE" or broker_exchange == "NSE":
|
|
521
530
|
return 1
|
|
522
531
|
try:
|
|
523
532
|
return int(
|
|
524
|
-
self.symbol_data["{}:{}".format(
|
|
533
|
+
self.symbol_data["{}:{}".format(broker_exchange, tradingsymbol)][
|
|
534
|
+
"lot_size"
|
|
535
|
+
]
|
|
525
536
|
)
|
|
526
537
|
except Exception as e:
|
|
527
538
|
logger.error(
|
|
528
|
-
f"[GET_LOT_SIZE] unable to get lot size for {
|
|
539
|
+
f"[GET_LOT_SIZE] unable to get lot size for {broker_exchange} {tradingsymbol}"
|
|
529
540
|
)
|
|
530
541
|
raise e
|
|
531
542
|
|
|
532
|
-
def filter_orders(
|
|
543
|
+
def filter_orders(
|
|
544
|
+
self, orders: pd.DataFrame, tag: str | None = None, status: str | None = None
|
|
545
|
+
):
|
|
533
546
|
if tag:
|
|
534
|
-
orders = orders[orders
|
|
547
|
+
orders = orders[orders["tag"] == tag]
|
|
535
548
|
|
|
536
549
|
if status:
|
|
537
|
-
orders = orders[orders
|
|
550
|
+
orders = orders[orders["status"] == status]
|
|
538
551
|
|
|
539
552
|
return orders
|
|
540
553
|
|
|
541
|
-
def option_symbol(
|
|
554
|
+
def option_symbol(
|
|
555
|
+
self, underlying_symbol: str, expiry_date, strike_price: float, type
|
|
556
|
+
):
|
|
542
557
|
option_symbol = MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP[
|
|
543
558
|
underlying_symbol
|
|
544
559
|
]
|
|
@@ -567,10 +582,10 @@ class Broker:
|
|
|
567
582
|
|
|
568
583
|
def exit_all_trigger_orders(
|
|
569
584
|
self,
|
|
570
|
-
tag="ALL",
|
|
585
|
+
tag: str = "ALL",
|
|
571
586
|
symbol_contains=None,
|
|
572
587
|
order_timestamp=None,
|
|
573
|
-
modify_sleep_time=10,
|
|
588
|
+
modify_sleep_time: float = 10,
|
|
574
589
|
):
|
|
575
590
|
stoploss_orders = self.orders()
|
|
576
591
|
stoploss_orders = stoploss_orders.filter(pl.col("status") == "TRIGGER PENDING")
|
|
@@ -1210,7 +1225,7 @@ class Broker:
|
|
|
1210
1225
|
"""
|
|
1211
1226
|
raise NotImplementedError
|
|
1212
1227
|
|
|
1213
|
-
def modify_order(self, order_to_modify
|
|
1228
|
+
def modify_order(self, order_to_modify) -> str:
|
|
1214
1229
|
"""Modifies Existing Order place on exchange
|
|
1215
1230
|
|
|
1216
1231
|
Args:
|
|
@@ -1235,7 +1250,7 @@ class Broker:
|
|
|
1235
1250
|
product: ProductType,
|
|
1236
1251
|
price: float,
|
|
1237
1252
|
trigger_price: float | None = None,
|
|
1238
|
-
) -> str:
|
|
1253
|
+
) -> str | None:
|
|
1239
1254
|
"""Function for Place Order to exchange
|
|
1240
1255
|
|
|
1241
1256
|
Args:
|
|
@@ -73,8 +73,6 @@ class KiteUtils:
|
|
|
73
73
|
pyotp.TOTP(totp).now()
|
|
74
74
|
driver = Selenium().get_browser()
|
|
75
75
|
|
|
76
|
-
# TODO api should be fetched from configuration
|
|
77
|
-
|
|
78
76
|
kite_url = "https://kite.trade/connect/login?api_key={}&v=3".format(api_key)
|
|
79
77
|
print("Kite Url {}".format(kite_url))
|
|
80
78
|
|