为啥我的 websocket 请求只有在用 OOP 编写时才“未经授权”,而在仅使用函数编写时同样可以完美运行?

Posted

技术标签:

【中文标题】为啥我的 websocket 请求只有在用 OOP 编写时才“未经授权”,而在仅使用函数编写时同样可以完美运行?【英文标题】:Why is my websocket request "unauthorized" only when written in OOP, when the same works perfectly when written with just functions?为什么我的 websocket 请求只有在用 OOP 编写时才“未经授权”,而在仅使用函数编写时同样可以完美运行? 【发布时间】:2019-11-14 19:17:37 【问题描述】:

我正在编写一个 Python 程序来实现一些交易自动化。我使用的 API 来自 Deribit,它的首选传输机制是 Websocket。我是 Python 的 websockets asyncio 模块的新手。

这是我首先编写的代码,用于验证我的客户,然后发送单独的私人消息以从帐户获取订单头寸,仅使用函数编写,没有类:

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())

它工作得很好。这是我的结果:

"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

我决定用OOP的方式重写它,这是我创建的类(文件名为“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)

这是我访问该类的主文件以及我发出所有请求的位置:

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)

这次第一个身份验证请求正在运行,我也能够提取访问令牌,但第二个 private/get_position 消息无论出于何种原因返回一个 unauthorized 错误。

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

我已经花费了几个小时,而且我在 OOP 版本中所做的事情似乎与我在原始版本中所做的完全一样。我对 OOP 及其概念(例如继承)的熟悉程度有限,所以我想知道我在这里缺少什么,以及为什么我的代码在 OOP 版本中不起作用,尽管遵循与中相同的确切工作流程原版。

这里是 Deribit API 的文档:https://docs.deribit.com/v2/?python#json-rpc

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

在主文件中添加params_auth 下的范围有效:

params_auth = 
    "grant_type": "client_credentials",
    "client_id": CL_ID,
    "client_secret": CL_SECRET,
    "scope": "session:test"

【讨论】:

以上是关于为啥我的 websocket 请求只有在用 OOP 编写时才“未经授权”,而在仅使用函数编写时同样可以完美运行?的主要内容,如果未能解决你的问题,请参考以下文章

WebSocket 是啥原理?为啥可以实现持久连接

websocket为啥只需一次握手

为啥客户端不接受我的 WebSocket 响应握手?

OOP 三大特征之多态(Polymorphism)

为啥没有 Sec-WebSocket-Key1 的 WebSocket 不安全?

崩溃:[UIPageControl] 页面越界。请求 0 但页面控件只有 0 个页面:为啥?