diff --git a/.gitignore b/.gitignore index 3953ffd0..0b843705 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ -.pypirc \ No newline at end of file +.pypirc +*.xml +.idea/smartapi-python.iml +build/* +smartapi_python.egg-info +dist/* \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/README.md b/README.md index 4ded8eb6..6ca796fe 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,202 @@ -# Migrated to New Github Repo https://github.com/angel-one/smartapi-python -For latest updates and bug fixes please refer to the new git repo. +# SMARTAPI-PYTHON +SMARTAPI-PYTHON is a Python library for interacting with Angel's Trading platform ,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 -r requirements_dev.txt # for downloading the other required packages +pip install smartapi-python +pip install websocket-client +``` + +## Usage + +```python +# package import statement +from SmartApi import SmartConnect #or from SmartApi.smartConnect import SmartConnect +import pyotp + +api_key = 'Your Api Key' +clientId = 'Your Client Id' +pwd = 'Your Pin' +smartApi = SmartConnect(api_key) +token = "Your QR code value" +totp=pyotp.TOTP(token).now() +correlation_id = "abc123" + +# login api call + +data = smartApi.generateSession(clientId, pwd, totp) +# print(data) +authToken = data['data']['jwtToken'] +refreshToken = data['data']['refreshToken'] + +# fetch the feedtoken +feedToken = smartApi.getfeedToken() + +# fetch User Profile +res = smartApi.getProfile(refreshToken) +smartApi.generateToken(refreshToken) +res=res['data']['exchanges'] + +#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=smartApi.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=smartApi.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=smartApi.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 SmartApi 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() + + +####### Websocket sample code ended here ####### + +####### Websocket V2 sample code ####### + +from SmartApi.smartWebSocketV2 import SmartWebSocketV2 +from logzero import logger + +AUTH_TOKEN = "Your Auth_Token" +API_KEY = "Your Api_Key" +CLIENT_CODE = "Your Client Code" +FEED_TOKEN = "Your Feed_Token" +correlation_id = "abc123" +action = 1 +mode = 1 +token_list = [ + { + "exchangeType": 1, + "tokens": ["26009"] + } +] +sws = SmartWebSocketV2(AUTH_TOKEN, API_KEY, CLIENT_CODE, FEED_TOKEN) + +def on_data(wsapp, message): + logger.info("Ticks: {}".format(message)) + # close_connection() + +def on_open(wsapp): + logger.info("on open") + sws.subscribe(correlation_id, mode, token_list) + +def on_error(wsapp, error): + logger.error(error) + +def on_close(wsapp): + logger.info("Close") + +def close_connection(): + sws.close_connection() + + +# Assign the callbacks. +sws.on_open = on_open +sws.on_data = on_data +sws.on_error = on_error +sws.on_close = on_close + +sws.connect() + +``` diff --git a/SmartApi/__init__.py b/SmartApi/__init__.py index 720c8164..8e9ecc04 100644 --- a/SmartApi/__init__.py +++ b/SmartApi/__init__.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals,absolute_import -from smartapi.smartConnect import SmartConnect -# from smartapi.webSocket import WebSocket -from smartapi.smartApiWebsocket import SmartWebSocket +from SmartApi.smartConnect import SmartConnect +# from SmartApi.webSocket import WebSocket +from SmartApi.smartApiWebsocket import SmartWebSocket __all__ = ["SmartConnect","SmartWebSocket"] diff --git a/SmartApi/smartConnect.py b/SmartApi/smartConnect.py index c8f65a89..2bfbbe0f 100644 --- a/SmartApi/smartConnect.py +++ b/SmartApi/smartConnect.py @@ -1,18 +1,12 @@ 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 SmartApi.smartExceptions as ex import requests from requests import get import re, uuid import socket -import platform -from smartapi.version import __version__, __title__ +from SmartApi.version import __version__, __title__ log = logging.getLogger(__name__) #user_sys=platform.system() @@ -222,30 +216,31 @@ def _getRequest(self, route, params=None): """Alias for sending a GET request.""" return self._request(route, "GET", params) - def generateSession(self,clientCode,password): + def generateSession(self,clientCode,password,totp): - params={"clientcode":clientCode,"password":password} + params={"clientcode":clientCode,"password":password,"totp":totp} 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'] + refreshToken = loginResultObject['data']['refreshToken'] + feedToken = loginResultObject['data']['feedToken'] self.setRefreshToken(refreshToken) self.setFeedToken(feedToken) - user=self.getProfile(refreshToken) - - id=user['data']['clientcode'] - #id='D88311' + user = self.getProfile(refreshToken) + + id = user['data']['clientcode'] + # id='D88311' self.setUserId(id) - user['data']['jwtToken']="Bearer "+jwtToken - user['data']['refreshToken']=refreshToken + user['data']['jwtToken'] = "Bearer " + jwtToken + user['data']['refreshToken'] = refreshToken + user['data']['feedToken'] = feedToken - return user else: return loginResultObject + def terminateSession(self,clientCode): logoutResponseObject=self._postRequest("api.logout",{"clientcode":clientCode}) return logoutResponseObject diff --git a/SmartApi/smartWebSocketV2.py b/SmartApi/smartWebSocketV2.py new file mode 100644 index 00000000..d6b6c90d --- /dev/null +++ b/SmartApi/smartWebSocketV2.py @@ -0,0 +1,437 @@ +import struct +import threading +import time +import ssl +import json +import websocket +from datetime import datetime, timedelta +from threading import Timer + + +class SmartWebSocketV2(object): + """ + SmartAPI Web Socket version 2 + """ + + ROOT_URI = "ws://smartapisocket.angelone.in/smart-stream" + HEART_BEAT_MESSAGE = "ping" + HEART_BEAT_INTERVAL = 10 # Adjusted to 10s + LITTLE_ENDIAN_BYTE_ORDER = "<" + RESUBSCRIBE_FLAG = False + # HB_THREAD_FLAG = True + + # Available Actions + SUBSCRIBE_ACTION = 1 + UNSUBSCRIBE_ACTION = 0 + + # Possible Subscription Mode + LTP_MODE = 1 + QUOTE = 2 + SNAP_QUOTE = 3 + + # Exchange Type + NSE_CM = 1 + NSE_FO = 2 + BSE_CM = 3 + BSE_FO = 4 + MCX_FO = 5 + NCX_FO = 7 + CDE_FO = 13 + + # Subscription Mode Map + SUBSCRIPTION_MODE_MAP = { + 1: "LTP", + 2: "QUOTE", + 3: "SNAP_QUOTE" + } + + wsapp = None + input_request_dict = {} + current_retry_attempt = 0 + + def __init__(self, auth_token, api_key, client_code, feed_token, max_retry_attempt=1): + """ + Initialise the SmartWebSocketV2 instance + Parameters + ------ + auth_token: string + jwt auth token received from Login API + api_key: string + api key from Smart API account + client_code: string + angel one account id + feed_token: string + feed token received from Login API + """ + self.auth_token = auth_token + self.api_key = api_key + self.client_code = client_code + self.feed_token = feed_token + self.DISCONNECT_FLAG = True + self.last_pong_timestamp = None + self.MAX_RETRY_ATTEMPT = max_retry_attempt + + if not self._sanity_check(): + raise Exception("Provide valid value for all the tokens") + + def _sanity_check(self): + return True + # if self.auth_token is None or self.api_key is None or self.client_code is None or self.feed_token is None: + # return False + # return True + + def _on_message(self, wsapp, message): + print("message--->", message) + if message != "pong": + parsed_message = self._parse_binary_data(message) + self.on_message(wsapp, parsed_message) + else: + self.on_message(wsapp, message) + + def _on_data(self, wsapp, data, data_type, continue_flag): + + if data_type == 2: + parsed_message = self._parse_binary_data(data) + self.on_data(wsapp, parsed_message) + else: + self.on_data(wsapp, data) + + def _on_open(self, wsapp): + if self.RESUBSCRIBE_FLAG: + self.resubscribe() + self.RESUBSCRIBE_FLAG = False # Add this line to prevent resubscription on subsequent reconnects + else: + self.on_open(wsapp) + + def _on_pong(self, wsapp, data): + if data == self.HEART_BEAT_MESSAGE: + timestamp = time.time() + formatted_timestamp = time.strftime("%d-%m-%y %H:%M:%S", time.localtime(timestamp)) + print(f"In on pong function ==> {data}, Timestamp: {formatted_timestamp}") + self.last_pong_timestamp = timestamp + else: + # Handle the received feed data here + self.on_data(wsapp, data) + + def _on_ping(self, wsapp, data): + timestamp = time.time() + formatted_timestamp = time.strftime("%d-%m-%y %H:%M:%S", time.localtime(timestamp)) + print(f"In on ping function ==> {data}, Timestamp: {formatted_timestamp}") + self.last_ping_timestamp = timestamp + + def check_connection_status(self): + current_time = time.time() + if self.last_pong_timestamp is not None and current_time - self.last_pong_timestamp > 2*self.HEART_BEAT_MESSAGE: + # Stale connection detected, take appropriate action + self.close_connection() + self.connect() + + def start_ping_timer(self): + def send_ping(): + try: + current_time = datetime.now() + if self.last_pong_timestamp is None or self.last_pong_timestamp < current_time - timedelta(self.HEART_BEAT_MESSAGE): + # print("stale connection detected") + # self.wsapp.close() + self.connect() + else: + self.last_ping_timestamp = time.time() + except Exception as e: + self.wsapp.close() + self.resubscribe() + + ping_timer = Timer(5, send_ping) + ping_timer.start() + + def subscribe(self, correlation_id, mode, token_list): + """ + This Function subscribe the price data for the given token + Parameters + ------ + correlation_id: string + A 10 character alphanumeric ID client may provide which will be returned by the server in error response + to indicate which request generated error response. + Clients can use this optional ID for tracking purposes between request and corresponding error response. + mode: integer + It denotes the subscription type + possible values -> 1, 2 and 3 + 1 -> LTP + 2 -> Quote + 3 -> Snap Quote + token_list: list of dict + Sample Value -> + [ + { "exchangeType": 1, "tokens": ["10626", "5290"]}, + {"exchangeType": 5, "tokens": [ "234230", "234235", "234219"]} + ] + exchangeType: integer + possible values -> + 1 -> nse_cm + 2 -> nse_fo + 3 -> bse_cm + 4 -> bse_fo + 5 -> mcx_fo + 7 -> ncx_fo + 13 -> cde_fo + tokens: list of string + """ + try: + request_data = { + "correlationID": correlation_id, + "action": self.SUBSCRIBE_ACTION, + "params": { + "mode": mode, + "tokenList": token_list + } + } + if self.input_request_dict.get(mode, None) is None: + self.input_request_dict[mode] = {} + + for token in token_list: + if token['exchangeType'] in self.input_request_dict[mode]: + self.input_request_dict[mode][token['exchangeType']].extend(token["tokens"]) + else: + self.input_request_dict[mode][token['exchangeType']] = token["tokens"] + self.wsapp.send(json.dumps(request_data)) + self.RESUBSCRIBE_FLAG = True + except Exception as e: + raise e + + def unsubscribe(self, correlation_id, mode, token_list): + """ + This function unsubscribe the data for given token + Parameters + ------ + correlation_id: string + A 10 character alphanumeric ID client may provide which will be returned by the server in error response + to indicate which request generated error response. + Clients can use this optional ID for tracking purposes between request and corresponding error response. + mode: integer + It denotes the subscription type + possible values -> 1, 2 and 3 + 1 -> LTP + 2 -> Quote + 3 -> Snap Quote + token_list: list of dict + Sample Value -> + [ + { "exchangeType": 1, "tokens": ["10626", "5290"]}, + {"exchangeType": 5, "tokens": [ "234230", "234235", "234219"]} + ] + exchangeType: integer + possible values -> + 1 -> nse_cm + 2 -> nse_fo + 3 -> bse_cm + 4 -> bse_fo + 5 -> mcx_fo + 7 -> ncx_fo + 13 -> cde_fo + tokens: list of string + """ + try: + request_data = { + "correlationID": correlation_id, + "action": self.UNSUBSCRIBE_ACTION, + "params": { + "mode": mode, + "tokenList": token_list + } + } + + self.input_request_dict.update(request_data) + self.input_request_dict.update(request_data) + self.wsapp.send(json.dumps(request_data)) + self.RESUBSCRIBE_FLAG = True + except Exception as e: + raise e + + def resubscribe(self): + try: + for key, val in self.input_request_dict.items(): + token_list = [] + for key1, val1 in val.items(): + temp_data = { + 'exchangeType': key1, + 'tokens': val1 + } + token_list.append(temp_data) + request_data = { + "action": self.SUBSCRIBE_ACTION, + "params": { + "mode": key, + "tokenList": token_list + } + } + self.wsapp.send(json.dumps(request_data)) + except Exception as e: + raise e + + def connect(self): + """ + Make the web socket connection with the server + """ + headers = { + "Authorization": self.auth_token, + "x-api-key": self.api_key, + "x-client-code": self.client_code, + "x-feed-token": self.feed_token + } + + try: + self.wsapp = websocket.WebSocketApp(self.ROOT_URI, header=headers, on_open=self._on_open, + on_error=self._on_error, on_close=self._on_close, on_data=self._on_data, + on_ping=self._on_ping, + on_pong=self._on_pong) + self.wsapp.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}, ping_interval=self.HEART_BEAT_INTERVAL, + ping_payload=self.HEART_BEAT_MESSAGE) + # self.start_ping_timer() + except Exception as e: + raise e + + def close_connection(self): + """ + Closes the connection + """ + self.RESUBSCRIBE_FLAG = False + self.DISCONNECT_FLAG = True + # self.HB_THREAD_FLAG = False + if self.wsapp: + self.wsapp.close() + + # def run(self): + # while True: + # if not self.HB_THREAD_FLAG: + # break + # self.send_heart_beat() + # time.sleep(self.HEAR_BEAT_INTERVAL) + + def send_heart_beat(self): + try: + self.wsapp.send(self.HEART_BEAT_MESSAGE) + except Exception as e: + raise e + + def _on_error(self, wsapp, error): + # self.HB_THREAD_FLAG = False + self.RESUBSCRIBE_FLAG = True + if self.current_retry_attempt < self.MAX_RETRY_ATTEMPT: + print("Attempting to resubscribe/reconnect...") + self.current_retry_attempt += 1 + try: + self.close_connection() + self.connect() + except Exception as e: + print("Error occurred during resubscribe/reconnect:", str(e)) + else: + self.close_connection() + + def _on_close(self, wsapp): + # self.HB_THREAD_FLAG = False + # print(self.wsapp.close_frame) + self.on_close(wsapp) + + def _parse_binary_data(self, binary_data): + parsed_data = { + "subscription_mode": self._unpack_data(binary_data, 0, 1, byte_format="B")[0], + "exchange_type": self._unpack_data(binary_data, 1, 2, byte_format="B")[0], + "token": SmartWebSocketV2._parse_token_value(binary_data[2:27]), + "sequence_number": self._unpack_data(binary_data, 27, 35, byte_format="q")[0], + "exchange_timestamp": self._unpack_data(binary_data, 35, 43, byte_format="q")[0], + "last_traded_price": self._unpack_data(binary_data, 43, 51, byte_format="q")[0] + } + try: + parsed_data["subscription_mode_val"] = self.SUBSCRIPTION_MODE_MAP.get(parsed_data["subscription_mode"]) + + if parsed_data["subscription_mode"] in [self.QUOTE, self.SNAP_QUOTE]: + parsed_data["last_traded_quantity"] = self._unpack_data(binary_data, 51, 59, byte_format="q")[0] + parsed_data["average_traded_price"] = self._unpack_data(binary_data, 59, 67, byte_format="q")[0] + parsed_data["volume_trade_for_the_day"] = self._unpack_data(binary_data, 67, 75, byte_format="q")[0] + parsed_data["total_buy_quantity"] = self._unpack_data(binary_data, 75, 83, byte_format="d")[0] + parsed_data["total_sell_quantity"] = self._unpack_data(binary_data, 83, 91, byte_format="d")[0] + parsed_data["open_price_of_the_day"] = self._unpack_data(binary_data, 91, 99, byte_format="q")[0] + parsed_data["high_price_of_the_day"] = self._unpack_data(binary_data, 99, 107, byte_format="q")[0] + parsed_data["low_price_of_the_day"] = self._unpack_data(binary_data, 107, 115, byte_format="q")[0] + parsed_data["closed_price"] = self._unpack_data(binary_data, 115, 123, byte_format="q")[0] + + if parsed_data["subscription_mode"] == self.SNAP_QUOTE: + parsed_data["last_traded_timestamp"] = self._unpack_data(binary_data, 123, 131, byte_format="q")[0] + parsed_data["open_interest"] = self._unpack_data(binary_data, 131, 139, byte_format="q")[0] + parsed_data["open_interest_change_percentage"] = \ + self._unpack_data(binary_data, 139, 147, byte_format="q")[0] + parsed_data["upper_circuit_limit"] = self._unpack_data(binary_data, 347, 355, byte_format="q")[0] + parsed_data["lower_circuit_limit"] = self._unpack_data(binary_data, 355, 363, byte_format="q")[0] + parsed_data["52_week_high_price"] = self._unpack_data(binary_data, 363, 371, byte_format="q")[0] + parsed_data["52_week_low_price"] = self._unpack_data(binary_data, 371, 379, byte_format="q")[0] + best_5_buy_and_sell_data = self._parse_best_5_buy_and_sell_data(binary_data[147:347]) + parsed_data["best_5_buy_data"] = best_5_buy_and_sell_data["best_5_sell_data"] + parsed_data["best_5_sell_data"] = best_5_buy_and_sell_data["best_5_buy_data"] + + return parsed_data + except Exception as e: + raise e + + def _unpack_data(self, binary_data, start, end, byte_format="I"): + """ + Unpack Binary Data to the integer according to the specified byte_format. + This function returns the tuple + """ + return struct.unpack(self.LITTLE_ENDIAN_BYTE_ORDER + byte_format, binary_data[start:end]) + + @staticmethod + def _parse_token_value(binary_packet): + token = "" + for i in range(len(binary_packet)): + if chr(binary_packet[i]) == '\x00': + return token + token += chr(binary_packet[i]) + return token + + def _parse_best_5_buy_and_sell_data(self, binary_data): + + def split_packets(binary_packets): + packets = [] + + i = 0 + while i < len(binary_packets): + packets.append(binary_packets[i: i + 20]) + i += 20 + return packets + + best_5_buy_sell_packets = split_packets(binary_data) + + best_5_buy_data = [] + best_5_sell_data = [] + + for packet in best_5_buy_sell_packets: + each_data = { + "flag": self._unpack_data(packet, 0, 2, byte_format="H")[0], + "quantity": self._unpack_data(packet, 2, 10, byte_format="q")[0], + "price": self._unpack_data(packet, 10, 18, byte_format="q")[0], + "no of orders": self._unpack_data(packet, 18, 20, byte_format="H")[0] + } + + if each_data["flag"] == 0: + best_5_buy_data.append(each_data) + else: + best_5_sell_data.append(each_data) + + return { + "best_5_buy_data": best_5_buy_data, + "best_5_sell_data": best_5_sell_data + } + + # def on_message(self, wsapp, message): + # print(message) + + def on_data(self, wsapp, data): + pass + + def on_close(self, wsapp): + pass + + def on_open(self, wsapp): + pass + + def on_error(self): + pass diff --git a/SmartApi/version.py b/SmartApi/version.py index 78d56e9f..c240cb6a 100644 --- a/SmartApi/version.py +++ b/SmartApi/version.py @@ -1,7 +1,7 @@ __title__ = "smartapi-python" __description__ = "Angel Broking openApi integration" __url__ = "https://www.angelbroking.com/" -__download_url__ = "https://github.com/angelbroking-github/smartapi-python" +__download_url__ = "https://github.com/angel-one/smartapi-python" __version__ = "1.2.6" __author__ = "ab-smartapi" __token__ = "ab-smartapi" diff --git a/example/sample.py b/example/sample.py index 9aca4354..50b70489 100644 --- a/example/sample.py +++ b/example/sample.py @@ -1,5 +1,6 @@ # package import statement -from smartapi import SmartConnect #or from smartapi.smartConnect import SmartConnect +from SmartApi import SmartConnect #or from smartapi.smartConnect import SmartConnect + #import smartapi.smartExceptions(for smartExceptions) #create object of call @@ -7,7 +8,8 @@ #login api call -data = obj.generateSession("Your Client ID","Your Password") +data = obj.generateSession("Your Client ID","Your Password","Your totp") + refreshToken= data['data']['refreshToken'] #fetch the feedtoken @@ -85,7 +87,8 @@ ## WebSocket -from smartapi import WebSocket + +from SmartApi.webSocket import WebSocket FEED_TOKEN= "your feed token" CLIENT_CODE="your client Id" diff --git a/example/smartwebsocketexamplev2.py b/example/smartwebsocketexamplev2.py new file mode 100644 index 00000000..a5518b9f --- /dev/null +++ b/example/smartwebsocketexamplev2.py @@ -0,0 +1,57 @@ +from SmartApi.smartWebSocketV2 import SmartWebSocketV2 +from logzero import logger + +AUTH_TOKEN = "authToken" +API_KEY = "api_key" +CLIENT_CODE = "client code" +FEED_TOKEN = "feedToken" +correlation_id = "abc123" +action = 1 +mode = 1 + +token_list = [ + { + "exchangeType": 1, + "tokens": ["26009"] + } +] +token_list1 = [ + { + "action": 0, + "exchangeType": 1, + "tokens": ["26009"] + } +] + +sws = SmartWebSocketV2(AUTH_TOKEN, API_KEY, CLIENT_CODE, FEED_TOKEN) + +def on_data(wsapp, message): + logger.info("Ticks: {}".format(message)) + # close_connection() + +def on_open(wsapp): + logger.info("on open") + sws.subscribe(correlation_id, mode, token_list) + # sws.unsubscribe(correlation_id, mode, token_list1) + + +def on_error(wsapp, error): + logger.error(error) + + +def on_close(wsapp): + logger.info("Close") + + + +def close_connection(): + sws.close_connection() + + +# Assign the callbacks. +sws.on_open = on_open +sws.on_data = on_data +sws.on_error = on_error +sws.on_close = on_close + +sws.connect() diff --git a/requirements_dev.txt b/requirements_dev.txt index 84c6643f..b8ff741b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,2 +1,30 @@ -requests>=2.24.0 -twine>=1.13.0 +attrs==23.1.0 +autobahn==23.6.2 +Automat==22.10.0 +certifi==2023.5.7 +cffi==1.15.1 +charset-normalizer==3.1.0 +constantly==15.1.0 +hyperlink==21.0.0 +idna==3.4 +incremental==22.10.0 +isodate==0.6.1 +logzero==1.7.0 +pycparser==2.21 +pyotp==2.8.0 +pyparsing==3.1.0 +python-dateutil==2.8.2 +pytz==2023.3 +rdflib==6.3.2 +rdflib-jsonld==0.6.2 +requests==2.31.0 +simplejson==3.19.1 +six==1.16.0 +smartapi-python==1.3.5 +Twisted==22.10.0 +txaio==23.1.1 +typing_extensions==4.6.3 +urllib3==2.0.3 +websocket-client==1.6.0 +zope.interface==6.0 + diff --git a/setup.py b/setup.py index 88e19084..fc720ad6 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ - from setuptools import setup, find_packages about={} @@ -13,7 +12,7 @@ setup( name="smartapi-python", - version="1.2.8", + version="1.3.5", author="ab-smartapi", author_email="smartapi.sdk@gmail.com", description="Angel Broking openApi integration", @@ -36,4 +35,4 @@ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries" ], -) \ No newline at end of file +) diff --git a/test/test.py b/test/test.py index 57267d6f..3a61cfb1 100644 --- a/test/test.py +++ b/test/test.py @@ -1,21 +1,25 @@ -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'] - +from logzero import logger +from SmartApi.smartConnect import SmartConnect +import pyotp + +api_key = 'Your Api Key' +username = 'Your client code' +pwd = 'Your pin' +smartApi = SmartConnect(api_key) +token = "Your QR value" +totp=pyotp.TOTP(token).now() +correlation_id = "abcde" +data = smartApi.generateSession(username, pwd, totp) +# print(data) +authToken = data['data']['jwtToken'] +refreshToken = data['data']['refreshToken'] feedToken = smartApi.getfeedToken() - -smartApi.getProfile(refreshToken) - +# print("Feed-Token :", feedToken) +res = smartApi.getProfile(refreshToken) +# print("Res:", res) smartApi.generateToken(refreshToken) +res=res['data']['exchanges'] + orderparams = { "variety": "NORMAL", @@ -32,6 +36,7 @@ "quantity": "1" } orderid = smartApi.placeOrder(orderparams) +print("PlaceOrder", orderid) modifyparams = { "variety": "NORMAL", @@ -41,106 +46,158 @@ "duration": "DAY", "price": "19500", "quantity": "1", - "tradingsymbol":"SBIN-EQ", - "symboltoken":"3045", - "exchange":"NSE" + "tradingsymbol": "SBIN-EQ", + "symboltoken": "3045", + "exchange": "NSE" } smartApi.modifyOrder(modifyparams) +print("Modify Orders:",modifyparams) -smartApi.cancelOrder(orderid, "NORMAL") +cancelOrder=smartApi.cancelOrder(orderid, "NORMAL") +print("cancelOrder",cancelOrder) -smartApi.orderBook() +orderbook=smartApi.orderBook() +print("Order Book :", orderbook) -smartApi.tradeBook() +tradebook=smartApi.tradeBook() +print("Trade Book :",tradebook) -smartApi.rmsLimit() +rmslimit=smartApi.rmsLimit() +print("RMS Limit :", rmslimit) -smartApi.position() +pos=smartApi.position() +print("Position :", pos) -smartApi.holding() +holdings=smartApi.holding() +print("Holdings :", holdings) exchange = "NSE" tradingsymbol = "SBIN-EQ" symboltoken = 3045 -smartApi.ltpData("NSE", "SBIN-EQ", "3045") +ltp=smartApi.ltpData("NSE", "SBIN-EQ", "3045") +print("Ltp Data :", ltp) + -params={ +params = { "exchange": "NSE", - "oldproducttype":"DELIVERY", + "oldproducttype": "DELIVERY", "newproducttype": "MARGIN", "tradingsymbol": "SBIN-EQ", - "transactiontype":"BUY", - "quantity":1, - "type":"DAY" + "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() +convertposition=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) +print("Gtt Rule :", rule_id) + +gttModifyParams = { + "id": rule_id, + "symboltoken": "3045", + "exchange": "NSE", + "price": 19500, + "quantity": 10, + "triggerprice": 200000, + "disclosedqty": 10, + "timeperiod": 365 +} +modified_id = smartApi.gttModifyRule(gttModifyParams) +print("Gtt Modified Rule :", modified_id) + +cancelParams = { + "id": rule_id, + "symboltoken": "3045", + "exchange": "NSE" +} + +cancelled_id = smartApi.gttCancelRule(cancelParams) +print("gtt Cancel Rule :", cancelled_id) + +gttdetails=smartApi.gttDetails(rule_id) +print("GTT Details",gttdetails) + +smartApi.gttLists('List of status', '', '') + +candleParams={ + "exchange": "NSE", + "symboltoken": "3045", + "interval": "ONE_MINUTE", + "fromdate": "2021-02-10 09:15", + "todate": "2021-02-10 09:16" +} +candledetails=smartApi.getCandleData(candleParams) +print("Historical Data",candledetails) + +terminate=smartApi.terminateSession('Your client code') +print("Connection Close",terminate) + +# # Websocket Programming + +from SmartApi.smartWebSocketV2 import SmartWebSocketV2 + +AUTH_TOKEN = authToken +API_KEY = api_key +CLIENT_CODE = username +FEED_TOKEN = feedToken +# correlation_id = "abc123" +action = 1 +mode = 1 + +token_list = [ + { + "exchangeType": 1, + "tokens": ["26009","1594"] + } +] +token_list1 = [ + { + "action": 0, + "exchangeType": 1, + "tokens": ["26009"] + } +] + +sws = SmartWebSocketV2(AUTH_TOKEN, API_KEY, CLIENT_CODE, FEED_TOKEN) + +def on_data(wsapp, message): + logger.info("Ticks: {}".format(message)) + close_connection() + +def on_open(wsapp): + logger.info("on open") + sws.subscribe(correlation_id, mode, token_list) + # sws.unsubscribe(correlation_id, mode, token_list1) + + +def on_error(wsapp, error): + logger.error(error) + +def on_close(wsapp): + logger.info("Close") + +def close_connection(): + sws.close_connection() + # Assign the callbacks. -ss.on_ticks = on_tick -ss.on_connect = on_connect -ss.on_close = on_close +sws.on_open = on_open +sws.on_data = on_data +sws.on_error = on_error +sws.on_close = on_close -p1 = multiprocessing.Process(target = ss.connect()) -sys.exit() -p1.start() \ No newline at end of file +sws.connect()