Skip to content

Commit 0db89c0

Browse files
authored
Merge pull request #452 from krishnavelu/develop
Develop -> master for 2.0.1
2 parents a832191 + 51d99b2 commit 0db89c0

File tree

4 files changed

+49
-50
lines changed

4 files changed

+49
-50
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ reliance_nse_eq = alice.get_instrument_by_symbol('NSE', 'RELIANCE')
181181
ongc_bse_eq = alice.get_instrument_by_symbol('BSE', 'ONGC')
182182
india_vix_nse_index = alice.get_instrument_by_symbol('NSE', 'India VIX')
183183
sensex_nse_index = alice.get_instrument_by_symbol('BSE', 'SENSEX')
184-
nifty50_nse_index = alice.get_instrument_by_symbol('NSE', 'Nifty 50')
185-
banknifty_nse_index = alice.get_instrument_by_symbol('NSE', 'Nifty Bank')
184+
nifty50_nse_index = alice.get_instrument_by_symbol('NSE', 'NIFTY 50')
185+
banknifty_nse_index = alice.get_instrument_by_symbol('NSE', 'NIFTY Bank')
186186
```
187187

188188
#### Get a single instrument by it's token number (generally useful only for BSE Equities):
@@ -337,7 +337,7 @@ You can place following types of order through this API.
337337
1. [Stop Loss Limit](#stop-loss-limit-order)
338338
1. [Stop Loss Market](#stop-loss-market-order)
339339
1. [Bracket Order](#bracket-order)
340-
1. [AMO](#after-market-rder)
340+
1. [AMO](#after-market-order)
341341

342342
#### Limit Order (Intraday Buy)
343343
Code

alice_blue/alice_blue.py

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from collections import OrderedDict, namedtuple
2-
from Crypto import Random
3-
from Crypto.Cipher import AES
1+
from collections import namedtuple
2+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
43
from time import sleep
54
from urllib.parse import urlparse, parse_qs
65
import base64
@@ -61,6 +60,7 @@ def __pad(data):
6160
def __unpad(data):
6261
return data[:-(data[-1] if type(data[-1]) == int else ord(data[-1]))]
6362

63+
@staticmethod
6464
def __bytes_to_key(data, salt, output=48):
6565
assert len(salt) == 8, len(salt)
6666
data += salt
@@ -73,12 +73,12 @@ def __bytes_to_key(data, salt, output=48):
7373

7474
@staticmethod
7575
def encrypt(message, passphrase):
76-
salt = Random.new().read(8)
76+
salt = os.urandom(8)
7777
key_iv = CryptoJsAES.__bytes_to_key(passphrase, salt, 32+16)
7878
key = key_iv[:32]
7979
iv = key_iv[32:]
80-
aes = AES.new(key, AES.MODE_CBC, iv)
81-
return base64.b64encode(b"Salted__" + salt + aes.encrypt(CryptoJsAES.__pad(message)))
80+
aes = Cipher(algorithms.AES(key), modes.CBC(iv))
81+
return base64.b64encode(b"Salted__" + salt + aes.encryptor().update(CryptoJsAES.__pad(message)) + aes.encryptor().finalize())
8282

8383
@staticmethod
8484
def decrypt(encrypted, passphrase):
@@ -88,8 +88,8 @@ def decrypt(encrypted, passphrase):
8888
key_iv = CryptoJsAES.__bytes_to_key(passphrase, salt, 32+16)
8989
key = key_iv[:32]
9090
iv = key_iv[32:]
91-
aes = AES.new(key, AES.MODE_CBC, iv)
92-
return CryptoJsAES.__unpad(aes.decrypt(encrypted[16:]))
91+
aes = Cipher(algorithms.AES(key), modes.CBC(iv))
92+
return CryptoJsAES.__unpad(aes.decryptor.update(encrypted[16:]) + aes.decryptor().finalize())
9393

9494
class AliceBlue:
9595
""" AliceBlue Class for all operations related to AliceBlue Server"""
@@ -142,20 +142,20 @@ def __init__(self, username, session_id, master_contracts_to_download = None):
142142
self.__order_update_callback = None
143143
self.__market_status_messages_callback = None
144144
self.__exchange_messages_callback = None
145-
self.__oi_callback = None
146-
self.__dpr_callback = None
147145
self.__subscribers = {}
148146
self.__market_status_messages = []
149147
self.__exchange_messages = []
150148
# Initialize Depth data
151149
self.__depth_data = {}
150+
self.__ltp = {}
152151

153152
try:
154153
self.get_profile()
155154
except Exception as e:
156155
raise Exception(f"Couldn't get profile info with credentials provided '{e}'")
157156
self.__master_contracts_by_token = {}
158157
self.__master_contracts_by_symbol = {}
158+
self.__get_master_contract("INDICES")
159159
if(master_contracts_to_download == None):
160160
for e in self.__enabled_exchanges:
161161
self.__get_master_contract(e)
@@ -248,8 +248,11 @@ def __extract_tick_data(self, data):
248248
data["instrument"] = self.get_instrument_by_token(data.pop("e"), int(data.pop("tk")))
249249
if("ts" in data): # Symbol
250250
data.pop("ts")
251+
if(data["instrument"].symbol not in self.__ltp):
252+
self.__ltp[data["instrument"].symbol] = 0
251253
if("lp" in data): # Last Traded Price
252-
data["ltp"] = float(data.pop("lp"))
254+
self.__ltp[data["instrument"].symbol] = float(data.pop("lp"))
255+
data["ltp"] = self.__ltp[data["instrument"].symbol]
253256
if("pc" in data): # percentage change
254257
data["percent_change"] = float(data.pop("pc"))
255258
if("cv" in data): # change value (absolute change in price)
@@ -399,7 +402,7 @@ def __on_data_callback(self, ws=None, message=None, data_type=None, continue_fla
399402
# message = '{"t":"tf","e":"NSE","tk":"1594","ft":"1662025326","v":"7399482","bp1":"1464.25","sp1":"1464.35","bq1":"460","sq1":"11"}'
400403
# message = '{"t":"df","e":"NSE","tk":"1594","ft":"1662025327","v":"7400196","ltt":"15:12:07","tbq":"510593","tsq":"2364472","bp1":"1464.55","sp1":"1464.90","bp2":"1464.30","sp2":"1464.95","bp3":"1464.25","sp3":"1465.00","bp4":"1464.20","sp4":"1465.05","bp5":"1464.15","sp5":"1465.10","bq1":"1","sq1":"301","bq2":"598","sq2":"61","bq3":"83","sq3":"3573","bq4":"175","sq4":"300","bq5":"482","sq5":"51","bo2":"5","so2":"4","bo3":"2","so3":"42","bo4":"3","so4":"1","bo5":"5","so5":"2"}'
401404
# message = '{"t":"df","e":"NSE","tk":"1594","ft":"1662025324","v":"7397757","ltq":"2","ltt":"15:12:04","tbq":"513958","tsq":"2373240","sp1":"1464.95","sp2":"1465.00","sp3":"1465.05","sp4":"1465.10","sp5":"1465.15","bq1":"275","sq1":"37","sq2":"3472","sq3":"300","sq4":"26","sq5":"110","bo1":"5","so1":"8","so2":"40","so3":"1","so4":"2","so5":"4"}'
402-
#logging.info(f"message - {message}")
405+
# logging.info(f"message - {message}")
403406
if(type(ws) is not websocket.WebSocketApp): # This workaround is to solve the websocket_client's compatiblity issue of older versions. ie.0.40.0 which is used in upstox. Now this will work in both 0.40.0 & newer version of websocket_client
404407
message = ws
405408
data = json.loads(message)
@@ -409,25 +412,21 @@ def __on_data_callback(self, ws=None, message=None, data_type=None, continue_fla
409412
if(self.__subscribe_callback is not None):
410413
data.pop("t")
411414
data = self.__extract_tick_data(data)
412-
logging.info(f"Tick Data Acknowledgement {data}") # @TODO remove
413415
self.__subscribe_callback(data)
414416
elif(data["t"] == "dk"): # depth data acknowledgment
415417
if(self.__subscribe_callback is not None):
416418
data.pop("t")
417419
data = self.__extract_depth_data(data)
418-
logging.info(f"Depth Data Acknowledgement {data}") # @TODO remove
419420
self.__subscribe_callback(data)
420421
elif(data["t"] == "tf"): # tick data feed
421422
if(self.__subscribe_callback is not None):
422423
data.pop("t")
423424
data = self.__extract_tick_data(data)
424-
logging.info(f"Tick feed {data}") # @TODO remove
425425
self.__subscribe_callback(data)
426426
elif(data["t"] == "df"): # depth data feed
427427
if(self.__subscribe_callback is not None):
428428
data.pop("t")
429429
data = self.__extract_depth_data(data)
430-
logging.info(f"depth feed {data}") # @TODO remove
431430
self.__subscribe_callback(data)
432431

433432
def __on_close_callback(self, *arguments, **keywords):
@@ -483,9 +482,7 @@ def start_websocket(self, subscribe_callback = None,
483482

484483
# Create websocket session
485484
data = {"loginType" : "API"}
486-
r = self.__api_call_helper('createWsSession', Requests.POST, data)
487-
# ws_session_id = r["result"]["wsSess"]
488-
# logging.info(f"ws session id {ws_session_id}")
485+
self.__api_call_helper('createWsSession', Requests.POST, data)
489486

490487
# Create websocket connection
491488
self.__websocket_connected = False
@@ -520,7 +517,7 @@ def get_profile(self):
520517
"bse_com" : "BCO"}
521518
profile = self.__api_call_helper('profile', Requests.GET)
522519
x = profile['exchEnabled'].split("|")
523-
self.__enabled_exchanges = [exch_dt[i] for i in x]
520+
self.__enabled_exchanges = [exch_dt[i] for i in x if i in exch_dt]
524521
return profile
525522

526523
def get_balance(self):
@@ -735,7 +732,7 @@ def subscribe(self, instrument, live_feed_type):
735732
for _instrument in instrument:
736733
if not isinstance(_instrument, Instrument):
737734
raise TypeError("Required parameter instrument is not of type Instrument")
738-
subscribe_string = f"#{_instrument.exchange}|{int(_instrument.token)}"
735+
subscribe_string += f"#{_instrument.exchange}|{int(_instrument.token)}"
739736
self.__subscribers[_instrument] = live_feed_type
740737
else:
741738
if not isinstance(instrument, Instrument):
@@ -813,7 +810,7 @@ def get_instrument_for_fno(self, symbol, expiry_date, is_fut=False, strike=None,
813810
return
814811
matches = []
815812
for i in res:
816-
sp = i.symbol.split(' ')
813+
sp = i.name.split(' ')
817814
if(sp[0] == symbol):
818815
if(i.expiry == expiry_date):
819816
matches.append(i)
@@ -822,7 +819,7 @@ def get_instrument_for_fno(self, symbol, expiry_date, is_fut=False, strike=None,
822819
if('FUT' in i.symbol):
823820
return i
824821
else:
825-
sp = i.symbol.split(' ')
822+
sp = i.name.split(' ')
826823
if((sp[-1] == 'CE') or (sp[-1] == 'PE')): # Only option scrips
827824
if(float(sp[-2]) == float(strike)):
828825
if(is_CE == True):
@@ -912,38 +909,41 @@ def __get_master_contract(self, exchange):
912909
# Write to temp file
913910
with open(tmp_file, 'w') as fo:
914911
fo.write(json.dumps(body))
915-
master_contract_by_token = OrderedDict()
916-
master_contract_by_symbol = OrderedDict()
917-
for sub in body:
918-
if(sub != "contract_date"):
919-
for scrip in body[sub]:
912+
for exch in body:
913+
if(exch != "contract_date"):
914+
for scrip in body[exch]:
920915
# convert token
921-
token = int(scrip['token'])
916+
token = int(scrip["token"])
922917

923918
# convert symbol to upper
924-
symbol = scrip['trading_symbol']
919+
if("trading_symbol" in scrip):
920+
symbol = scrip["trading_symbol"]
921+
else:
922+
symbol = scrip["symbol"]
925923

926924
# convert expiry to none if it's non-existent
927-
if('expiry_date' in scrip):
928-
expiry = datetime.datetime.fromtimestamp(scrip['expiry_date']/1000).date()
925+
if("expiry_date" in scrip):
926+
expiry = datetime.datetime.fromtimestamp(scrip['expiry_date']/1000, tz=pytz.utc).date()
929927
else:
930928
expiry = None
931929

932930
# convert lot size to int
933-
if('lot_size' in scrip):
934-
lot_size = scrip['lot_size']
935-
else:
936-
lot_size = None
931+
lot_size = None
932+
if("lot_size" in scrip):
933+
lot_size = scrip["lot_size"]
937934

938-
# Name & Exchange
939-
name = scrip['formatted_ins_name']
940-
exch = scrip['exch']
941-
935+
# Name
936+
name = None
937+
if("formatted_ins_name" in scrip):
938+
name = scrip["formatted_ins_name"]
939+
942940
instrument = Instrument(exch, token, symbol, name, expiry, lot_size)
943-
master_contract_by_token[token] = instrument
944-
master_contract_by_symbol[symbol] = instrument
945-
self.__master_contracts_by_token[exchange] = master_contract_by_token
946-
self.__master_contracts_by_symbol[exchange] = master_contract_by_symbol
941+
if(exch not in self.__master_contracts_by_token):
942+
self.__master_contracts_by_token[exch] = {}
943+
if(exch not in self.__master_contracts_by_symbol):
944+
self.__master_contracts_by_symbol[exch] = {}
945+
self.__master_contracts_by_token[exch][token] = instrument
946+
self.__master_contracts_by_symbol[exch][symbol] = instrument
947947

948948
def __api_call_helper(self, name, http_method, data=None, params=None):
949949
# helper formats the url and reads error codes nicely
@@ -956,7 +956,6 @@ def __api_call_helper(self, name, http_method, data=None, params=None):
956956
return response.json()
957957

958958
def __api_call(self, url, http_method, data):
959-
#logger.debug('url:: %s http_method:: %s data:: %s headers:: %s', url, http_method, data, headers)
960959
# Update header with Session ID
961960
headers = { "Content-Type" : "application/json",
962961
"Authorization" : f"Bearer {self.__username} {self.__session_id}"}

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
requests
22
pytz
33
websocket_client
4-
4+
cryptography

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
setuptools.setup(
77
name = 'alice_blue',
88
packages=setuptools.find_packages(),
9-
version = '2.0.0',
9+
version = '2.0.1',
1010
include_package_data=True,
1111
description = 'Official Python library for Alice Blue APIs',
1212
long_description=long_description,

0 commit comments

Comments
 (0)