diff --git a/.gitignore b/.gitignore index 8b13789..3953ffd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.pypirc \ No newline at end of file diff --git a/.pypirc b/.pypirc new file mode 100644 index 0000000..13da8a9 --- /dev/null +++ b/.pypirc @@ -0,0 +1,10 @@ +[distutils] +index-servers = + pypi + +[pypi] +repository: https://upload.pypi.org/legacy/ +username: ab-smartapi +password: Smart#54321 + + diff --git a/README.md b/README.md index 817e2f0..381491a 100644 --- a/README.md +++ b/README.md @@ -1 +1,145 @@ -# smartapi-python \ No newline at end of file +# SMARTAPI-PYTHON + +SMARTAPI-PYTHON is a Python library for dealing AMX,that is a set of REST-like HTTP APIs that expose many capabilities required to build stock market investment and trading platforms. It lets you execute orders in real time. + +## Installation + +Use the package manager [pip](https://pip.pypa.io/en/stable/) to install smartapi-python. + +```bash +pip install smartapi-python +pip install websocket-client +``` + +## Usage + +```python +# package import statement +from smartapi import SmartConnect #or from smartapi.smartConnect import SmartConnect +#import smartapi.smartExceptions(for smartExceptions) + +#create object of call +obj=SmartConnect(api_key="your api key", + #optional + #access_token = "your access token", + #refresh_token = "your refresh_token") + +#login api call + +data = obj.generateSession("Your Client ID","Your Password") +refreshToken= data['data']['refreshToken'] + +#fetch the feedtoken +feedToken=obj.getfeedToken() + +#fetch User Profile +userProfile= obj.getProfile(refreshToken) +#place order +try: + orderparams = { + "variety": "NORMAL", + "tradingsymbol": "SBIN-EQ", + "symboltoken": "3045", + "transactiontype": "BUY", + "exchange": "NSE", + "ordertype": "LIMIT", + "producttype": "INTRADAY", + "duration": "DAY", + "price": "19500", + "squareoff": "0", + "stoploss": "0", + "quantity": "1" + } + orderId=obj.placeOrder(orderparams) + print("The order id is: {}".format(orderId)) +except Exception as e: + print("Order placement failed: {}".format(e.message)) +#gtt rule creation +try: + gttCreateParams={ + "tradingsymbol" : "SBIN-EQ", + "symboltoken" : "3045", + "exchange" : "NSE", + "producttype" : "MARGIN", + "transactiontype" : "BUY", + "price" : 100000, + "qty" : 10, + "disclosedqty": 10, + "triggerprice" : 200000, + "timeperiod" : 365 + } + rule_id=gtt.gttCreateRule(gttCreateParams) + print("The GTT rule id is: {}".format(rule_id)) +except Exception as e: + print("GTT Rule creation failed: {}".format(e.message)) + +#gtt rule list +try: + status=["FORALL"] #should be a list + page=1 + count=10 + lists=smartApi.gttLists(status,page,count) +except Exception as e: + print("GTT Rule List failed: {}".format(e.message)) + +#Historic api +try: + historicParam={ + "exchange": "NSE", + "symboltoken": "3045", + "interval": "ONE_MINUTE", + "fromdate": "2021-02-08 09:00", + "todate": "2021-02-08 09:16" + } + smartApi.getCandleData(historicParam) +except Exception as e: + print("Historic Api failed: {}".format(e.message)) +#logout +try: + logout=obj.terminateSession('Your Client Id') + print("Logout Successfull") +except Exception as e: + print("Logout failed: {}".format(e.message)) +``` + + +## Getting started with SmartAPI Websocket's +```python + +from smartapiwebsocket import SmartWebSocket + +# feed_token=092017047 +FEED_TOKEN="YOUR_FEED_TOKEN" +CLIENT_CODE="YOUR_CLIENT_CODE" +# token="mcx_fo|224395" +token="EXCHANGE|TOKEN_SYMBOL" #SAMPLE: nse_cm|2885&nse_cm|1594&nse_cm|11536&nse_cm|3045 +# token="mcx_fo|226745&mcx_fo|220822&mcx_fo|227182&mcx_fo|221599" +task="mw" # mw|sfi|dp + +ss = SmartWebSocket(FEED_TOKEN, CLIENT_CODE) + +def on_message(ws, message): + print("Ticks: {}".format(message)) + +def on_open(ws): + print("on open") + ss.subscribe(task,token) + +def on_error(ws, error): + print(error) + +def on_close(ws): + print("Close") + +# Assign the callbacks. +ss._on_open = on_open +ss._on_message = on_message +ss._on_error = on_error +ss._on_close = on_close + +ss.connect() +``` + + + + diff --git a/SmartApi/__init__.py b/SmartApi/__init__.py new file mode 100644 index 0000000..720c816 --- /dev/null +++ b/SmartApi/__init__.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals,absolute_import + +from smartapi.smartConnect import SmartConnect +# from smartapi.webSocket import WebSocket +from smartapi.smartApiWebsocket import SmartWebSocket + +__all__ = ["SmartConnect","SmartWebSocket"] + + + + diff --git a/SmartApi/__pycache__/__init__.cpython-36.pyc b/SmartApi/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..f619741 Binary files /dev/null and b/SmartApi/__pycache__/__init__.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/smartConnect.cpython-36.pyc b/SmartApi/__pycache__/smartConnect.cpython-36.pyc new file mode 100644 index 0000000..7a7be23 Binary files /dev/null and b/SmartApi/__pycache__/smartConnect.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/smartExceptions.cpython-36.pyc b/SmartApi/__pycache__/smartExceptions.cpython-36.pyc new file mode 100644 index 0000000..0abc8e0 Binary files /dev/null and b/SmartApi/__pycache__/smartExceptions.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/smartSocket.cpython-36.pyc b/SmartApi/__pycache__/smartSocket.cpython-36.pyc new file mode 100644 index 0000000..99c5ce7 Binary files /dev/null and b/SmartApi/__pycache__/smartSocket.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/socket.cpython-36.pyc b/SmartApi/__pycache__/socket.cpython-36.pyc new file mode 100644 index 0000000..addd724 Binary files /dev/null and b/SmartApi/__pycache__/socket.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/socketTP.cpython-36.pyc b/SmartApi/__pycache__/socketTP.cpython-36.pyc new file mode 100644 index 0000000..5b56185 Binary files /dev/null and b/SmartApi/__pycache__/socketTP.cpython-36.pyc differ diff --git a/SmartApi/__pycache__/version.cpython-36.pyc b/SmartApi/__pycache__/version.cpython-36.pyc new file mode 100644 index 0000000..5c18fcb Binary files /dev/null and b/SmartApi/__pycache__/version.cpython-36.pyc differ diff --git a/SmartApi/smartApiWebsocket.py b/SmartApi/smartApiWebsocket.py new file mode 100644 index 0000000..723f680 --- /dev/null +++ b/SmartApi/smartApiWebsocket.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Apr 23 11:38:36 2021 + +@author: Sandip.Khairnar +""" + +import websocket +import six +import base64 +import zlib +import datetime +import time +import json +import threading +import ssl + +class SmartWebSocket(object): + ROOT_URI='wss://wsfeeds.angelbroking.com/NestHtml5Mobile/socket/stream' + HB_INTERVAL=30 + HB_THREAD_FLAG=False + WS_RECONNECT_FLAG=False + feed_token=None + client_code=None + ws=None + task_dict = {} + + def __init__(self, FEED_TOKEN, CLIENT_CODE): + self.root = self.ROOT_URI + self.feed_token = FEED_TOKEN + self.client_code = CLIENT_CODE + if self.client_code == None or self.feed_token == None: + return "client_code or feed_token or task is missing" + + def _subscribe_on_open(self): + request = {"task": "cn", "channel": "NONLM", "token": self.feed_token, "user": self.client_code, + "acctid": self.client_code} + print(request) + self.ws.send( + six.b(json.dumps(request)) + ) + + thread = threading.Thread(target=self.run, args=()) + thread.daemon = True + thread.start() + + def run(self): + while True: + # More statements comes here + if self.HB_THREAD_FLAG: + break + print(datetime.datetime.now().__str__() + ' : Start task in the background') + + self.heartBeat() + + time.sleep(self.HB_INTERVAL) + + def subscribe(self, task, token): + # print(self.task_dict) + self.task_dict.update([(task,token),]) + # print(self.task_dict) + if task in ("mw", "sfi", "dp"): + strwatchlistscrips = token # dynamic call + + try: + request = {"task": task, "channel": strwatchlistscrips, "token": self.feed_token, + "user": self.client_code, "acctid": self.client_code} + + self.ws.send( + six.b(json.dumps(request)) + ) + return True + except Exception as e: + self._close(reason="Error while request sending: {}".format(str(e))) + raise + else: + print("The task entered is invalid, Please enter correct task(mw,sfi,dp) ") + + def resubscribe(self): + for task, marketwatch in self.task_dict.items(): + print(task, '->', marketwatch) + try: + request = {"task": task, "channel": marketwatch, "token": self.feed_token, + "user": self.client_code, "acctid": self.client_code} + + self.ws.send( + six.b(json.dumps(request)) + ) + return True + except Exception as e: + self._close(reason="Error while request sending: {}".format(str(e))) + raise + + def heartBeat(self): + try: + request = {"task": "hb", "channel": "", "token": self.feed_token, "user": self.client_code, + "acctid": self.client_code} + print(request) + self.ws.send( + six.b(json.dumps(request)) + ) + + except: + print("HeartBeat Sending Failed") + # time.sleep(60) + + def _parse_text_message(self, message): + """Parse text message.""" + + data = base64.b64decode(message) + + try: + data = bytes((zlib.decompress(data)).decode("utf-8"), 'utf-8') + data = json.loads(data.decode('utf8').replace("'", '"')) + data = json.loads(json.dumps(data, indent=4, sort_keys=True)) + except ValueError: + return + + # return data + if data: + self._on_message(self.ws,data) + + def connect(self): + # websocket.enableTrace(True) + self.ws = websocket.WebSocketApp(self.ROOT_URI, + on_message=self.__on_message, + on_close=self.__on_close, + on_open=self.__on_open, + on_error=self.__on_error) + + self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) + + def __on_message(self, ws, message): + self._parse_text_message(message) + # print(msg) + + def __on_open(self, ws): + print("__on_open################") + self.HB_THREAD_FLAG = False + self._subscribe_on_open() + if self.WS_RECONNECT_FLAG: + self.WS_RECONNECT_FLAG = False + self.resubscribe() + else: + self._on_open(ws) + + def __on_close(self, ws): + self.HB_THREAD_FLAG = True + print("__on_close################") + self._on_close(ws) + + def __on_error(self, ws, error): + + if ( "timed" in str(error) ) or ( "Connection is already closed" in str(error) ): + + self.WS_RECONNECT_FLAG = True + self.HB_THREAD_FLAG = True + + if (ws is not None): + ws.close() + ws.on_message = None + ws.on_open = None + ws.close = None + # print (' deleting ws') + del ws + + self.connect() + else: + print ('Error info: %s' %(error)) + self._on_error(ws, error) + + def _on_message(self, ws, message): + pass + + def _on_open(self, ws): + pass + + def _on_close(self, ws): + pass + + def _on_error(self, ws, error): + pass \ No newline at end of file diff --git a/SmartApi/smartConnect.py b/SmartApi/smartConnect.py new file mode 100644 index 0000000..c8f65a8 --- /dev/null +++ b/SmartApi/smartConnect.py @@ -0,0 +1,408 @@ +from six.moves.urllib.parse import urljoin +import sys +import csv +import json +import dateutil.parser +import hashlib +import logging +import datetime +import smartapi.smartExceptions as ex +import requests +from requests import get +import re, uuid +import socket +import platform +from smartapi.version import __version__, __title__ + +log = logging.getLogger(__name__) +#user_sys=platform.system() +#print("the system",user_sys) + +class SmartConnect(object): + #_rootUrl = "https://openapisuat.angelbroking.com" + _rootUrl="https://apiconnect.angelbroking.com" #prod endpoint + #_login_url ="https://smartapi.angelbroking.com/login" + _login_url="https://smartapi.angelbroking.com/publisher-login" #prod endpoint + _default_timeout = 7 # In seconds + + _routes = { + "api.login":"/rest/auth/angelbroking/user/v1/loginByPassword", + "api.logout":"/rest/secure/angelbroking/user/v1/logout", + "api.token": "/rest/auth/angelbroking/jwt/v1/generateTokens", + "api.refresh": "/rest/auth/angelbroking/jwt/v1/generateTokens", + "api.user.profile": "/rest/secure/angelbroking/user/v1/getProfile", + + "api.order.place": "/rest/secure/angelbroking/order/v1/placeOrder", + "api.order.modify": "/rest/secure/angelbroking/order/v1/modifyOrder", + "api.order.cancel": "/rest/secure/angelbroking/order/v1/cancelOrder", + "api.order.book":"/rest/secure/angelbroking/order/v1/getOrderBook", + + "api.ltp.data": "/rest/secure/angelbroking/order/v1/getLtpData", + "api.trade.book": "/rest/secure/angelbroking/order/v1/getTradeBook", + "api.rms.limit": "/rest/secure/angelbroking/user/v1/getRMS", + "api.holding": "/rest/secure/angelbroking/portfolio/v1/getHolding", + "api.position": "/rest/secure/angelbroking/order/v1/getPosition", + "api.convert.position": "/rest/secure/angelbroking/order/v1/convertPosition", + + "api.gtt.create":"/gtt-service/rest/secure/angelbroking/gtt/v1/createRule", + "api.gtt.modify":"/gtt-service/rest/secure/angelbroking/gtt/v1/modifyRule", + "api.gtt.cancel":"/gtt-service/rest/secure/angelbroking/gtt/v1/cancelRule", + "api.gtt.details":"/rest/secure/angelbroking/gtt/v1/ruleDetails", + "api.gtt.list":"/rest/secure/angelbroking/gtt/v1/ruleList", + + "api.candle.data":"/rest/secure/angelbroking/historical/v1/getCandleData" + } + + + try: + clientPublicIp= " " + get('https://api.ipify.org').text + if " " in clientPublicIp: + clientPublicIp=clientPublicIp.replace(" ","") + hostname = socket.gethostname() + clientLocalIp=socket.gethostbyname(hostname) + except Exception as e: + print("Exception while retriving IP Address,using local host IP address",e) + finally: + clientPublicIp="106.193.147.98" + clientLocalIp="127.0.0.1" + clientMacAddress=':'.join(re.findall('..', '%012x' % uuid.getnode())) + accept = "application/json" + userType = "USER" + sourceID = "WEB" + + + def __init__(self, api_key=None, access_token=None, refresh_token=None,feed_token=None, userId=None, root=None, debug=False, timeout=None, proxies=None, pool=None, disable_ssl=False,accept=None,userType=None,sourceID=None,Authorization=None,clientPublicIP=None,clientMacAddress=None,clientLocalIP=None,privateKey=None): + self.debug = debug + self.api_key = api_key + self.session_expiry_hook = None + self.disable_ssl = disable_ssl + self.access_token = access_token + self.refresh_token = refresh_token + self.feed_token = feed_token + self.userId = userId + self.proxies = proxies if proxies else {} + self.root = root or self._rootUrl + self.timeout = timeout or self._default_timeout + self.Authorization= None + self.clientLocalIP=self.clientLocalIp + self.clientPublicIP=self.clientPublicIp + self.clientMacAddress=self.clientMacAddress + self.privateKey=api_key + self.accept=self.accept + self.userType=self.userType + self.sourceID=self.sourceID + + if pool: + self.reqsession = requests.Session() + reqadapter = requests.adapters.HTTPAdapter(**pool) + self.reqsession.mount("https://", reqadapter) + print("in pool") + else: + self.reqsession = requests + + # disable requests SSL warning + requests.packages.urllib3.disable_warnings() + def requestHeaders(self): + return{ + "Content-type":self.accept, + "X-ClientLocalIP": self.clientLocalIp, + "X-ClientPublicIP": self.clientPublicIp, + "X-MACAddress": self.clientMacAddress, + "Accept": self.accept, + "X-PrivateKey": self.privateKey, + "X-UserType": self.userType, + "X-SourceID": self.sourceID + } + + def setSessionExpiryHook(self, method): + if not callable(method): + raise TypeError("Invalid input type. Only functions are accepted.") + self.session_expiry_hook = method + + def getUserId(): + return userId + + def setUserId(self,id): + self.userId=id + + def setAccessToken(self, access_token): + + self.access_token = access_token + + def setRefreshToken(self, refresh_token): + + self.refresh_token = refresh_token + + def setFeedToken(self,feedToken): + + self.feed_token=feedToken + + def getfeedToken(self): + return self.feed_token + + + def login_url(self): + """Get the remote login url to which a user should be redirected to initiate the login flow.""" + return "%s?api_key=%s" % (self._login_url, self.api_key) + + def _request(self, route, method, parameters=None): + """Make an HTTP request.""" + params = parameters.copy() if parameters else {} + + uri =self._routes[route].format(**params) + url = urljoin(self.root, uri) + + + # Custom headers + headers = self.requestHeaders() + + if self.access_token: + # set authorization header + + auth_header = self.access_token + headers["Authorization"] = "Bearer {}".format(auth_header) + + if self.debug: + log.debug("Request: {method} {url} {params} {headers}".format(method=method, url=url, params=params, headers=headers)) + + try: + r = requests.request(method, + url, + data=json.dumps(params) if method in ["POST", "PUT"] else None, + params=json.dumps(params) if method in ["GET", "DELETE"] else None, + headers=headers, + verify=not self.disable_ssl, + allow_redirects=True, + timeout=self.timeout, + proxies=self.proxies) + + except Exception as e: + raise e + + if self.debug: + log.debug("Response: {code} {content}".format(code=r.status_code, content=r.content)) + + # Validate the content type. + if "json" in headers["Content-type"]: + try: + data = json.loads(r.content.decode("utf8")) + + except ValueError: + raise ex.DataException("Couldn't parse the JSON response received from the server: {content}".format( + content=r.content)) + + # api error + if data.get("error_type"): + # Call session hook if its registered and TokenException is raised + if self.session_expiry_hook and r.status_code == 403 and data["error_type"] == "TokenException": + self.session_expiry_hook() + + # native errors + exp = getattr(ex, data["error_type"], ex.GeneralException) + raise exp(data["message"], code=r.status_code) + + return data + elif "csv" in headers["Content-type"]: + return r.content + else: + raise ex.DataException("Unknown Content-type ({content_type}) with response: ({content})".format( + content_type=headers["Content-type"], + content=r.content)) + + def _deleteRequest(self, route, params=None): + """Alias for sending a DELETE request.""" + return self._request(route, "DELETE", params) + def _putRequest(self, route, params=None): + """Alias for sending a PUT request.""" + return self._request(route, "PUT", params) + def _postRequest(self, route, params=None): + """Alias for sending a POST request.""" + return self._request(route, "POST", params) + def _getRequest(self, route, params=None): + """Alias for sending a GET request.""" + return self._request(route, "GET", params) + + def generateSession(self,clientCode,password): + + params={"clientcode":clientCode,"password":password} + loginResultObject=self._postRequest("api.login",params) + + if loginResultObject['status']==True: + jwtToken=loginResultObject['data']['jwtToken'] + self.setAccessToken(jwtToken) + refreshToken=loginResultObject['data']['refreshToken'] + feedToken=loginResultObject['data']['feedToken'] + self.setRefreshToken(refreshToken) + self.setFeedToken(feedToken) + user=self.getProfile(refreshToken) + + id=user['data']['clientcode'] + #id='D88311' + self.setUserId(id) + user['data']['jwtToken']="Bearer "+jwtToken + user['data']['refreshToken']=refreshToken + + + return user + else: + return loginResultObject + def terminateSession(self,clientCode): + logoutResponseObject=self._postRequest("api.logout",{"clientcode":clientCode}) + return logoutResponseObject + + def generateToken(self,refresh_token): + response=self._postRequest('api.token',{"refreshToken":refresh_token}) + jwtToken=response['data']['jwtToken'] + feedToken=response['data']['feedToken'] + self.setFeedToken(feedToken) + self.setAccessToken(jwtToken) + + return response + + def renewAccessToken(self): + response =self._postRequest('api.refresh', { + "jwtToken": self.access_token, + "refreshToken": self.refresh_token, + + }) + + tokenSet={} + + if "jwtToken" in response: + tokenSet['jwtToken']=response['data']['jwtToken'] + tokenSet['clientcode']=self. userId + tokenSet['refreshToken']=response['data']["refreshToken"] + + return tokenSet + + def getProfile(self,refreshToken): + user=self._getRequest("api.user.profile",{"refreshToken":refreshToken}) + return user + + def placeOrder(self,orderparams): + + params=orderparams + + for k in list(params.keys()): + if params[k] is None : + del(params[k]) + + orderResponse= self._postRequest("api.order.place", params)['data']['orderid'] + + return orderResponse + + def modifyOrder(self,orderparams): + params = orderparams + + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + + orderResponse= self._postRequest("api.order.modify", params) + return orderResponse + + def cancelOrder(self, order_id,variety): + orderResponse= self._postRequest("api.order.cancel", {"variety": variety,"orderid": order_id}) + return orderResponse + + def ltpData(self,exchange,tradingsymbol,symboltoken): + params={ + "exchange":exchange, + "tradingsymbol":tradingsymbol, + "symboltoken":symboltoken + } + ltpDataResponse= self._postRequest("api.ltp.data",params) + return ltpDataResponse + + def orderBook(self): + orderBookResponse=self._getRequest("api.order.book") + return orderBookResponse + + + def tradeBook(self): + tradeBookResponse=self._getRequest("api.trade.book") + return tradeBookResponse + + def rmsLimit(self): + rmsLimitResponse= self._getRequest("api.rms.limit") + return rmsLimitResponse + + def position(self): + positionResponse= self._getRequest("api.position") + return positionResponse + + def holding(self): + holdingResponse= self._getRequest("api.holding") + return holdingResponse + + def convertPosition(self,positionParams): + params=positionParams + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + convertPositionResponse= self._postRequest("api.convert.position",params) + + return convertPositionResponse + + def gttCreateRule(self,createRuleParams): + params=createRuleParams + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + + createGttRuleResponse=self._postRequest("api.gtt.create",params) + #print(createGttRuleResponse) + return createGttRuleResponse['data']['id'] + + def gttModifyRule(self,modifyRuleParams): + params=modifyRuleParams + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + modifyGttRuleResponse=self._postRequest("api.gtt.modify",params) + #print(modifyGttRuleResponse) + return modifyGttRuleResponse['data']['id'] + + def gttCancelRule(self,gttCancelParams): + params=gttCancelParams + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + + #print(params) + cancelGttRuleResponse=self._postRequest("api.gtt.cancel",params) + #print(cancelGttRuleResponse) + return cancelGttRuleResponse + + def gttDetails(self,id): + params={ + "id":id + } + gttDetailsResponse=self._postRequest("api.gtt.details",params) + return gttDetailsResponse + + def gttLists(self,status,page,count): + if type(status)== list: + params={ + "status":status, + "page":page, + "count":count + } + gttListResponse=self._postRequest("api.gtt.list",params) + #print(gttListResponse) + return gttListResponse + else: + message="The status param is entered as" +str(type(status))+". Please enter status param as a list i.e., status=['CANCELLED']" + return message + + def getCandleData(self,historicDataParams): + params=historicDataParams + for k in list(params.keys()): + if params[k] is None: + del(params[k]) + getCandleDataResponse=self._postRequest("api.candle.data",historicDataParams) + return getCandleDataResponse + + def _user_agent(self): + return (__title__ + "-python/").capitalize() + __version__ + diff --git a/SmartApi/smartExceptions.py b/SmartApi/smartExceptions.py new file mode 100644 index 0000000..b9f2561 --- /dev/null +++ b/SmartApi/smartExceptions.py @@ -0,0 +1,62 @@ +class SmartAPIException(Exception): + + def __init__(self, message, code=500): + """Initialize the exception.""" + super(SmartAPIException, self).__init__(message) + self.code = code + + +class GeneralException(SmartAPIException): + """An unclassified, general error. Default code is 500.""" + + def __init__(self, message, code=500): + """Initialize the exception.""" + super(GeneralException, self).__init__(message, code) + + +class TokenException(SmartAPIException): + """Represents all token and authentication related errors. Default code is 403.""" + + def __init__(self, message, code=403): + """Initialize the exception.""" + super(TokenException, self).__init__(message, code) + + +class PermissionException(SmartAPIException): + """Represents permission denied exceptions for certain calls. Default code is 403.""" + + def __init__(self, message, code=403): + """Initialize the exception.""" + super(PermissionException, self).__init__(message, code) + + +class OrderException(SmartAPIException): + """Represents all order placement and manipulation errors. Default code is 500.""" + + def __init__(self, message, code=500): + """Initialize the exception.""" + super(OrderException, self).__init__(message, code) + + +class InputException(SmartAPIException): + """Represents user input errors such as missing and invalid parameters. Default code is 400.""" + + def __init__(self, message, code=400): + """Initialize the exception.""" + super(InputException, self).__init__(message, code) + + +class DataException(SmartAPIException): + """Represents a bad response from the backend Order Management System (OMS). Default code is 502.""" + + def __init__(self, message, code=502): + """Initialize the exception.""" + super(DataException, self).__init__(message, code) + + +class NetworkException(SmartAPIException): + """Represents a network issue between api and the backend Order Management System (OMS). Default code is 503.""" + + def __init__(self, message, code=503): + """Initialize the exception.""" + super(NetworkException, self).__init__(message, code) diff --git a/SmartApi/version.py b/SmartApi/version.py new file mode 100644 index 0000000..78d56e9 --- /dev/null +++ b/SmartApi/version.py @@ -0,0 +1,13 @@ +__title__ = "smartapi-python" +__description__ = "Angel Broking openApi integration" +__url__ = "https://www.angelbroking.com/" +__download_url__ = "https://github.com/angelbroking-github/smartapi-python" +__version__ = "1.2.6" +__author__ = "ab-smartapi" +__token__ = "ab-smartapi" +__author_email__ = "smartapi.sdk@gmail.com" + + +# [pypi] +# username = __token__ +# password = pypi-AgEIcHlwaS5vcmcCJDkwNzYwNzU1LWMwOTUtNGNkOC1iYjQzLTU3OWNhZjI1NDQ1MwACJXsicGVybWlzc2lvbnMiOiAidXNlciIsICJ2ZXJzaW9uIjogMX0AAAYgSpW5PAywXvchMUQnkF5H6-SolJysfUvIWopMsxE4hCM diff --git a/SmartApi/webSocket.py b/SmartApi/webSocket.py new file mode 100644 index 0000000..c98b1ea --- /dev/null +++ b/SmartApi/webSocket.py @@ -0,0 +1,448 @@ + +import six +import sys +import time +import json +import struct +import logging +import threading +import base64 +import zlib +from datetime import datetime +from twisted.internet import reactor, ssl +from twisted.python import log as twisted_log +from twisted.internet.protocol import ReconnectingClientFactory +from autobahn.twisted.websocket import WebSocketClientProtocol, \ + WebSocketClientFactory, connectWS + +log = logging.getLogger(__name__) + +class SmartSocketClientProtocol(WebSocketClientProtocol): + + def __init__(self, *args, **kwargs): + super(SmartSocketClientProtocol,self).__init__(*args,**kwargs) + + def onConnect(self, response): # noqa + """Called when WebSocket server connection was established""" + self.factory.ws = self + + if self.factory.on_connect: + self.factory.on_connect(self, response) + + def onOpen(self): + if self.factory.on_open: + self.factory.on_open(self) + + + + def onMessage(self, payload, is_binary): # noqa + """Called when text or binary message is received.""" + if self.factory.on_message: + self.factory.on_message(self, payload, is_binary) + + + def onClose(self, was_clean, code, reason): # noqa + """Called when connection is closed.""" + if not was_clean: + if self.factory.on_error: + self.factory.on_error(self, code, reason) + + if self.factory.on_close: + self.factory.on_close(self, code, reason) + + +class SmartSocketClientFactory(WebSocketClientFactory,ReconnectingClientFactory): + protocol = SmartSocketClientProtocol + + maxDelay = 5 + maxRetries = 10 + + _last_connection_time = None + + def __init__(self, *args, **kwargs): + """Initialize with default callback method values.""" + self.debug = False + self.ws = None + self.on_open = None + self.on_error = None + self.on_close = None + self.on_message = None + self.on_connect = None + self.on_reconnect = None + self.on_noreconnect = None + + + super(SmartSocketClientFactory, self).__init__(*args, **kwargs) + + def startedConnecting(self, connector): # noqa + """On connecting start or reconnection.""" + if not self._last_connection_time and self.debug: + log.debug("Start WebSocket connection.") + + self._last_connection_time = time.time() + + def clientConnectionFailed(self, connector, reason): # noqa + """On connection failure (When connect request fails)""" + if self.retries > 0: + print("Retrying connection. Retry attempt count: {}. Next retry in around: {} seconds".format(self.retries, int(round(self.delay)))) + + # on reconnect callback + if self.on_reconnect: + self.on_reconnect(self.retries) + + # Retry the connection + self.retry(connector) + self.send_noreconnect() + + def clientConnectionLost(self, connector, reason): # noqa + """On connection lost (When ongoing connection got disconnected).""" + if self.retries > 0: + # on reconnect callback + if self.on_reconnect: + self.on_reconnect(self.retries) + + # Retry the connection + self.retry(connector) + self.send_noreconnect() + + def send_noreconnect(self): + """Callback `no_reconnect` if max retries are exhausted.""" + if self.maxRetries is not None and (self.retries > self.maxRetries): + if self.debug: + log.debug("Maximum retries ({}) exhausted.".format(self.maxRetries)) + + if self.on_noreconnect: + self.on_noreconnect() + +class WebSocket(object): + EXCHANGE_MAP = { + "nse": 1, + "nfo": 2, + "cds": 3, + "bse": 4, + "bfo": 5, + "bsecds": 6, + "mcx": 7, + "mcxsx": 8, + "indices": 9 + } + # Default connection timeout + CONNECT_TIMEOUT = 30 + # Default Reconnect max delay. + RECONNECT_MAX_DELAY = 60 + # Default reconnect attempts + RECONNECT_MAX_TRIES = 50 + + ROOT_URI='wss://wsfeeds.angelbroking.com/NestHtml5Mobile/socket/stream' + + # Flag to set if its first connect + _is_first_connect = True + + # Minimum delay which should be set between retries. User can't set less than this + _minimum_reconnect_max_delay = 5 + # Maximum number or retries user can set + _maximum_reconnect_max_tries = 300 + + feed_token=None + client_code=None + def __init__(self, FEED_TOKEN, CLIENT_CODE,debug=False, root=None,reconnect=True,reconnect_max_tries=RECONNECT_MAX_TRIES, reconnect_max_delay=RECONNECT_MAX_DELAY,connect_timeout=CONNECT_TIMEOUT): + + + self.root = root or self.ROOT_URI + self.feed_token= FEED_TOKEN + self.client_code= CLIENT_CODE + + # Set max reconnect tries + if reconnect_max_tries > self._maximum_reconnect_max_tries: + log.warning("`reconnect_max_tries` can not be more than {val}. Setting to highest possible value - {val}.".format( + val=self._maximum_reconnect_max_tries)) + self.reconnect_max_tries = self._maximum_reconnect_max_tries + else: + self.reconnect_max_tries = reconnect_max_tries + + # Set max reconnect delay + if reconnect_max_delay < self._minimum_reconnect_max_delay: + log.warning("`reconnect_max_delay` can not be less than {val}. Setting to lowest possible value - {val}.".format( + val=self._minimum_reconnect_max_delay)) + self.reconnect_max_delay = self._minimum_reconnect_max_delay + else: + self.reconnect_max_delay = reconnect_max_delay + + self.connect_timeout = connect_timeout + + # Debug enables logs + self.debug = debug + + # Placeholders for callbacks. + self.on_ticks = None + self.on_open = None + self.on_close = None + self.on_error = None + self.on_connect = None + self.on_message = None + self.on_reconnect = None + self.on_noreconnect = None + + + def _create_connection(self, url, **kwargs): + """Create a WebSocket client connection.""" + self.factory = SmartSocketClientFactory(url, **kwargs) + + # Alias for current websocket connection + self.ws = self.factory.ws + + self.factory.debug = self.debug + + # Register private callbacks + self.factory.on_open = self._on_open + self.factory.on_error = self._on_error + self.factory.on_close = self._on_close + self.factory.on_message = self._on_message + self.factory.on_connect = self._on_connect + self.factory.on_reconnect = self._on_reconnect + self.factory.on_noreconnect = self._on_noreconnect + + + self.factory.maxDelay = self.reconnect_max_delay + self.factory.maxRetries = self.reconnect_max_tries + + def connect(self, threaded=False, disable_ssl_verification=False, proxy=None): + #print("Connect") + self._create_connection(self.ROOT_URI) + + context_factory = None + #print(self.factory.isSecure,disable_ssl_verification) + if self.factory.isSecure and not disable_ssl_verification: + context_factory = ssl.ClientContextFactory() + #print("context_factory",context_factory) + connectWS(self.factory, contextFactory=context_factory, timeout=30) + + # Run in seperate thread of blocking + opts = {} + + # Run when reactor is not running + if not reactor.running: + if threaded: + #print("inside threaded") + # Signals are not allowed in non main thread by twisted so suppress it. + opts["installSignalHandlers"] = False + self.websocket_thread = threading.Thread(target=reactor.run, kwargs=opts) + self.websocket_thread.daemon = True + self.websocket_thread.start() + else: + reactor.run(**opts) + + + def is_connected(self): + #print("Check if WebSocket connection is established.") + if self.ws and self.ws.state == self.ws.STATE_OPEN: + return True + else: + return False + + def _close(self, code=None, reason=None): + #print("Close the WebSocket connection.") + if self.ws: + self.ws.sendClose(code, reason) + + def close(self, code=None, reason=None): + """Close the WebSocket connection.""" + self.stop_retry() + self._close(code, reason) + + def stop(self): + """Stop the event loop. Should be used if main thread has to be closed in `on_close` method.""" + #print("stop") + + reactor.stop() + + def stop_retry(self): + """Stop auto retry when it is in progress.""" + if self.factory: + self.factory.stopTrying() + + def _on_reconnect(self, attempts_count): + if self.on_reconnect: + return self.on_reconnect(self, attempts_count) + + def _on_noreconnect(self): + if self.on_noreconnect: + return self.on_noreconnect(self) + + def websocket_connection(self): + if self.client_code == None or self.feed_token == None: + return "client_code or feed_token or task is missing" + + request={"task":"cn","channel":"","token":self.feed_token,"user":self.client_code,"acctid":self.client_code} + self.ws.sendMessage( + six.b(json.dumps(request)) + ) + #print(request) + + threading.Thread(target=self.heartBeat,daemon=True).start() + + def send_request(self,token,task): + if task in ("mw","sfi","dp"): + strwatchlistscrips = token #dynamic call + + try: + request={"task":task,"channel":strwatchlistscrips,"token":self.feed_token,"user":self.client_code,"acctid":self.client_code} + + self.ws.sendMessage( + six.b(json.dumps(request)) + ) + return True + except Exception as e: + self._close(reason="Error while request sending: {}".format(str(e))) + raise + else: + print("The task entered is invalid, Please enter correct task(mw,sfi,dp) ") + + def _on_connect(self, ws, response): + #print("-----_on_connect-------") + self.ws = ws + if self.on_connect: + + print(self.on_connect) + self.on_connect(self, response) + #self.websocket_connection + + def _on_close(self, ws, code, reason): + """Call `on_close` callback when connection is closed.""" + log.debug("Connection closed: {} - {}".format(code, str(reason))) + + if self.on_close: + self.on_close(self, code, reason) + + def _on_error(self, ws, code, reason): + """Call `on_error` callback when connection throws an error.""" + log.debug("Connection error: {} - {}".format(code, str(reason))) + + if self.on_error: + self.on_error(self, code, reason) + + + + def _on_message(self, ws, payload, is_binary): + """Call `on_message` callback when text message is received.""" + if self.on_message: + self.on_message(self, payload, is_binary) + + # If the message is binary, parse it and send it to the callback. + if self.on_ticks and is_binary and len(payload) > 4: + self.on_ticks(self, self._parse_binary(payload)) + + # Parse text messages + if not is_binary: + self._parse_text_message(payload) + + def _on_open(self, ws): + if not self._is_first_connect: + self.connect() + + self._is_first_connect = False + + if self.on_open: + return self.on_open(self) + + + def heartBeat(self): + while True: + try: + request={"task":"hb","channel":"","token":self.feed_token,"user":self.client_code,"acctid":self.client_code} + self.ws.sendMessage( + six.b(json.dumps(request)) + ) + + except: + print("HeartBeats Failed") + time.sleep(60) + + + def _parse_text_message(self, payload): + """Parse text message.""" + # Decode unicode data + if not six.PY2 and type(payload) == bytes: + payload = payload.decode("utf-8") + + data =base64.b64decode(payload) + + try: + data = bytes((zlib.decompress(data)).decode("utf-8"), 'utf-8') + data = json.loads(data.decode('utf8').replace("'", '"')) + data = json.loads(json.dumps(data, indent=4, sort_keys=True)) + except ValueError: + return + + self.on_ticks(self, data) + + def _parse_binary(self, bin): + """Parse binary data to a (list of) ticks structure.""" + packets = self._split_packets(bin) # split data to individual ticks packet + data = [] + + for packet in packets: + instrument_token = self._unpack_int(packet, 0, 4) + segment = instrument_token & 0xff # Retrive segment constant from instrument_token + + divisor = 10000000.0 if segment == self.EXCHANGE_MAP["cds"] else 100.0 + + # All indices are not tradable + tradable = False if segment == self.EXCHANGE_MAP["indices"] else True + try: + last_trade_time = datetime.fromtimestamp(self._unpack_int(packet, 44, 48)) + except Exception: + last_trade_time = None + + try: + timestamp = datetime.fromtimestamp(self._unpack_int(packet, 60, 64)) + except Exception: + timestamp = None + + d["last_trade_time"] = last_trade_time + d["oi"] = self._unpack_int(packet, 48, 52) + d["oi_day_high"] = self._unpack_int(packet, 52, 56) + d["oi_day_low"] = self._unpack_int(packet, 56, 60) + d["timestamp"] = timestamp + + # Market depth entries. + depth = { + "buy": [], + "sell": [] + } + + # Compile the market depth lists. + for i, p in enumerate(range(64, len(packet), 12)): + depth["sell" if i >= 5 else "buy"].append({ + "quantity": self._unpack_int(packet, p, p + 4), + "price": self._unpack_int(packet, p + 4, p + 8) / divisor, + "orders": self._unpack_int(packet, p + 8, p + 10, byte_format="H") + }) + + d["depth"] = depth + + data.append(d) + + return data + + def _unpack_int(self, bin, start, end, byte_format="I"): + """Unpack binary data as unsgined interger.""" + return struct.unpack(">" + byte_format, bin[start:end])[0] + + def _split_packets(self, bin): + """Split the data to individual packets of ticks.""" + # Ignore heartbeat data. + if len(bin) < 2: + return [] + + number_of_packets = self._unpack_int(bin, 0, 2, byte_format="H") + packets = [] + + j = 2 + for i in range(number_of_packets): + packet_length = self._unpack_int(bin, j, j + 2, byte_format="H") + packets.append(bin[j + 2: j + 2 + packet_length]) + j = j + 2 + packet_length + + return packets + \ No newline at end of file diff --git a/example/sample.py b/example/sample.py new file mode 100644 index 0000000..9aca435 --- /dev/null +++ b/example/sample.py @@ -0,0 +1,113 @@ +# package import statement +from smartapi import SmartConnect #or from smartapi.smartConnect import SmartConnect +#import smartapi.smartExceptions(for smartExceptions) + +#create object of call +obj=SmartConnect(api_key="your api key") + +#login api call + +data = obj.generateSession("Your Client ID","Your Password") +refreshToken= data['data']['refreshToken'] + +#fetch the feedtoken +feedToken=obj.getfeedToken() + +#fetch User Profile +userProfile= obj.getProfile(refreshToken) +#place order +try: + orderparams = { + "variety": "NORMAL", + "tradingsymbol": "SBIN-EQ", + "symboltoken": "3045", + "transactiontype": "BUY", + "exchange": "NSE", + "ordertype": "LIMIT", + "producttype": "INTRADAY", + "duration": "DAY", + "price": "19500", + "squareoff": "0", + "stoploss": "0", + "quantity": "1" + } + orderId=obj.placeOrder(orderparams) + print("The order id is: {}".format(orderId)) +except Exception as e: + print("Order placement failed: {}".format(e.message)) +#gtt rule creation +try: + gttCreateParams={ + "tradingsymbol" : "SBIN-EQ", + "symboltoken" : "3045", + "exchange" : "NSE", + "producttype" : "MARGIN", + "transactiontype" : "BUY", + "price" : 100000, + "qty" : 10, + "disclosedqty": 10, + "triggerprice" : 200000, + "timeperiod" : 365 + } + rule_id=obj.gttCreateRule(gttCreateParams) + print("The GTT rule id is: {}".format(rule_id)) +except Exception as e: + print("GTT Rule creation failed: {}".format(e.message)) + +#gtt rule list +try: + status=["FORALL"] #should be a list + page=1 + count=10 + lists=obj.gttLists(status,page,count) +except Exception as e: + print("GTT Rule List failed: {}".format(e.message)) + +#Historic api +try: + historicParam={ + "exchange": "NSE", + "symboltoken": "3045", + "interval": "ONE_MINUTE", + "fromdate": "2021-02-08 09:00", + "todate": "2021-02-08 09:16" + } + obj.getCandleData(historicParam) +except Exception as e: + print("Historic Api failed: {}".format(e.message)) +#logout +try: + logout=obj.terminateSession('Your Client Id') + print("Logout Successfull") +except Exception as e: + print("Logout failed: {}".format(e.message)) + + + +## WebSocket +from smartapi import WebSocket + +FEED_TOKEN= "your feed token" +CLIENT_CODE="your client Id" +token="channel you want the information of" #"nse_cm|2885&nse_cm|1594&nse_cm|11536" +task="task" #"mw"|"sfi"|"dp" +ss = WebSocket(FEED_TOKEN, CLIENT_CODE) + +def on_tick(ws, tick): + print("Ticks: {}".format(tick)) + +def on_connect(ws, response): + ws.websocket_connection() # Websocket connection + ws.send_request(token,task) + +def on_close(ws, code, reason): + ws.stop() + +# Assign the callbacks. +ss.on_ticks = on_tick +ss.on_connect = on_connect +ss.on_close = on_close + +ss.connect() + + diff --git a/example/smartwebsocketexample.py b/example/smartwebsocketexample.py new file mode 100644 index 0000000..fe620c4 --- /dev/null +++ b/example/smartwebsocketexample.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed May 26 18:50:44 2021 + +@author: Sandip.Khairnar +""" + +from smartapiwebsocket import SmartWebSocket + +# feed_token=092017047 +FEED_TOKEN="092017047" +CLIENT_CODE="S212741" +# token="mcx_fo|224395" +token="nse_cm|2885&nse_cm|1594&nse_cm|11536&nse_cm|3045" +# token="mcx_fo|226745&mcx_fo|220822&mcx_fo|227182&mcx_fo|221599" +task="mw" +ss = SmartWebSocket(FEED_TOKEN, CLIENT_CODE) + +def on_message(ws, message): + print("Ticks: {}".format(message)) + +def on_open(ws): + print("on open") + ss.subscribe(task,token) + +def on_error(ws, error): + print(error) + +def on_close(ws): + print("Close") + +# Assign the callbacks. +ss._on_open = on_open +ss._on_message = on_message +ss._on_error = on_error +ss._on_close = on_close + +ss.connect() \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000..84c6643 --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,2 @@ +requests>=2.24.0 +twine>=1.13.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d34b4b4 --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ + +from setuptools import setup, find_packages + +about={} +with open("README.md", "r") as readme_file: + readme = readme_file.read() + +requirements = [ + "requests>=2.18.4", + "six>=1.11.0", + "python-dateutil>=2.6.1" + ] + +setup( + name="smartapi-python", + version="1.2.7", + author="ab-smartapi", + author_email="smartapi.sdk@gmail.com", + description="Angel Broking openApi integration", + long_description=readme, + long_description_content_type="text/markdown", + url="https://github.com/angelbroking-github/smartapi-python", + packages=["smartapi"], + install_requires=requirements, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Financial and Insurance Industry", + "Programming Language :: Python", + "Natural Language :: English", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Office/Business :: Financial :: Investment", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Libraries" + ], +) \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..2193513 Binary files /dev/null and b/test/__init__.py differ diff --git a/test/test.py b/test/test.py new file mode 100644 index 0000000..57267d6 --- /dev/null +++ b/test/test.py @@ -0,0 +1,146 @@ +from smartapi import SmartConnect + +#---------for smartExceptions--------- +#import smartapi.smartExceptions +#or +#from smartapi import smartExceptions + +smartApi =SmartConnect(api_key="Your Api Key") + +login = smartApi.generateSession('Your Client Id', 'Your Password') + +refreshToken = login['data']['refreshToken'] + +feedToken = smartApi.getfeedToken() + +smartApi.getProfile(refreshToken) + +smartApi.generateToken(refreshToken) + +orderparams = { + "variety": "NORMAL", + "tradingsymbol": "SBIN-EQ", + "symboltoken": "3045", + "transactiontype": "BUY", + "exchange": "NSE", + "ordertype": "LIMIT", + "producttype": "INTRADAY", + "duration": "DAY", + "price": "19500", + "squareoff": "0", + "stoploss": "0", + "quantity": "1" +} +orderid = smartApi.placeOrder(orderparams) + +modifyparams = { + "variety": "NORMAL", + "orderid": orderid, + "ordertype": "LIMIT", + "producttype": "INTRADAY", + "duration": "DAY", + "price": "19500", + "quantity": "1", + "tradingsymbol":"SBIN-EQ", + "symboltoken":"3045", + "exchange":"NSE" +} +smartApi.modifyOrder(modifyparams) + +smartApi.cancelOrder(orderid, "NORMAL") + +smartApi.orderBook() + +smartApi.tradeBook() + +smartApi.rmsLimit() + +smartApi.position() + +smartApi.holding() + +exchange = "NSE" +tradingsymbol = "SBIN-EQ" +symboltoken = 3045 +smartApi.ltpData("NSE", "SBIN-EQ", "3045") + +params={ + "exchange": "NSE", + "oldproducttype":"DELIVERY", + "newproducttype": "MARGIN", + "tradingsymbol": "SBIN-EQ", + "transactiontype":"BUY", + "quantity":1, + "type":"DAY" + +} + +smartApi.convertPosition(params) +gttCreateParams={ + "tradingsymbol" : "SBIN-EQ", + "symboltoken" : "3045", + "exchange" : "NSE", + "producttype" : "MARGIN", + "transactiontype" : "BUY", + "price" : 100000, + "qty" : 10, + "disclosedqty": 10, + "triggerprice" : 200000, + "timeperiod" : 365 + } +rule_id=smartApi.gttCreateRule(gttCreateParams) + +gttModifyParams={ + "id": rule_id, + "symboltoken":"3045", + "exchange":"NSE", + "price":19500, + "quantity":10, + "triggerprice":200000, + "disclosedqty":10, + "timeperiod":365 + } +modified_id=smartApi.gttModifyRule(gttModifyParams) + +cancelParams={ + "id": rule_id, + "symboltoken":"3045", + "exchange":"NSE" + } + +cancelled_id=smartApi.gttCancelRule(cancelParams) + +smartApi.gttDetails(rule_id) + +smartApi.gttLists('List of status','','') + +smartApi.terminateSession('Your Client Id') + +## Websocket Programming + +from smartapi import WebSocket +import multiprocessing +import sys +FEED_TOKEN=feedToken +CLIENT_CODE="Your Client Id" +token=None +task=None +ss = WebSocket(FEED_TOKEN, CLIENT_CODE) +def on_tick(ws, tick): + print("Ticks: {}".format(tick)) + +def on_connect(ws, response): + ws.websocket_connection() + ws.send_request(token,task) + +def on_close(ws, code, reason): + ws.stop() + +# Assign the callbacks. +ss.on_ticks = on_tick +ss.on_connect = on_connect +ss.on_close = on_close + +p1 = multiprocessing.Process(target = ss.connect()) +sys.exit() +p1.start() \ No newline at end of file