├── .gitignore ├── JSON-RPC_HTTP └── JSON-RPC_HTTP-Example.py ├── JSON-RPC_Websockets ├── JSON-RPC_Websockets-AuthenticationExample.py ├── JSON-RPC_Websockets-OrderBookSubscribe.py ├── JSON-RPC_Websockets-OrderBookSubscribeAllInstruments.py └── requirements.txt ├── README.md └── User-Credentials └── UserCredentials.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Local 7 | .history/ 8 | .vscode/ 9 | 10 | # Scratchfiles and WIP 11 | JSON-RPC_Websockets/scratchfile.py 12 | JSON-RPC_Websockets/scratchfile2.py 13 | JSON-RPC_HTTP/ 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | pip-wheel-metadata/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | *.py,cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Django stuff: 68 | *.log 69 | local_settings.py 70 | db.sqlite3 71 | db.sqlite3-journal 72 | 73 | # Flask stuff: 74 | instance/ 75 | .webassets-cache 76 | 77 | # Scrapy stuff: 78 | .scrapy 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # PyBuilder 84 | target/ 85 | 86 | # Jupyter Notebook 87 | .ipynb_checkpoints 88 | 89 | # IPython 90 | profile_default/ 91 | ipython_config.py 92 | 93 | # pyenv 94 | .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 104 | __pypackages__/ 105 | 106 | # Celery stuff 107 | celerybeat-schedule 108 | celerybeat.pid 109 | 110 | # SageMath parsed files 111 | *.sage.py 112 | 113 | # Environments 114 | .env 115 | .venv 116 | env/ 117 | venv/ 118 | ENV/ 119 | env.bak/ 120 | venv.bak/ 121 | 122 | # Spyder project settings 123 | .spyderproject 124 | .spyproject 125 | 126 | # Rope project settings 127 | .ropeproject 128 | 129 | # mkdocs documentation 130 | /site 131 | 132 | # mypy 133 | .mypy_cache/ 134 | .dmypy.json 135 | dmypy.json 136 | 137 | # Pyre type checker 138 | .pyre/ 139 | -------------------------------------------------------------------------------- /JSON-RPC_HTTP/JSON-RPC_HTTP-Example.py: -------------------------------------------------------------------------------- 1 | # built ins 2 | import sys 3 | import hashlib 4 | import hmac 5 | import time 6 | import requests 7 | 8 | # imported from project 9 | sys.path.append('/home/elliotp/dev/deribit/API-Guide/Deribit-API_Authentication-Examples/User-Credentials') # noqa: E501 10 | from UserCredentials import Client_Id, Client_Secret # noqa: E402 E501 11 | 12 | class DeribitExchangeVersion: 13 | def __init__(self, exchange_version): 14 | self.exchange_version = exchange_version 15 | self.main() 16 | 17 | def main(self): 18 | if self.exchange_version == 'live': 19 | self.exchange_version = 'https://www.deribit.com' 20 | elif self.exchange_version == 'testnet': 21 | self.exchange_version = 'https://www.deribit.com' 22 | else: 23 | print('Invalid Exchange Version, please try "live" or "testnet"') 24 | 25 | return self.exchange_version 26 | 27 | 28 | class UserHTTPEngine: 29 | def __init__(self, Client_Id, Client_Secret, exchange_version): 30 | self.client_id = Client_Id 31 | self.client_secret = Client_Secret 32 | self.exchange_version = exchange_version 33 | self.main() 34 | 35 | def main(self): 36 | 37 | host = "https://test.deribit.com" 38 | # #host = "https://www.deribit.com" 39 | 40 | # # replace these values with your client-id/secret 41 | Client_Id = self.client_id 42 | Client_Secret = self.client_secret 43 | 44 | tstamp = str(int(time.time())*1000) 45 | 46 | #nonce must be be a random string from security reasons, here for simplcity 47 | nonce = "1234567890" 48 | 49 | #Does not need ot be the same as the body string dict. The string dict "body" has priority/preference 50 | uri = "/api/v2/private/sell" 51 | 52 | # this is body of the POST request, it is JSON-RPC string, the fields are described here https://docs.deribit.com/#private-buy 53 | body = "{\"jsonrpc\": \"2.0\",\"id\": \"6091\",\"method\": \"private/sell\",\"params\": {\"instrument_name\": \"BTC-PERPETUAL\",\"amount\": 20,\"price\": \"10000\",\"label\": \"test_order_1560335086587\"}}" 54 | 55 | # here we prepare signature for the request, this several lines prepare the hash of the signature 56 | request_data = "POST" + "\n" + uri + "\n" + body + "\n" 57 | base_signature_string = tstamp + "\n" + nonce + "\n" + request_data 58 | byte_key = Client_Secret.encode() 59 | message = base_signature_string.encode() 60 | sig = hmac.new(byte_key, message, hashlib.sha256).hexdigest() 61 | 62 | # #the signature is send in the Authorization header of the HTTPS Request, according to described here https://docs.deribit.com/#authentication 63 | 64 | authorization = "deri-hmac-sha256 id="+Client_Id+",ts="+tstamp+",sig="+sig+",nonce="+nonce 65 | headers = {"Authorization": authorization} 66 | 67 | print("authorization: " + authorization) 68 | print("POST request to " + (host+uri+"?")) 69 | 70 | # here we send HTTPS POST request 71 | 72 | json = requests.post((host+uri+"?"), headers=headers, data=body) 73 | 74 | # # te reply is printed 75 | print(json.content) 76 | 77 | 78 | if __name__ == "__main__": 79 | # Your "exchange_version" variable must be 'live' or 'testnet'. 80 | exchange_version = 'testnet' 81 | 82 | # host = "https://test.deribit.com" 83 | # #host = "https://www.deribit.com" 84 | 85 | # # replace these values with your client-id/secret 86 | # Client_Id = Client_Id 87 | # Client_Secret = Client_Secret 88 | 89 | # tstamp = str(int(time.time())*1000) 90 | 91 | # #nonce must be be a random string from security reasons, here for simplcity 92 | # nonce = "1234567890" 93 | 94 | # #Does not need ot be the same as the body string dict. The string dict "body" has priority/preference 95 | # uri = "/api/v2/private/sell" 96 | 97 | # # this is body of the POST request, it is JSON-RPC string, the fields are described here https://docs.deribit.com/#private-buy 98 | # body = "{\"jsonrpc\": \"2.0\",\"id\": \"6091\",\"method\": \"private/sell\",\"params\": {\"instrument_name\": \"BTC-PERPETUAL\",\"amount\": 20,\"price\": \"10000\",\"label\": \"test_order_1560335086587\"}}" 99 | 100 | # # here we prepare signature for the request, this several lines prepare the hash of the signature 101 | # request_data = "POST" + "\n" + uri + "\n" + body + "\n" 102 | # base_signature_string = tstamp + "\n" + nonce + "\n" + request_data 103 | # byte_key = Client_Secret.encode() 104 | # message = base_signature_string.encode() 105 | # sig = hmac.new(byte_key, message, hashlib.sha256).hexdigest() 106 | 107 | # #the signature is send in the Authorization header of the HTTPS Request, according to described here https://docs.deribit.com/#authentication 108 | 109 | # authorization = "deri-hmac-sha256 id="+Client_Id+",ts="+tstamp+",sig="+sig+",nonce="+nonce 110 | # headers = {"Authorization": authorization} 111 | 112 | UserHTTPEngine(Client_Id=Client_Id, 113 | Client_Secret=Client_Secret, 114 | exchange_version=DeribitExchangeVersion(exchange_version=exchange_version).exchange_version) 115 | 116 | # print("authorization: " + authorization) 117 | # print("POST request to " + (host+uri+"?")) 118 | 119 | # # here we send HTTPS POST request 120 | 121 | # json = requests.post((host+uri+"?"), headers=headers, data=body) 122 | 123 | # # te reply is printed 124 | # print(json.content) -------------------------------------------------------------------------------- /JSON-RPC_Websockets/JSON-RPC_Websockets-AuthenticationExample.py: -------------------------------------------------------------------------------- 1 | # built ins 2 | import sys 3 | import time 4 | from datetime import datetime, timedelta 5 | import hashlib 6 | import hmac 7 | import json 8 | import secrets 9 | 10 | # installed 11 | import websocket 12 | 13 | # imported from project 14 | sys.path.append('/home/elliotp/dev/deribit/API-Guide/Deribit-API_Authentication-Examples/User-Credentials') # noqa: E501 15 | from UserCredentials import Client_Id, Client_Secret # noqa: E402 E501 16 | 17 | class DeribitAPIAccessScope: 18 | def __init__(self, scope): 19 | self.scope = scope 20 | self.main() 21 | 22 | def main(self): 23 | if self.scope == 'read-only': 24 | self.account_scope = "read" 25 | self.trade_scope = "read" 26 | self.wallet_scope = "read" 27 | self.block_scope = "read" 28 | self.custody = "read_write" # Must be 'read_write' for the moment given the present API implementation # noqa: E501 29 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( 30 | self.account_scope, # noqa: E501 31 | self.trade_scope, # noqa: E501 32 | self.wallet_scope, # noqa: E501 33 | self.block_scope, # noqa: E501 34 | self.custody # noqa: E501 35 | ) # noqa: E501 36 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 37 | elif self.scope == 'read-write': 38 | self.account_scope = "read_write" 39 | self.trade_scope = "read_write" 40 | self.wallet_scope = "read_write" 41 | self.block_scope = "read_write" 42 | self.custody = "read_write" 43 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( # noqa: E501 44 | self.account_scope, # noqa: E501 45 | self.trade_scope, # noqa: E501 46 | self.wallet_scope, # noqa: E501 47 | self.block_scope, # noqa: E501 48 | self.custody # noqa: E501 49 | ) # noqa: E501 50 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 51 | else: 52 | self.designated_scope = self.scope 53 | print('Invalid Scope Definition: "{}" is not an accepted scope definition. Please try "read-only" or "read-write".'.format(self.scope)) # noqa: E501 54 | print('You will receive inconsistent scope definitions.') 55 | 56 | return self.designated_scope 57 | 58 | 59 | class DeribitExchangeVersion: 60 | def __init__(self, exchange_version): 61 | self.exchange_version = exchange_version 62 | self.main() 63 | 64 | def main(self): 65 | if self.exchange_version == 'live': 66 | self.exchange_version = 'wss://www.deribit.com/ws/api/v2/' 67 | elif self.exchange_version == 'testnet': 68 | self.exchange_version = 'wss://test.deribit.com/ws/api/v2/' 69 | else: 70 | print('Invalid Exchange Version, please try "live" or "testnet"') 71 | 72 | return self.exchange_version 73 | 74 | 75 | class UserWebsocketEngine: 76 | def __init__(self, client_Id, scope, exchange_version): 77 | self.client_id = client_Id 78 | self.scope = scope 79 | self.exchange_version = exchange_version 80 | self.expiry_time = None 81 | self.seconds_delay = 10 82 | self.refresh_token = '' 83 | self.authentication_refresh_flag = 0 84 | self.heartbeat_requested_flag = 0 85 | self.heartbeat_set_flag = 0 86 | self.main() 87 | 88 | def main(self): 89 | def on_message(ws, message): 90 | message = json.loads(message) 91 | print("Message Received at: " + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 92 | # print(message) 93 | 94 | if 'error' in message.keys(): 95 | error_message = message['error']['message'] 96 | error_code = message['error']['code'] 97 | print('You have received an ERROR MESSAGE: {} with the ERROR CODE: {}'.format(error_message, error_code)) # noqa: E501 98 | 99 | # display successful authentication messages as well as stores your refresh_token # noqa: E501 100 | if 'result' in message.keys(): 101 | if type(message['result']) == list: 102 | for a in range(0, len(message['result'])): 103 | if all(column_name in message['result'][a].keys() for column_name in ['total_profit_loss', 'size_currency', 'size', 'settlement_price', # noqa: E501 104 | 'realized_profit_loss', 'realized_funding', 'open_orders_margin', # noqa: E501 105 | 'mark_price', 'maintenance_margin', 'leverage', 'kind', 'instrument_name', # noqa: E501 106 | 'initial_margin', 'index_price', 'floating_profit_loss', # noqa: E501 107 | 'estimated_liquidation_price', 'direction', 'delta', 'average_price']): # noqa: E501 108 | print('Present Positions at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 109 | print(message) 110 | if [*message['result']] == ['token_type', 'scope', 'refresh_token', 'expires_in', 'access_token']: # noqa: E501 111 | if self.authentication_refresh_flag == 1: 112 | print('Successfully Refreshed your Authentication at: ' + 113 | str(datetime.now().time().strftime('%H:%M:%S'))) 114 | else: 115 | print('Authentication Success at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 116 | self.refresh_token = message['result']['refresh_token'] 117 | if message['testnet']: 118 | # The testnet returns an extremely high value for expires_in and is best to use # noqa: E501 119 | # 600 in place as so the functionality is as similar as the Live exchange # noqa: E501 120 | expires_in = 600 121 | else: 122 | expires_in = message['result']['expires_in'] 123 | 124 | self.expiry_time = (datetime.now() + timedelta(seconds=expires_in)) # noqa: E501 125 | print('Authentication Expires at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 126 | 127 | # uses your refresh_token to refresh your authentication 128 | if datetime.now() > self.expiry_time and self.authentication_refresh_flag == 0: # noqa: E501 129 | self.authentication_refresh_flag = 1 130 | print('Refreshing your Authentication at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 131 | ws_data = { 132 | "jsonrpc": "2.0", 133 | "id": 1, 134 | "method": "public/auth", 135 | "params": { 136 | "grant_type": "refresh_token", 137 | "refresh_token": self.refresh_token 138 | } 139 | } 140 | ws.send(json.dumps(ws_data)) 141 | 142 | # heartbeat set success check and heartbeat response 143 | if 'params' in message.keys(): 144 | if message['params']['type'] == 'heartbeat' and self.heartbeat_set_flag == 0: # noqa: E501 145 | self.heartbeat_set_flag = 1 146 | print('Heartbeat Successfully Initiated at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 147 | 148 | if message['params']['type'] == 'test_request': 149 | ws_data = { 150 | "jsonrpc": "2.0", 151 | "id": 1, 152 | "method": "public/test", 153 | "params": { 154 | } 155 | } 156 | ws.send(json.dumps(ws_data)) 157 | # get_positions API request 158 | ws_data = {"jsonrpc": "2.0", "id": 1, 159 | "method": "private/get_positions", 160 | "params": { 161 | "currency": "BTC", 162 | "kind": "future"}} 163 | ws.send(json.dumps(ws_data)) 164 | 165 | # natural delay 166 | print('Pending {} Second Introduced Delay at: '.format(self.seconds_delay) + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 167 | time.sleep(self.seconds_delay) 168 | 169 | def on_error(ws, error): 170 | if type(error == ""): # noqa: E501 171 | print('') 172 | print('ERROR MESSAGE:'+'Testnet is likely down for maintenance or your connection is unstable unless you cancelled this yourself.') # noqa: E501 173 | print('') 174 | else: 175 | print(error) 176 | 177 | def on_close(ws): 178 | print('CONNECTION CLOSED AT: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 179 | 180 | def on_open(ws): 181 | ws_data = { 182 | "jsonrpc": "2.0", 183 | "id": 1, 184 | "method": "public/auth", 185 | "params": { 186 | "grant_type": "client_signature", 187 | "client_id": self.client_id, 188 | "timestamp": tstamp, 189 | "nonce": nonce, 190 | "scope": self.scope, 191 | "signature": signature, 192 | "data": data} 193 | } 194 | ws.send(json.dumps(ws_data)) 195 | 196 | # initiating heartbeat 197 | if self.heartbeat_set_flag == 0 and self.heartbeat_requested_flag == 0: # noqa: E501 198 | self.heartbeat_requested_flag = 1 199 | print('Heartbeat Requested at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 200 | ws_data = { 201 | "jsonrpc": "2.0", 202 | "id": 1, 203 | "method": "public/set_heartbeat", 204 | "params": { 205 | "interval": 10 206 | } 207 | } 208 | ws.send(json.dumps(ws_data)) 209 | 210 | # Detailed Logging 211 | # websocket.enableTrace(True) 212 | 213 | # Initialize Websocket App 214 | ws = websocket.WebSocketApp(self.exchange_version, 215 | on_message=on_message, 216 | on_error=on_error, 217 | on_close=on_close) 218 | ws.on_open = on_open 219 | 220 | ws.run_forever() 221 | 222 | 223 | if __name__ == "__main__": 224 | # Your "scope" variable must be 'read-only' or 'read-write'. 225 | scope = 'read-only' 226 | # Your "exchange_version" variable must be 'live' or 'testnet'. 227 | exchange_version = 'testnet' 228 | 229 | # Client Signature Authentication 230 | tstamp = str(int(time.time()) * 1000) 231 | data = '' 232 | nonce = secrets.token_urlsafe(10) 233 | base_signature_string = tstamp + "\n" + nonce + "\n" + data 234 | byte_key = Client_Secret.encode() 235 | message = base_signature_string.encode() 236 | signature = hmac.new(byte_key, message, hashlib.sha256).hexdigest() 237 | 238 | # Your Trading Engine 239 | UserWebsocketEngine(client_Id=Client_Id, 240 | scope=DeribitAPIAccessScope(scope).designated_scope, 241 | exchange_version=DeribitExchangeVersion(exchange_version).exchange_version) # noqa: E501 242 | -------------------------------------------------------------------------------- /JSON-RPC_Websockets/JSON-RPC_Websockets-OrderBookSubscribe.py: -------------------------------------------------------------------------------- 1 | # built ins 2 | import time 3 | from datetime import datetime, timedelta 4 | import hashlib 5 | import hmac 6 | import json 7 | import secrets 8 | 9 | # installed 10 | import websocket 11 | 12 | class DeribitAPIAccessScope: 13 | def __init__(self, scope): 14 | self.scope = scope 15 | self.main() 16 | 17 | def main(self): 18 | if self.scope == 'read-only': 19 | self.account_scope = "read" 20 | self.trade_scope = "read" 21 | self.wallet_scope = "read" 22 | self.block_scope = "read" 23 | self.custody = "read_write" # Must be 'read_write' for the moment given the present API implementation # noqa: E501 24 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( 25 | self.account_scope, # noqa: E501 26 | self.trade_scope, # noqa: E501 27 | self.wallet_scope, # noqa: E501 28 | self.block_scope, # noqa: E501 29 | self.custody # noqa: E501 30 | ) # noqa: E501 31 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 32 | elif self.scope == 'read-write': 33 | self.account_scope = "read_write" 34 | self.trade_scope = "read_write" 35 | self.wallet_scope = "read_write" 36 | self.block_scope = "read_write" 37 | self.custody = "read_write" 38 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( # noqa: E501 39 | self.account_scope, # noqa: E501 40 | self.trade_scope, # noqa: E501 41 | self.wallet_scope, # noqa: E501 42 | self.block_scope, # noqa: E501 43 | self.custody # noqa: E501 44 | ) # noqa: E501 45 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 46 | else: 47 | self.designated_scope = self.scope 48 | print('Invalid Scope Definition: "{}" is not an accepted scope definition. Please try "read-only" or "read-write".'.format(self.scope)) # noqa: E501 49 | print('You will receive inconsistent scope definitions.') 50 | 51 | return self.designated_scope 52 | 53 | 54 | class DeribitExchangeVersion: 55 | def __init__(self, exchange_version): 56 | self.exchange_version = exchange_version 57 | self.main() 58 | 59 | def main(self): 60 | if self.exchange_version == 'live': 61 | self.exchange_version = 'wss://www.deribit.com/ws/api/v2/' 62 | elif self.exchange_version == 'testnet': 63 | self.exchange_version = 'wss://test.deribit.com/ws/api/v2/' 64 | else: 65 | print('Invalid Exchange Version, please try "live" or "testnet"') 66 | 67 | return self.exchange_version 68 | 69 | 70 | class UserWebsocketEngine: 71 | def __init__(self, client_Id, scope, exchange_version): 72 | self.client_id = client_Id 73 | self.scope = scope 74 | self.exchange_version = exchange_version 75 | self.expiry_time = None 76 | self.refresh_token = '' 77 | self.authentication_refresh_flag = 0 78 | self.heartbeat_requested_flag = 0 79 | self.heartbeat_set_flag = 0 80 | self.main() 81 | 82 | def main(self): 83 | def on_message(ws, message): 84 | message = json.loads(message) 85 | # print("Message Received at: " + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 86 | # print(message) 87 | # print(message.keys()) 88 | 89 | if 'error' in message.keys(): 90 | error_message = message['error']['message'] 91 | error_code = message['error']['code'] 92 | print('You have received an ERROR MESSAGE: {} with the ERROR CODE: {}'.format(error_message, error_code)) # noqa: E501 93 | 94 | # display successful authentication messages as well as stores your refresh_token # noqa: E501 95 | if 'result' in message.keys(): 96 | if [*message['result']] == ['token_type', 'scope', 'refresh_token', 'expires_in', 'access_token']: # noqa: E501 97 | if self.authentication_refresh_flag == 1: 98 | print('Successfully Refreshed your Authentication at: ' + 99 | str(datetime.now().time().strftime('%H:%M:%S'))) 100 | else: 101 | print('Authentication Success at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 102 | self.refresh_token = message['result']['refresh_token'] 103 | if message['testnet']: 104 | # The testnet returns an extremely high value for expires_in and is best to use # noqa: E501 105 | # 600 in place as so the functionality is as similar as the Live exchange # noqa: E501 106 | expires_in = 600 107 | else: 108 | expires_in = message['result']['expires_in'] 109 | 110 | self.expiry_time = (datetime.now() + timedelta(seconds=expires_in)) # noqa: E501 111 | print('Authentication Expires at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 112 | 113 | # uses your refresh_token to refresh your authentication 114 | if datetime.now() > self.expiry_time and self.authentication_refresh_flag == 0: # noqa: E501 115 | self.authentication_refresh_flag = 1 116 | print('Refreshing your Authentication at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 117 | ws_data = { 118 | "jsonrpc": "2.0", 119 | "id": 1, 120 | "method": "public/auth", 121 | "params": { 122 | "grant_type": "refresh_token", 123 | "refresh_token": self.refresh_token 124 | } 125 | } 126 | ws.send(json.dumps(ws_data)) 127 | 128 | # heartbeat set success check and heartbeat response 129 | if 'params' in message.keys() and message['params']['type'] == 'heartbeat' and self.heartbeat_set_flag == 0: # noqa: E501 130 | self.heartbeat_set_flag = 1 131 | print('Heartbeat Successfully Initiated at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 132 | 133 | # respond to a test request 134 | if 'params' in message.keys() and message['params']['type'] == 'test_request': # noqa: E501 135 | ws_data = { 136 | "jsonrpc": "2.0", 137 | "id": 1, 138 | "method": "public/test", 139 | "params": { 140 | } 141 | } 142 | ws.send(json.dumps(ws_data)) 143 | 144 | # print of successful order book subscription 145 | if message['id'] == 42: 146 | print('Successfully Subscribed to Order Book Data at: {}'.format(datetime.utcnow())) 147 | for channel in range(0, len(message['result'])): 148 | message_split = message['result'][channel].split('.') 149 | print('Subscription Channel: {}'.format(message_split[1])) 150 | print('Price Groupings: {}'.format(message_split[2])) 151 | print('Price Levels/Depth: {}'.format(message_split[3])) 152 | print('Interval: {}'.format(message_split[4])) 153 | 154 | def on_error(ws, error): 155 | if type(error == ""): # noqa: E501 156 | print('') 157 | print('ERROR MESSAGE:'+'Testnet is likely down for maintenance or your connection is unstable unless you cancelled this yourself.') # noqa: E501 158 | print('') 159 | else: 160 | print(error) 161 | 162 | def on_close(ws): 163 | print('CONNECTION CLOSED AT: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 164 | 165 | def on_open(ws): 166 | # Initial Authentication 167 | ws_data = { 168 | "jsonrpc": "2.0", 169 | "id": 1, 170 | "method": "public/auth", 171 | "params": { 172 | "grant_type": "client_signature", 173 | "client_id": self.client_id, 174 | "timestamp": tstamp, 175 | "nonce": nonce, 176 | "scope": self.scope, 177 | "signature": signature, 178 | "data": data} 179 | } 180 | ws.send(json.dumps(ws_data)) 181 | 182 | # Initiating Heartbeat 183 | if self.heartbeat_set_flag == 0 and self.heartbeat_requested_flag == 0: # noqa: E501 184 | self.heartbeat_requested_flag = 1 185 | print('Heartbeat Requested at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 186 | ws_data = { 187 | "jsonrpc": "2.0", 188 | "id": 1, 189 | "method": "public/set_heartbeat", 190 | "params": { 191 | "interval": 10 192 | } 193 | } 194 | ws.send(json.dumps(ws_data)) 195 | 196 | # Subscribing to the Order Book 197 | ws_data = {"jsonrpc": "2.0", 198 | "method": "public/subscribe", 199 | "id": 42, 200 | "params": { 201 | "channels": ["book.BTC-PERPETUAL.none.10.100ms", "book.ETH-PERPETUAL.none.10.100ms"]} 202 | } 203 | ws.send(json.dumps(ws_data)) 204 | 205 | # Detailed Logging 206 | # websocket.enableTrace(True) 207 | 208 | # Initialize Websocket App 209 | ws = websocket.WebSocketApp(self.exchange_version, 210 | on_message=on_message, 211 | on_error=on_error, 212 | on_close=on_close) 213 | ws.on_open = on_open 214 | 215 | ws.run_forever() 216 | 217 | 218 | if __name__ == "__main__": 219 | # Local Testing 220 | Client_Id = "XBagIoFw" 221 | Client_Secret = "BtsbXxYRbpct7ZB44BEidFPlhICBoDAOQadZ31QD_mY" 222 | 223 | # Your "scope" variable must be 'read-only' or 'read-write'. 224 | scope = 'read-only' 225 | # Your "exchange_version" variable must be 'live' or 'testnet'. 226 | exchange_version = 'testnet' 227 | 228 | # Client Signature Authentication 229 | tstamp = str(int(time.time()) * 1000) 230 | data = '' 231 | nonce = secrets.token_urlsafe(10) 232 | base_signature_string = tstamp + "\n" + nonce + "\n" + data 233 | byte_key = Client_Secret.encode() 234 | message = base_signature_string.encode() 235 | signature = hmac.new(byte_key, message, hashlib.sha256).hexdigest() 236 | 237 | # Your Trading Engine 238 | UserWebsocketEngine(client_Id=Client_Id, 239 | scope=DeribitAPIAccessScope(scope).designated_scope, 240 | exchange_version=DeribitExchangeVersion(exchange_version).exchange_version) # noqa: E501 241 | -------------------------------------------------------------------------------- /JSON-RPC_Websockets/JSON-RPC_Websockets-OrderBookSubscribeAllInstruments.py: -------------------------------------------------------------------------------- 1 | # built ins 2 | import time 3 | from datetime import datetime, timedelta 4 | import hashlib 5 | import hmac 6 | import json 7 | import secrets 8 | 9 | # installed 10 | import websocket 11 | 12 | class DeribitAPIAccessScope: 13 | def __init__(self, scope): 14 | self.scope = scope 15 | self.main() 16 | 17 | def main(self): 18 | if self.scope == 'read-only': 19 | self.account_scope = "read" 20 | self.trade_scope = "read" 21 | self.wallet_scope = "read" 22 | self.block_scope = "read" 23 | self.custody = "read_write" # Must be 'read_write' for the moment given the present API implementation # noqa: E501 24 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( 25 | self.account_scope, # noqa: E501 26 | self.trade_scope, # noqa: E501 27 | self.wallet_scope, # noqa: E501 28 | self.block_scope, # noqa: E501 29 | self.custody # noqa: E501 30 | ) # noqa: E501 31 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 32 | elif self.scope == 'read-write': 33 | self.account_scope = "read_write" 34 | self.trade_scope = "read_write" 35 | self.wallet_scope = "read_write" 36 | self.block_scope = "read_write" 37 | self.custody = "read_write" 38 | self.designated_scope = "account:{} trade:{} wallet:{} block_trade:{} custody:{}".format( # noqa: E501 39 | self.account_scope, # noqa: E501 40 | self.trade_scope, # noqa: E501 41 | self.wallet_scope, # noqa: E501 42 | self.block_scope, # noqa: E501 43 | self.custody # noqa: E501 44 | ) # noqa: E501 45 | print('Scope Definition: You are using "{}" access'.format(self.scope)) # noqa: E501 46 | else: 47 | self.designated_scope = self.scope 48 | print('Invalid Scope Definition: "{}" is not an accepted scope definition. Please try "read-only" or "read-write".'.format(self.scope)) # noqa: E501 49 | print('You will receive inconsistent scope definitions.') 50 | 51 | return self.designated_scope 52 | 53 | 54 | class DeribitExchangeVersion: 55 | def __init__(self, exchange_version): 56 | self.exchange_version = exchange_version 57 | self.main() 58 | 59 | def main(self): 60 | if self.exchange_version == 'live': 61 | self.exchange_version = 'wss://www.deribit.com/ws/api/v2/' 62 | elif self.exchange_version == 'testnet': 63 | self.exchange_version = 'wss://test.deribit.com/ws/api/v2/' 64 | else: 65 | print('Invalid Exchange Version, please try "live" or "testnet"') 66 | 67 | return self.exchange_version 68 | 69 | 70 | class UserWebsocketEngine: 71 | def __init__(self, client_Id, scope, exchange_version): 72 | self.client_id = client_Id 73 | self.scope = scope 74 | self.exchange_version = exchange_version 75 | self.expiry_time = None 76 | self.refresh_token = '' 77 | self.authentication_refresh_flag = 0 78 | self.heartbeat_requested_flag = 0 79 | self.heartbeat_set_flag = 0 80 | self.instruments_list = [] 81 | self.pulled_instruments_flag = 0 82 | self.pulled_instruments_datetime = datetime.utcnow() + timedelta(hours=12) 83 | self.instruments_subscribe_flag = 0 84 | self.main() 85 | 86 | def main(self): 87 | def on_message(ws, message): 88 | message = json.loads(message) 89 | # print("Message Received at: " + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 90 | # print(message) 91 | # print(message.keys()) 92 | 93 | if 'error' in message.keys(): 94 | error_message = message['error']['message'] 95 | error_code = message['error']['code'] 96 | print('You have received an ERROR MESSAGE: {} with the ERROR CODE: {}'.format(error_message, error_code)) # noqa: E501 97 | 98 | # display successful authentication messages as well as stores your refresh_token # noqa: E501 99 | if 'result' in message.keys(): 100 | if [*message['result']] == ['token_type', 'scope', 'refresh_token', 'expires_in', 'access_token']: # noqa: E501 101 | if self.authentication_refresh_flag == 1: 102 | print('Successfully Refreshed your Authentication at: ' + 103 | str(datetime.now().time().strftime('%H:%M:%S'))) 104 | else: 105 | print('Authentication Success at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 106 | self.refresh_token = message['result']['refresh_token'] 107 | if message['testnet']: 108 | # The testnet returns an extremely high value for expires_in and is best to use # noqa: E501 109 | # 600 in place as so the functionality is as similar as the Live exchange # noqa: E501 110 | expires_in = 600 111 | else: 112 | expires_in = message['result']['expires_in'] 113 | 114 | self.expiry_time = (datetime.now() + timedelta(seconds=expires_in)) # noqa: E501 115 | print('Authentication Expires at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 116 | 117 | # uses your refresh_token to refresh your authentication 118 | if datetime.now() > self.expiry_time and self.authentication_refresh_flag == 0: # noqa: E501 119 | self.authentication_refresh_flag = 1 120 | print('Refreshing your Authentication at: ' + str(self.expiry_time.strftime('%H:%M:%S'))) # noqa: E501 121 | ws_data = { 122 | "jsonrpc": "2.0", 123 | "id": 1, 124 | "method": "public/auth", 125 | "params": { 126 | "grant_type": "refresh_token", 127 | "refresh_token": self.refresh_token 128 | } 129 | } 130 | ws.send(json.dumps(ws_data)) 131 | 132 | # heartbeat set success check and heartbeat response 133 | if 'params' in message.keys() and message['params']['type'] == 'heartbeat' and self.heartbeat_set_flag == 0: # noqa: E501 134 | self.heartbeat_set_flag = 1 135 | print('Heartbeat Successfully Initiated at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 136 | 137 | # respond to a test request 138 | if 'params' in message.keys() and message['params']['type'] == 'test_request': # noqa: E501 139 | ws_data = { 140 | "jsonrpc": "2.0", 141 | "id": 1, 142 | "method": "public/test", 143 | "params": { 144 | } 145 | } 146 | ws.send(json.dumps(ws_data)) 147 | 148 | # print of successful order book subscription 149 | if message['id'] == 42: 150 | print('Successfully Subscribed to Order Book Data at: {}'.format(datetime.utcnow())) 151 | for channel in range(0, len(message['result'])): 152 | message_split = message['result'][channel].split('.') 153 | # print('Subscription Channel: {}'.format(message_split[1])) 154 | # print('Price Groupings: {}'.format(message_split[2])) 155 | # print('Price Levels/Depth: {}'.format(message_split[3])) 156 | # print('Interval: {}'.format(message_split[4])) 157 | 158 | # construct channels to subscribe to from pull instruments 159 | if message['id'] == 7617: 160 | self.pulled_instruments_flag = 1 161 | for instrument in range(0,len(message['result'])): 162 | subscribe_channel = "book."+str(message['result'][instrument]['instrument_name'])+".none.10.100ms" 163 | self.instruments_list.append(subscribe_channel) 164 | 165 | # subscribe to all available instruments 166 | if self.instruments_subscribe_flag == 0 and self.pulled_instruments_flag == 1: 167 | # Subscribing to the Order Book 168 | ws_data = {"jsonrpc": "2.0", 169 | "method": "public/subscribe", 170 | "id": 42, 171 | "params": { 172 | "channels": self.instruments_list 173 | } 174 | } 175 | ws.send(json.dumps(ws_data)) 176 | 177 | self.instruments_subscribe_flag = 1 178 | 179 | # repull all instruments and resubscribe 180 | if datetime.utcnow() > self.pulled_instruments_datetime: 181 | self.pulled_instruments_flag = 0 182 | self.instruments_subscribe_flag = 0 183 | self.instruments_list = [] 184 | 185 | # incrementing the /get_instruments datetime 186 | self.pulled_instruments_datetime = datetime.utcnow() + timedelta(hours=12) 187 | 188 | # Sending the request 189 | ws_data ={ 190 | "jsonrpc": "2.0", 191 | "id": 7617, 192 | "method": "public/get_instruments", 193 | "params": { 194 | "currency": "BTC", 195 | "kind": "option", 196 | "expired": False 197 | } 198 | } 199 | ws.send(json.dumps(ws_data)) 200 | 201 | 202 | 203 | def on_error(ws, error): 204 | if type(error == ""): # noqa: E501 205 | print('') 206 | print('ERROR MESSAGE:'+'Testnet is likely down for maintenance or your connection is unstable unless you cancelled this yourself.') # noqa: E501 207 | print('') 208 | else: 209 | print(error) 210 | 211 | def on_close(ws): 212 | print('CONNECTION CLOSED AT: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 213 | 214 | def on_open(ws): 215 | # Initial Authentication 216 | ws_data = { 217 | "jsonrpc": "2.0", 218 | "id": 1, 219 | "method": "public/auth", 220 | "params": { 221 | "grant_type": "client_signature", 222 | "client_id": self.client_id, 223 | "timestamp": tstamp, 224 | "nonce": nonce, 225 | "scope": self.scope, 226 | "signature": signature, 227 | "data": data} 228 | } 229 | ws.send(json.dumps(ws_data)) 230 | 231 | # Initiating Heartbeat 232 | if self.heartbeat_set_flag == 0 and self.heartbeat_requested_flag == 0: # noqa: E501 233 | self.heartbeat_requested_flag = 1 234 | print('Heartbeat Requested at: ' + str(datetime.now().time().strftime('%H:%M:%S'))) # noqa: E501 235 | ws_data = { 236 | "jsonrpc": "2.0", 237 | "id": 1, 238 | "method": "public/set_heartbeat", 239 | "params": { 240 | "interval": 10 241 | } 242 | } 243 | ws.send(json.dumps(ws_data)) 244 | 245 | # Pull all available BTC Option Instrument Names 246 | ws_data ={ 247 | "jsonrpc": "2.0", 248 | "id": 7617, 249 | "method": "public/get_instruments", 250 | "params": { 251 | "currency": "BTC", 252 | "kind": "option", 253 | "expired": False 254 | } 255 | } 256 | ws.send(json.dumps(ws_data)) 257 | 258 | # Detailed Logging 259 | # websocket.enableTrace(True) 260 | 261 | # Initialize Websocket App 262 | ws = websocket.WebSocketApp(self.exchange_version, 263 | on_message=on_message, 264 | on_error=on_error, 265 | on_close=on_close) 266 | ws.on_open = on_open 267 | 268 | ws.run_forever() 269 | 270 | 271 | if __name__ == "__main__": 272 | # Local Testing 273 | Client_Id = "XBagIoFw" 274 | Client_Secret = "BtsbXxYRbpct7ZB44BEidFPlhICBoDAOQadZ31QD_mY" 275 | Client_Id = "Ac5nOoVh" 276 | Client_Secret = "hrOkDCReLk00b70fBgZHolzbakzF5iGg7I4_E9hlYIc" 277 | 278 | # Your "scope" variable must be 'read-only' or 'read-write'. 279 | scope = 'read-only' 280 | # Your "exchange_version" variable must be 'live' or 'testnet'. 281 | exchange_version = 'testnet' 282 | 283 | # Client Signature Authentication 284 | tstamp = str(int(time.time()) * 1000) 285 | data = '' 286 | nonce = secrets.token_urlsafe(10) 287 | base_signature_string = tstamp + "\n" + nonce + "\n" + data 288 | byte_key = Client_Secret.encode() 289 | message = base_signature_string.encode() 290 | signature = hmac.new(byte_key, message, hashlib.sha256).hexdigest() 291 | 292 | # Your Trading Engine 293 | UserWebsocketEngine(client_Id=Client_Id, 294 | scope=DeribitAPIAccessScope(scope).designated_scope, 295 | exchange_version=DeribitExchangeVersion(exchange_version).exchange_version) # noqa: E501 296 | -------------------------------------------------------------------------------- /JSON-RPC_Websockets/requirements.txt: -------------------------------------------------------------------------------- 1 | websocket_client >= 0.57.0 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deribit API - Authentication Examples 2 | Live Exchange: www.deribit.com 3 | 4 | Testnet: test.deribit.com 5 | 6 | A series of examples in Python across the different provided API Protocols offered by Deribit.com 7 | 8 | JSON-RPC over Websockets: 9 | --- 10 | 11 | - Authentication with /public/auth: https://docs.deribit.com/#public-auth 12 | - Heartbeat set with /public/set_heartbeat: https://docs.deribit.com/#public-set_heartbeat 13 | - refresh_token used to refresh application's authentication: https://docs.deribit.com/#public-auth 14 | 15 | - Subscribe to Order Book Data: https://docs.deribit.com/#book-instrument_name-group-depth-interval 16 | -------------------------------------------------------------------------------- /User-Credentials/UserCredentials.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file holds your API Credentials specific to your Deribit or Testnet account (or Subaccount). 3 | 4 | # IMPORTANT: www.deribit.com and test.deribit.com do not share Account Credentials or API Keys. 5 | # You must activate an API key in your Account Settings to create a "Client Id" and a "Client Signature" 6 | 7 | Relevant Links: 8 | Deribit live exchange: www.deribit.com 9 | Testnet exchange = test.deribit.com 10 | """ 11 | 12 | #This is your "Client Id" and must be a string type. 13 | Client_Id = "mmMgHOM6" 14 | #This is your "Client Secret" and must be a string type. 15 | Client_Secret = "lVpOFyThTkjDxCjidd38i8a7HBiAuHy26iM2D0gU5ng" 16 | --------------------------------------------------------------------------------