├── .gitattributes ├── LICENSE ├── README.md ├── bytes_marketdata.py ├── client.py ├── example.py ├── get-pip.py ├── requirements.txt ├── server.py └── ws_client.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Aeron7 Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Deprecated: [Unofficed](https://www.unofficed.com/) is no longer a partner of Aliceblue. We will not provide any support, and no further updates or code maintenance will be done. 2 | 3 | ## This program is written in Flask. 4 | 5 | - Tested with Python 3.6.7 6 | - Recommended Python 3.6 or higher version 7 | - Needs to run with sudo as redirect url is set to run on port 80 8 | 9 | ## Support of Coding: 10 | - Support is limited to the people who are under subbrokership of Unofficed. For any help, raise a ticket at https://forum.unofficed.com/ 11 | - Otherwise, Please contact Aliceblue customer care. It is not possible to reply every Aliceblue customer. 12 | 13 | ## Install packages: 14 | 15 | Download all the files from here and install all required python packages using command 16 | `pip3 install -r requirements.txt` 17 | 18 | ## Usage: 19 | 20 | - Update the variables `client_id` and `client_secret` config in server.py 21 | ``` 22 | # config 23 | client_id = 'your_app_id_here' 24 | client_secret = 'your_app_secret_here' 25 | ``` 26 | - Now run the Flask program using command 27 | `sudo python3 server.py` 28 | 29 | Now the app is ready! Open https://127.0.0.1/getcode/ and get started. 30 | -------------------------------------------------------------------------------- /bytes_marketdata.py: -------------------------------------------------------------------------------- 1 | from ctypes import Structure 2 | import ctypes, struct 3 | from struct import pack_into 4 | 5 | 6 | # class for reading bytes 7 | class CliMarketdataRes(Structure): 8 | _pack_ = 1 9 | _fields_ = [ 10 | ('mode', ctypes.c_int8), 11 | ('exchange_code', ctypes.c_uint8), 12 | ('instrument_token', ctypes.c_uint32), 13 | ('last_traded_price', ctypes.c_uint32), 14 | ('last_traded_time', ctypes.c_uint32), 15 | ('last_traded_quantity', ctypes.c_uint32), 16 | ('trade_volume', ctypes.c_uint32), 17 | ('best_bid_price', ctypes.c_uint32), 18 | ('best_bid_quantity', ctypes.c_uint32), 19 | ('best_ask_price', ctypes.c_uint32), 20 | ('best_ask_quantity', ctypes.c_uint32), 21 | ('total_buy_quantity', ctypes.c_uint64), 22 | ('total_sell_quantity', ctypes.c_uint64), 23 | ('average_trade_price', ctypes.c_uint32), 24 | ('exchange_timestamp', ctypes.c_uint32), 25 | ('open_price', ctypes.c_uint32), 26 | ('high_price', ctypes.c_uint32), 27 | ('low_price', ctypes.c_uint32), 28 | ('close_price', ctypes.c_uint32), 29 | ('yearly_high_price', ctypes.c_uint32), 30 | ('yearly_low_price', ctypes.c_uint32), 31 | ] 32 | size = 86 33 | 34 | def get_CliMarketdataRes_Instruct(self, packet_buffer): 35 | self.mode = struct.unpack('>b', packet_buffer[0:1])[0] 36 | self.exchange_code = struct.unpack('>b', packet_buffer[1:2])[0] 37 | self.instrument_token = struct.unpack('>I', packet_buffer[2:6])[0] 38 | self.last_traded_price = struct.unpack('>I', packet_buffer[6:10])[0] 39 | self.last_traded_time = struct.unpack('>I', packet_buffer[10:14])[0] 40 | self.last_traded_quantity = struct.unpack('>I', packet_buffer[14:18])[0] 41 | self.trade_volume = struct.unpack('>I', packet_buffer[18:22])[0] 42 | self.best_bid_price = struct.unpack('>I', packet_buffer[22:26])[0] 43 | self.best_bid_quantity = struct.unpack('>I', packet_buffer[26:30])[0] 44 | self.best_ask_price = struct.unpack('>I', packet_buffer[30:34])[0] 45 | self.best_ask_quantity = struct.unpack('>I', packet_buffer[34:38])[0] 46 | self.total_buy_quantity = struct.unpack('>Q', packet_buffer[38:46])[0] 47 | self.total_sell_quantity = struct.unpack('>Q', packet_buffer[46:54])[0] 48 | self.average_trade_price = struct.unpack('>I', packet_buffer[54:58])[0] 49 | self.exchange_timestamp = struct.unpack('>I', packet_buffer[58:62])[0] 50 | self.open_price = struct.unpack('>I', packet_buffer[62:66])[0] 51 | self.high_price = struct.unpack('>I', packet_buffer[66:70])[0] 52 | self.low_price = struct.unpack('>I', packet_buffer[70:74])[0] 53 | self.close_price = struct.unpack('>I', packet_buffer[74:78])[0] 54 | self.yearly_high_price = struct.unpack('>I', packet_buffer[78:82])[0] 55 | self.yearly_low_price = struct.unpack('>I', packet_buffer[82:86])[0] 56 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | # config 4 | web_url = 'https://ant.aliceblueonline.com' 5 | 6 | 7 | class Client(object): 8 | base_url = web_url 9 | def __init__(self, access_token): 10 | self.headers = { 11 | 'Accept': 'application/json', 12 | 'Authorization': f'Bearer {access_token}' 13 | } 14 | 15 | def _send_request(self, method='get', endpoint='', params={}): 16 | r = requests.request( 17 | method=method, 18 | headers=self.headers, 19 | url=f'{Client.base_url}{endpoint}', 20 | params=params) 21 | 22 | if r.status_code == 200: 23 | r_json = r.json() 24 | try: 25 | return r_json['data'] 26 | except KeyError as e: 27 | print(e) 28 | return r_json 29 | 30 | print(r) 31 | print(r.json()) 32 | 33 | def fetch_profile(self): 34 | endpoint = '/api/v2/profile' 35 | data = self._send_request(endpoint=endpoint) 36 | return data -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from client import Client 3 | from ws_client import handle_streams 4 | 5 | 6 | # example function to show use case of access token 7 | # it fetches client profile from REST api 8 | # and also logs market data from websockets 9 | def data_from_resource_server(access_token): 10 | user = Client(access_token) 11 | profile = user.fetch_profile() 12 | print('profile', profile) 13 | th = threading.Thread(target=handle_streams, args=(access_token,)) 14 | th.start() 15 | # handle_streams(access_token) 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests_oauthlib==1.2.0 2 | Flask==1.0.2 3 | requests==2.20.0 4 | websocket_client==0.54.0 5 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import threading 3 | from requests_oauthlib import OAuth2Session 4 | from flask import Flask, request, redirect 5 | from client import web_url 6 | from example import data_from_resource_server 7 | 8 | # to make oauth2 work with http 9 | os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' 10 | 11 | 12 | # config 13 | client_id = 'your_app_id_here' 14 | client_secret = 'your_app_secret_here' 15 | redirect_uri = 'http://127.0.0.1/' 16 | authorization_base_url = f'{web_url}/oauth2/auth' 17 | token_url = f'{web_url}/oauth2/token' 18 | scope=['orders'] 19 | # state='https://www.google.com' 20 | 21 | # creating instance of Flask 22 | app = Flask(__name__) 23 | 24 | 25 | # access token 26 | access_token = None 27 | 28 | 29 | @app.route('/getcode') 30 | def get_authorization_url(): 31 | oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope) 32 | authorization_url, _state = oauth.authorization_url(authorization_base_url, access_type="authorization_code") 33 | print('authorization_url') 34 | print(authorization_url) 35 | return redirect(authorization_url) 36 | 37 | 38 | @app.route('/') 39 | def callback(): 40 | global access_token 41 | oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope) 42 | token = oauth.fetch_token(token_url, authorization_response=request.url, client_secret=client_secret) 43 | access_token = token['access_token'] 44 | print('access token is:', access_token) 45 | 46 | ## we will be shutting down the server after getting access_token 47 | ## the thread created here is copied in if __name__ == '__main__' block 48 | ## and will run after closing the server 49 | 50 | # th = threading.Thread(target=data_from_resource_server, args=(access_token,)) 51 | # th.start() 52 | 53 | func = request.environ.get('werkzeug.server.shutdown') 54 | if func: 55 | print('stoping server') 56 | func() 57 | 58 | 59 | return 'see terminal for logs' 60 | 61 | 62 | if __name__ == '__main__': 63 | app.secret_key = 'example' 64 | app.env = 'development' 65 | print() 66 | print('Open this url in browser:', 'http://127.0.0.1/getcode', end='\n\n') 67 | 68 | app.run(host='127.0.0.1', port='80') 69 | 70 | print('server stopped') 71 | 72 | ## got access_token, closed the server, now running ray integration code 73 | if access_token: 74 | th = threading.Thread(target=data_from_resource_server, args=(access_token,)) 75 | th.start() 76 | -------------------------------------------------------------------------------- /ws_client.py: -------------------------------------------------------------------------------- 1 | import websocket 2 | import threading 3 | import json 4 | import time 5 | from bytes_marketdata import CliMarketdataRes 6 | 7 | def heartbeat_thread(clientSocket): 8 | while clientSocket: 9 | send_data = '{"a": "h", "v": [], "m": ""}' 10 | try: 11 | clientSocket.send(send_data) 12 | except Exception as e: 13 | print(e) 14 | print("HEARTBEAT [ERROR]: [BLITZ_HYDRA_STREAM] Connection closed.") 15 | break 16 | print("Sent Heart-Beat to Exchange") 17 | time.sleep(8) 18 | 19 | 20 | def on_message(ws, message): 21 | marketdataPkt = CliMarketdataRes() 22 | 23 | # mode is always 0, so putting check on length also 24 | if marketdataPkt.mode == 0 and len(message) != 86: 25 | print('heartbeat') 26 | return 27 | 28 | marketdataPkt.get_CliMarketdataRes_Instruct(message) 29 | print('\n') 30 | print('exchange_code', marketdataPkt.exchange_code) 31 | print('instrument_token', marketdataPkt.instrument_token) 32 | print('last_traded_price', marketdataPkt.last_traded_price) 33 | print('last_traded_time', marketdataPkt.last_traded_time) 34 | print('last_traded_quantity', marketdataPkt.last_traded_quantity) 35 | print('trade_volume', marketdataPkt.trade_volume) 36 | print('best_bid_price', marketdataPkt.best_bid_price) 37 | print('best_bid_quantity', marketdataPkt.best_bid_quantity) 38 | print('best_ask_price', marketdataPkt.best_ask_price) 39 | print('best_ask_quantity', marketdataPkt.best_ask_quantity) 40 | print('total_buy_quantity', marketdataPkt.total_buy_quantity) 41 | print('total_sell_quantity', marketdataPkt.total_sell_quantity) 42 | print('average_trade_price', marketdataPkt.average_trade_price) 43 | print('exchange_timestamp', marketdataPkt.exchange_timestamp) 44 | print('open_price', marketdataPkt.open_price) 45 | print('high_price', marketdataPkt.high_price) 46 | print('low_price', marketdataPkt.low_price) 47 | print('close_price', marketdataPkt.close_price) 48 | print('yearly_high_price', marketdataPkt.yearly_high_price) 49 | print('yearly_low_price', marketdataPkt.yearly_low_price) 50 | 51 | 52 | def on_error(ws, error): 53 | print(error) 54 | 55 | def on_close(ws): 56 | print("### closed ###") 57 | 58 | def on_open(ws): 59 | hbThread = threading.Thread(target=heartbeat_thread, args=(ws,)) 60 | hbThread.start() 61 | sub_packet = { 62 | "a": "subscribe", 63 | "v": [[1,3045], [1, 22]], 64 | "m": "marketdata" 65 | } 66 | 67 | ws.send(json.dumps(sub_packet)) 68 | 69 | # for pair in sub_packet["v"]: 70 | # temp_packet = { 71 | # "a": "subscribe", 72 | # "v": [pair], 73 | # "m": "marketdata" 74 | # } 75 | # ws.send(json.dumps(temp_packet)) 76 | 77 | 78 | def handle_streams(access_token): 79 | websocket.enableTrace(False) 80 | ws = websocket.WebSocketApp(f"wss://ant.aliceblueonline.com/hydrasocket/v2/websocket?access_token={'access_token'}", 81 | on_message = on_message, 82 | on_error = on_error, 83 | on_close = on_close) 84 | ws.on_open = on_open 85 | ws.run_forever() 86 | --------------------------------------------------------------------------------