0

I'm writing a Python program that does some trading automation. The API I work with is from Deribit, whose preferred transport mechanism is Websocket. I'm a complete newbie to Python's websockets and asyncio modules.

Here's the code I first wrote for authenticating my client and then sending a separate private message to get an order position from the account, written only with functions and no classes:

import asyncio
import websockets
import json

CL_ID = 'qxv0EeAu'
CL_SECRET = 't24F49ocH1_qFawiKnEyqlWF5D-haABb31O8xCQhySg'
REQ_URL = 'wss://test.deribit.com/ws/api/v2'

acc_token = ''

msg = {
    "jsonrpc": "2.0",
    "id": 1,
    "params": {}
}

async def auth_api():

    global msg
    global acc_token 
    msg["method"] = "public/auth"
    msg["params"] = {
        "grant_type": "client_credentials",
        "client_id": CL_ID,
        "client_secret": CL_SECRET,
        "scope": "session:test"
    }

    async with websockets.connect(REQ_URL) as websocket:
        await websocket.send(json.dumps(msg))
        while websocket.open:
            response = await websocket.recv()
            response_json = json.loads(response)
            acc_token = response_json["result"]["access_token"]
            return

async def get_position(websocket, instrument):
    global msg
    global acc_token
    msg["id"] += 1
    msg["method"] = "private/get_position"
    msg["params"] = {
        "access_token": acc_token,
        "instrument_name": instrument
    }
    await websocket.send(json.dumps(msg))
    while websocket.open:
        response = await websocket.recv()
        return response

async def main():
    global msg
    await auth_api()
    async with websockets.connect(REQ_URL) as websocket:
        response = await get_position(websocket, "BTC-PERPETUAL")
        print(response)


asyncio.get_event_loop().run_until_complete(main())

It works perfectly fine. Here's my result:

{"jsonrpc":"2.0","id":2,"result":{"total_profit_loss":0.000209124,"size_currency":-0.017402402,"size":-150.0,"settlement_price":8649.9,"realized_profit_loss":2.67e-7,"open_orders_margin":0.0,"mark_price":8619.5,"maintenance_margin":0.000100079,"leverage":100,"kind":"future","instrument_name":"BTC-PERPETUAL","initial_margin":0.000174039,"index_price":8619.45,"floating_profit_loss":0.000061161,"estimated_liquidation_price":-14.95,"direction":"sell","delta":-0.017402402,"average_price":8724.34},"usIn":1573756522511975,"usOut":1573756522512240,"usDiff":265,"testnet":true}

I decided to rewrite it the OOP way, and here's the class I created (the file is named "Call_Deribit"):

import asyncio, websockets, json

class WSClient():
    def __init__(self, key=None, secret=None, url=None):
        self.api_key = key
        self.api_secret = secret
        self.msg = {
            "jsonrpc": "2.0",
            "id": 0
        }
        if url:
            self.host = url
        else:
            self.host = 'wss://test.deribit.com/ws/api/v2'

    async def call_api(self, msg):   
        async with websockets.connect(self.host) as websocket:
            print("Connected to URL:", self.host)
            try:
                await websocket.send(msg)
                while websocket.open:
                    response = await websocket.recv()
                    response_json = json.loads(response)
                    return response_json
            except Exception as e:
                return e

    def request(self, method, params, session=None):
        msg = self.msg
        msg["id"] += 1
        msg["method"] = method
        msg["params"] = params
        if session != None:
            msg["params"]["scope": "session:{}".format(session)]
        return asyncio.get_event_loop().run_until_complete(self.call_api(json.dumps(msg)))

    def get_order_book(self, instrument):
        method = "public/get_order_book"
        params = {
            "instrument_name": instrument
        }
        return self.request(method, params)

And here's the main file I'm accessing the class from and where I make all the requests:

import json, asyncio, websockets
from Call_Deribit import WSClient

CL_ID = 'qxv0EeAu'
CL_SECRET = 't24F49ocH1_qFawiKnEyqlWF5D-haABb31O8xCQhySg'
REQ_URL = 'wss://test.deribit.com/ws/api/v2'

method_auth = "public/auth"
params_auth = {
    "grant_type": "client_credentials",
    "client_id": CL_ID,
    "client_secret": CL_SECRET
}

main_client = WSClient(key=CL_ID, secret=CL_SECRET, url=REQ_URL)
auth_response = main_client.request(method_auth, params_auth)
acc_token = auth_response["result"]["access_token"]

method_pos = "private/get_position"
params_pos = {
    "access_token": acc_token,
    "instrument_name": "BTC-PERPETUAL"
}

position =  main_client.request(method_pos, params_pos)
print(position)

The first request for authentication is working this time, and I'm able to extract the access token as well, but the second private/get_position message is, for whatever reason, returning an unauthorized error.

{'jsonrpc': '2.0', 'id': 1, 'error': {'message': 'unauthorized', 'code': 13009}, 'testnet': True, 'usIn': 1573756936534405, 'usOut': 1573756936534629, 'usDiff': 224}

I've spent hours on it, and I seem to be doing exactly the same thing in the OOP version as I did on the original one. My familiarity with OOP and its concepts (such as inheritance) is limited, so I'd like to know what I'm missing here, and why my code isn't working in the OOP version, despite following the same exact workflow as in the original version.

Here's the documentation for the Deribit API: https://docs.deribit.com/v2/?python#json-rpc

Any help would be greatly appreciated.

1 Answers1

2

Adding the scope under params_auth in the main file works:

params_auth = {
    "grant_type": "client_credentials",
    "client_id": CL_ID,
    "client_secret": CL_SECRET,
    "scope": "session:test"
}
Waqar UlHaq
  • 6,144
  • 2
  • 34
  • 42
tnrdd
  • 21
  • 4