quantplay 2.0.51__tar.gz → 2.0.53__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.51 → quantplay-2.0.53}/PKG-INFO +1 -1
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/broker_factory.py +25 -3
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/generics/broker.py +2 -2
- quantplay-2.0.53/quantplay/broker/kotak.py +370 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay.egg-info/SOURCES.txt +1 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/setup.py +1 -1
- {quantplay-2.0.51 → quantplay-2.0.53}/README.md +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/pyproject.toml +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/aliceblue.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/angelone.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/dhan.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/five_paisa.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/motilal.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/noren.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/upstox.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/xts.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/xts_utils/Connect.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/broker/zerodha.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/model/broker.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/model/instrument_data.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/py.typed +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/caching.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/pickle_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/wrapper/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay/wrapper/aws/s3.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/setup.cfg +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/tests/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/tests/conftest.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/tests/wrapper/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.51 → quantplay-2.0.53}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -5,9 +5,11 @@ from typing import Any, Dict
|
|
|
5
5
|
|
|
6
6
|
from quantplay.broker.aliceblue import Aliceblue
|
|
7
7
|
from quantplay.broker.angelone import AngelOne
|
|
8
|
+
from quantplay.broker.dhan import Dhan
|
|
8
9
|
from quantplay.broker.five_paisa import FivePaisa
|
|
9
10
|
from quantplay.broker.flattrade import FlatTrade
|
|
10
11
|
from quantplay.broker.iifl_xts import IIFL as IIFL_XTS
|
|
12
|
+
from quantplay.broker.kotak import Kotak
|
|
11
13
|
from quantplay.broker.motilal import Motilal
|
|
12
14
|
from quantplay.broker.shoonya import FinvAsia
|
|
13
15
|
from quantplay.broker.upstox import Upstox
|
|
@@ -16,6 +18,7 @@ from quantplay.exception.exceptions import InvalidArgumentException
|
|
|
16
18
|
from quantplay.utils.caching import InstrumentCache
|
|
17
19
|
from quantplay.utils.pickle_utils import PickleUtils
|
|
18
20
|
|
|
21
|
+
|
|
19
22
|
BrokerType = (
|
|
20
23
|
Aliceblue
|
|
21
24
|
| AngelOne
|
|
@@ -26,6 +29,8 @@ BrokerType = (
|
|
|
26
29
|
| Zerodha
|
|
27
30
|
| IIFL_XTS
|
|
28
31
|
| FivePaisa
|
|
32
|
+
| Kotak
|
|
33
|
+
| Dhan
|
|
29
34
|
)
|
|
30
35
|
|
|
31
36
|
instrument_cache = InstrumentCache()
|
|
@@ -42,6 +47,8 @@ class Broker:
|
|
|
42
47
|
IIFL_XTS = "IIFL_XTS"
|
|
43
48
|
MOTILAL = "Motilal"
|
|
44
49
|
ANGELONE = "Angelone"
|
|
50
|
+
KOTAK = "Kotak"
|
|
51
|
+
DHAN = "Dhan"
|
|
45
52
|
|
|
46
53
|
|
|
47
54
|
class BrokerFactory:
|
|
@@ -55,6 +62,7 @@ class BrokerFactory:
|
|
|
55
62
|
Broker.ALICEBLUE: "aliceblue_instruments",
|
|
56
63
|
Broker.UPSTOX: "upstox_instruments",
|
|
57
64
|
Broker.FIVEPAISA_OPENAPI: "5paisa_instruments",
|
|
65
|
+
Broker.KOTAK: "upstox_instruments",
|
|
58
66
|
}
|
|
59
67
|
broker_required_args = {
|
|
60
68
|
Broker.ZERODHA: set(["user_id", "zerodha_wrapper"]),
|
|
@@ -64,6 +72,7 @@ class BrokerFactory:
|
|
|
64
72
|
Broker.MOTILAL: set(["user_id", "headers"]),
|
|
65
73
|
Broker.ALICEBLUE: set(["user_id", "client"]),
|
|
66
74
|
Broker.UPSTOX: set(["user_id", "access_token"]),
|
|
75
|
+
Broker.DHAN: set(["user_id", "access_token"]),
|
|
67
76
|
Broker.FIVEPAISA_OPENAPI: set(["user_id", "client"]),
|
|
68
77
|
Broker.ANGELONE: set(
|
|
69
78
|
[
|
|
@@ -74,6 +83,7 @@ class BrokerFactory:
|
|
|
74
83
|
"feed_token",
|
|
75
84
|
]
|
|
76
85
|
),
|
|
86
|
+
Broker.KOTAK: set(["user_id", "configuration"]),
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
def __init__(self):
|
|
@@ -107,19 +117,31 @@ class BrokerFactory:
|
|
|
107
117
|
|
|
108
118
|
broker_client: BrokerType | None = None
|
|
109
119
|
|
|
110
|
-
if broker ==
|
|
120
|
+
if broker == Broker.MOTILAL:
|
|
111
121
|
broker_client = Motilal(
|
|
112
122
|
headers=broker_data["headers"],
|
|
113
123
|
load_instrument=load_instrument,
|
|
114
124
|
)
|
|
115
125
|
|
|
116
|
-
|
|
126
|
+
if broker == Broker.DHAN:
|
|
127
|
+
broker_client = Dhan(
|
|
128
|
+
user_id=broker_data["user_id"],
|
|
129
|
+
access_token=broker_data["access_token"],
|
|
130
|
+
load_instrument=load_instrument,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if broker == Broker.KOTAK:
|
|
134
|
+
broker_client = Kotak(
|
|
135
|
+
configuration=broker_data["configuration"],
|
|
136
|
+
load_instrument=load_instrument,
|
|
137
|
+
)
|
|
138
|
+
elif broker == Broker.ZERODHA:
|
|
117
139
|
broker_client = Zerodha(
|
|
118
140
|
wrapper=broker_data["zerodha_wrapper"],
|
|
119
141
|
load_instrument=load_instrument,
|
|
120
142
|
)
|
|
121
143
|
|
|
122
|
-
elif broker ==
|
|
144
|
+
elif broker == Broker.ANGELONE:
|
|
123
145
|
broker_client = AngelOne(
|
|
124
146
|
user_id=broker_data["user_id"],
|
|
125
147
|
api_key=broker_data["api_key"],
|
|
@@ -250,7 +250,7 @@ class Broker(ABC):
|
|
|
250
250
|
positions = self.positions()
|
|
251
251
|
pnl = positions["pnl"].sum()
|
|
252
252
|
|
|
253
|
-
positions = positions
|
|
253
|
+
positions = positions.filter(pl.col("product") != "CNC")
|
|
254
254
|
if keep_hedges:
|
|
255
255
|
positions = positions.filter(
|
|
256
256
|
(~pl.col("option_type").is_in(["CE", "PE"])) & (pl.col("quantity").gt(0))
|
|
@@ -953,7 +953,7 @@ class Broker(ABC):
|
|
|
953
953
|
drop_cnc (bool, optional): CNC positions to be dropped or not. Defaults to True.
|
|
954
954
|
|
|
955
955
|
Returns:
|
|
956
|
-
|
|
956
|
+
pl.DataFrame: returns user positions
|
|
957
957
|
"""
|
|
958
958
|
...
|
|
959
959
|
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from typing import Any, Dict, Literal
|
|
4
|
+
import polars as pl
|
|
5
|
+
import requests
|
|
6
|
+
from quantplay.broker.generics.broker import Broker
|
|
7
|
+
from quantplay.model.broker import ModifyOrderRequest, UserBrokerProfileResponse
|
|
8
|
+
from quantplay.model.generics import (
|
|
9
|
+
ExchangeType,
|
|
10
|
+
OrderTypeType,
|
|
11
|
+
ProductType,
|
|
12
|
+
TransactionType,
|
|
13
|
+
)
|
|
14
|
+
from quantplay.model.order_event import OrderUpdateEvent
|
|
15
|
+
|
|
16
|
+
from urllib.parse import urlencode
|
|
17
|
+
|
|
18
|
+
import base64
|
|
19
|
+
|
|
20
|
+
PROD_BASE_URL = "https://gw-napi.kotaksecurities.com/"
|
|
21
|
+
SESSION_PROD_BASE_URL = "https://napi.kotaksecurities.com/"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# EvOUzPVqkwvlFl_6cZYZSCO5mwQa
|
|
25
|
+
# pVwafvF3ssA_jFVbKPrwhKt7igwa
|
|
26
|
+
# Kotak@123
|
|
27
|
+
# +918149949114
|
|
28
|
+
# 491700
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Kotak(Broker):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
order_updates: Queue[OrderUpdateEvent] | None = None,
|
|
35
|
+
consumer_key: str | None = None,
|
|
36
|
+
consumer_secret: str | None = None,
|
|
37
|
+
mobilenumber: str | None = None,
|
|
38
|
+
password: str | None = None,
|
|
39
|
+
mpin: str | None = None,
|
|
40
|
+
configuration: Dict[str, str] | None = None,
|
|
41
|
+
load_instrument: bool = True,
|
|
42
|
+
):
|
|
43
|
+
super().__init__()
|
|
44
|
+
|
|
45
|
+
self.configuration: Dict[str, str] = {
|
|
46
|
+
"fin_key": "X6Nk8cQhUgGmJ2vBdWw4sfzrz4L5En",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if configuration:
|
|
50
|
+
self.configuration = {
|
|
51
|
+
"fin_key": "X6Nk8cQhUgGmJ2vBdWw4sfzrz4L5En",
|
|
52
|
+
"bearer_token": configuration["bearer_token"],
|
|
53
|
+
"view_token": configuration["view_token"],
|
|
54
|
+
"sid": configuration["sid"],
|
|
55
|
+
"edit_token": configuration["edit_token"],
|
|
56
|
+
"edit_sid": configuration["edit_sid"],
|
|
57
|
+
"edit_rid": configuration["edit_rid"],
|
|
58
|
+
"serverId": configuration["serverId"],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
elif consumer_key and consumer_secret and mobilenumber and password and mpin:
|
|
62
|
+
self.login(consumer_key, consumer_secret, mobilenumber, password, mpin)
|
|
63
|
+
|
|
64
|
+
def login(
|
|
65
|
+
self,
|
|
66
|
+
consumer_key: str,
|
|
67
|
+
consumer_secret: str,
|
|
68
|
+
mobilenumber: str,
|
|
69
|
+
password: str,
|
|
70
|
+
mpin: str,
|
|
71
|
+
):
|
|
72
|
+
base64_string = str(consumer_key) + ":" + str(consumer_secret)
|
|
73
|
+
base64_token = base64_string.encode("ascii")
|
|
74
|
+
|
|
75
|
+
base64_bytes = base64.b64encode(base64_token)
|
|
76
|
+
final_base64_token = base64_bytes.decode("ascii")
|
|
77
|
+
|
|
78
|
+
session_init = requests.post(
|
|
79
|
+
url=f"{SESSION_PROD_BASE_URL}oauth2/token",
|
|
80
|
+
headers={
|
|
81
|
+
"Authorization": "Basic " + final_base64_token,
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
},
|
|
84
|
+
data=json.dumps({"grant_type": "client_credentials"}),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
print(session_init)
|
|
88
|
+
|
|
89
|
+
if not session_init.ok:
|
|
90
|
+
raise Exception("")
|
|
91
|
+
|
|
92
|
+
json_resp = json.loads(session_init.text)
|
|
93
|
+
self.configuration["bearer_token"] = json_resp.get("access_token")
|
|
94
|
+
|
|
95
|
+
view_token_resp = requests.post(
|
|
96
|
+
url=f"{PROD_BASE_URL}login/1.0/login/v2/validate",
|
|
97
|
+
headers={
|
|
98
|
+
"Authorization": "Bearer " + self.configuration["bearer_token"],
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
},
|
|
101
|
+
data=json.dumps({"mobileNumber": mobilenumber, "password": password}),
|
|
102
|
+
)
|
|
103
|
+
view_token = json.loads(view_token_resp.text)
|
|
104
|
+
|
|
105
|
+
self.configuration["view_token"] = view_token.get("data").get("token")
|
|
106
|
+
self.configuration["sid"] = view_token.get("data").get("sid")
|
|
107
|
+
|
|
108
|
+
view_token_resp = requests.post(
|
|
109
|
+
url=f"{PROD_BASE_URL}login/1.0/login/v2/validate",
|
|
110
|
+
headers={
|
|
111
|
+
"Authorization": "Bearer " + self.configuration["bearer_token"],
|
|
112
|
+
"sid": self.configuration["sid"],
|
|
113
|
+
"Auth": self.configuration["view_token"],
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
},
|
|
116
|
+
data=json.dumps({"mobileNumber": mobilenumber, "password": password}),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
login_resp = requests.post(
|
|
120
|
+
url=f"{PROD_BASE_URL}login/1.0/login/v2/validate",
|
|
121
|
+
headers={"Authorization": "Bearer " + self.configuration["bearer_token"]},
|
|
122
|
+
data=json.dumps({"mobileNumber": mobilenumber, "mpin": mpin}),
|
|
123
|
+
)
|
|
124
|
+
edit_token_resp = json.loads(login_resp.text)
|
|
125
|
+
|
|
126
|
+
if "error" not in edit_token_resp:
|
|
127
|
+
self.configuration["edit_token"] = edit_token_resp.get("data").get("token")
|
|
128
|
+
self.configuration["edit_sid"] = edit_token_resp.get("data").get("sid")
|
|
129
|
+
self.configuration["edit_rid"] = edit_token_resp.get("data").get("rid")
|
|
130
|
+
self.configuration["serverId"] = edit_token_resp.get("data").get("hsServerId")
|
|
131
|
+
self.user_id = edit_token_resp.get("data").get("ucc")
|
|
132
|
+
|
|
133
|
+
# decode_jwt = jwt.decode( # type: ignore
|
|
134
|
+
# self.configuration["view_token"], options={"verify_signature": False}
|
|
135
|
+
# )
|
|
136
|
+
# userid = decode_jwt.get("sub")
|
|
137
|
+
# self.kotakUserId = userid
|
|
138
|
+
|
|
139
|
+
# generate_otp_resp = requests.post(
|
|
140
|
+
# url=f"{PROD_BASE_URL}login/1.0/login/otp/generate",
|
|
141
|
+
# headers={"Authorization": "Bearer " + self.configuration["bearer_token"]},
|
|
142
|
+
# data=json.dumps({"userId": userid, "sendEmail": True, "isWhitelisted": True}),
|
|
143
|
+
# )
|
|
144
|
+
|
|
145
|
+
# generate_otp = json.loads(generate_otp_resp.text)
|
|
146
|
+
|
|
147
|
+
def get_base64_token(self, consumer_key: str, consumer_secret: str):
|
|
148
|
+
"""The Base64 Token Generation.
|
|
149
|
+
This function will generate the Base64 Token this will be used to generate the Bearer Token.
|
|
150
|
+
Return the Token in a String Format
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
# **
|
|
154
|
+
# ** GET Api's
|
|
155
|
+
# **
|
|
156
|
+
|
|
157
|
+
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
158
|
+
orders_resp = self.request("order_book")
|
|
159
|
+
|
|
160
|
+
if orders_resp["stat"] == "Not_Ok" and orders_resp["errMsg"] == "No Data":
|
|
161
|
+
return pl.DataFrame(schema=self.orders_schema)
|
|
162
|
+
|
|
163
|
+
return pl.DataFrame(schema=self.orders_schema)
|
|
164
|
+
|
|
165
|
+
def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
|
|
166
|
+
positions_resp = self.request("order_book")
|
|
167
|
+
|
|
168
|
+
if positions_resp["stat"] == "Not_Ok" and positions_resp["errMsg"] == "No Data":
|
|
169
|
+
return pl.DataFrame(schema=self.positions_schema)
|
|
170
|
+
|
|
171
|
+
return pl.DataFrame(schema=self.positions_schema)
|
|
172
|
+
|
|
173
|
+
def holdings(self) -> pl.DataFrame:
|
|
174
|
+
holdings_resp = self.request("order_book")
|
|
175
|
+
|
|
176
|
+
if holdings_resp["stat"] == "Not_Ok" and holdings_resp["errMsg"] == "No Data":
|
|
177
|
+
return pl.DataFrame(schema=self.holidings_schema)
|
|
178
|
+
|
|
179
|
+
return pl.DataFrame(schema=self.holidings_schema)
|
|
180
|
+
|
|
181
|
+
def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
182
|
+
raise NotImplementedError()
|
|
183
|
+
|
|
184
|
+
def profile(self) -> UserBrokerProfileResponse:
|
|
185
|
+
return {"user_id": self.user_id or ""}
|
|
186
|
+
|
|
187
|
+
def margins(self) -> Dict[str, float]:
|
|
188
|
+
return self.request("limits", body={"seg": "ALL", "exch": "ALL", "prod": "ALL"})
|
|
189
|
+
|
|
190
|
+
# **
|
|
191
|
+
# ** POST/PUT Api's
|
|
192
|
+
# **
|
|
193
|
+
|
|
194
|
+
def place_order(
|
|
195
|
+
self,
|
|
196
|
+
tradingsymbol: str,
|
|
197
|
+
exchange: ExchangeType,
|
|
198
|
+
quantity: int,
|
|
199
|
+
order_type: OrderTypeType,
|
|
200
|
+
transaction_type: TransactionType,
|
|
201
|
+
tag: str | None,
|
|
202
|
+
product: ProductType,
|
|
203
|
+
price: float,
|
|
204
|
+
trigger_price: float | None = None,
|
|
205
|
+
) -> str | None:
|
|
206
|
+
place_order_body = {
|
|
207
|
+
"am": "NO",
|
|
208
|
+
"dq": "0",
|
|
209
|
+
"es": self.get_exchange(exchange),
|
|
210
|
+
"mp": "0",
|
|
211
|
+
"pc": self.get_product(product),
|
|
212
|
+
"pf": "N",
|
|
213
|
+
"pr": str(price),
|
|
214
|
+
"pt": self.get_order_type(order_type),
|
|
215
|
+
"qt": str(quantity),
|
|
216
|
+
"rt": "DAY",
|
|
217
|
+
"tp": str(trigger_price or "0"),
|
|
218
|
+
"ts": tradingsymbol,
|
|
219
|
+
"tt": self.get_transaction_type(transaction_type),
|
|
220
|
+
"ig": tag,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
place_order_resp = self.request("place_order", body=place_order_body)
|
|
224
|
+
print(place_order_resp)
|
|
225
|
+
|
|
226
|
+
return ""
|
|
227
|
+
|
|
228
|
+
def cancel_order(self, order_id: str, variety: str | None = None) -> None:
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
def modify_order(self, order: ModifyOrderRequest) -> str:
|
|
232
|
+
return ""
|
|
233
|
+
|
|
234
|
+
# **
|
|
235
|
+
# ** General Utils
|
|
236
|
+
# **
|
|
237
|
+
|
|
238
|
+
def get_transaction_type(self, transaction_type: TransactionType) -> str:
|
|
239
|
+
transaction_type_map: Dict[TransactionType, str] = {
|
|
240
|
+
"BUY": "B",
|
|
241
|
+
"SELL": "S",
|
|
242
|
+
}
|
|
243
|
+
return transaction_type_map.get(transaction_type, transaction_type)
|
|
244
|
+
|
|
245
|
+
def get_order_type(self, order_type: OrderTypeType) -> str:
|
|
246
|
+
order_type_map: Dict[OrderTypeType, str] = {
|
|
247
|
+
"LIMIT": "L",
|
|
248
|
+
"MARKET": "MKT",
|
|
249
|
+
"SL": "SL",
|
|
250
|
+
"SL-M": "SL-M",
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return order_type_map.get(order_type, order_type)
|
|
254
|
+
|
|
255
|
+
def get_exchange(self, exchange: ExchangeType) -> str:
|
|
256
|
+
exchange_segment_map: Dict[ExchangeType, str] = {
|
|
257
|
+
"NSE": "nse_cm",
|
|
258
|
+
"BSE": "bse_cm",
|
|
259
|
+
"NFO": "nse_fo",
|
|
260
|
+
"BFO": "bse_fo",
|
|
261
|
+
"CDS": "cde_fo",
|
|
262
|
+
"BCD": "bcs-fo",
|
|
263
|
+
"MCX": "mcx",
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return exchange_segment_map.get(exchange, exchange)
|
|
267
|
+
|
|
268
|
+
def get_product(self, product: ProductType) -> str:
|
|
269
|
+
product_map: Dict[ProductType, str] = {
|
|
270
|
+
"NRML": "NRML",
|
|
271
|
+
"CNC": "CNC",
|
|
272
|
+
"MIS": "MIS",
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return product_map.get(product, product)
|
|
276
|
+
|
|
277
|
+
# **
|
|
278
|
+
# ** Kotak Utils
|
|
279
|
+
# **
|
|
280
|
+
|
|
281
|
+
def request(
|
|
282
|
+
self,
|
|
283
|
+
item: Literal[
|
|
284
|
+
"place_order",
|
|
285
|
+
"cancel_order",
|
|
286
|
+
"modify_order",
|
|
287
|
+
"order_history",
|
|
288
|
+
"order_book",
|
|
289
|
+
"trade_report",
|
|
290
|
+
"positions",
|
|
291
|
+
"holdings",
|
|
292
|
+
"margin",
|
|
293
|
+
"scrip_master",
|
|
294
|
+
"limits",
|
|
295
|
+
"logout",
|
|
296
|
+
],
|
|
297
|
+
params: Dict[str, str | int | float] | None = None,
|
|
298
|
+
body: Dict[str, Any] | None = None,
|
|
299
|
+
) -> Dict[str, Any]:
|
|
300
|
+
url_map = {
|
|
301
|
+
"place_order": ("Orders/2.0/quick/order/rule/ms/place", "POST", False),
|
|
302
|
+
"cancel_order": ("Orders/2.0/quick/order/cancel", "POST", False),
|
|
303
|
+
"modify_order": ("Orders/2.0/quick/order/vr/modify", "POST", False),
|
|
304
|
+
"order_history": ("Orders/2.0/quick/order/history", "GET", True),
|
|
305
|
+
"order_book": ("Orders/2.0/quick/user/orders", "GET", True),
|
|
306
|
+
"trade_report": ("Orders/2.0/quick/user/trades", "GET", True),
|
|
307
|
+
"positions": ("Orders/2.0/quick/user/positions", "GET", True),
|
|
308
|
+
"holdings": ("Portfolio/1.0/portfolio/v1/holdings", "GET", True),
|
|
309
|
+
"margin": ("Orders/2.0/quick/user/check-margin", "GET", True),
|
|
310
|
+
"scrip_master": ("Files/1.0/masterscrip/v1/file-paths", "GET", True),
|
|
311
|
+
"limits": ("Orders/2.0/quick/user/limits", "POST", False),
|
|
312
|
+
"logout": ("login/1.0/logout", "GET", True),
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
url, method, is_content_type_json = url_map[item]
|
|
316
|
+
content_type = (
|
|
317
|
+
"application/json"
|
|
318
|
+
if is_content_type_json
|
|
319
|
+
else "application/x-www-form-urlencoded"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
url = f"{PROD_BASE_URL}{url}"
|
|
323
|
+
|
|
324
|
+
request_headers = {
|
|
325
|
+
"Authorization": "Bearer " + self.configuration["bearer_token"],
|
|
326
|
+
"Sid": self.configuration["edit_sid"],
|
|
327
|
+
"Auth": self.configuration["edit_token"],
|
|
328
|
+
"neo-fin-key": self.configuration["fin_key"],
|
|
329
|
+
"Content-Type": content_type,
|
|
330
|
+
"accept": "application/json",
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
query_params = {"sId": self.configuration["serverId"]}
|
|
334
|
+
|
|
335
|
+
request_body = None
|
|
336
|
+
request_params = None
|
|
337
|
+
|
|
338
|
+
if params is not None:
|
|
339
|
+
request_params = urlencode({**params, **query_params})
|
|
340
|
+
else:
|
|
341
|
+
request_params = urlencode(query_params)
|
|
342
|
+
|
|
343
|
+
if body is not None:
|
|
344
|
+
if content_type == "application/json":
|
|
345
|
+
request_body = json.dumps(body)
|
|
346
|
+
|
|
347
|
+
elif content_type == "application/x-www-form-urlencoded":
|
|
348
|
+
request_body = {"jData": json.dumps(body)}
|
|
349
|
+
|
|
350
|
+
resp = None
|
|
351
|
+
resp_data = None
|
|
352
|
+
|
|
353
|
+
if method == "GET":
|
|
354
|
+
resp = requests.get(url=url, params=request_params, headers=request_headers)
|
|
355
|
+
|
|
356
|
+
elif method == "POST":
|
|
357
|
+
resp = requests.post(
|
|
358
|
+
url=url,
|
|
359
|
+
headers=request_headers,
|
|
360
|
+
data=request_body,
|
|
361
|
+
params=request_params,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if resp and resp.ok:
|
|
365
|
+
resp_data = resp.json()
|
|
366
|
+
|
|
367
|
+
if resp_data is None:
|
|
368
|
+
raise Exception("")
|
|
369
|
+
|
|
370
|
+
return resp_data
|
|
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
|
|
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
|