最近在做一个量化交易的小项目玩,在对接交易平台API接口的时候,正常通过requests库出去的请求,没什么问题,看了下源码,是通过https请求的。
但是,我想通过websockets连接wss://连接时,出现了问题,报如下错误。
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)
虚拟环境版本3.12,SSL部分的配置如下(单有这个配置还不行)。
import ssl
import certifi
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
# ...
我猜测,这个配置在非虚拟环境中,应该是可以正常使用https://和wss://协议的,但是虚拟环境因为找不到证书路径的问题,会报unable to get local issuer certificate的错误。
踩了半天坑,最后在stackoverflow上,看到一位友人的配置,解决了我的问题。(因为是事后写的文章,已经很难找到原链接了。
Anyway,解决方案就是,做一个链接,终端输入以下指令。
ln -s /etc/ssl/* "/Library/Frameworks/Python.framework/Versions/3.12/etc/openssl"
不多解释了,让GPT回答:
该命令
ln -s /etc/ssl/* "/Library/Frameworks/Python.framework/Versions/3.12/etc/openssl"
在 Unix 或 Linux 系统(包括 macOS)中使用,并且执行以下操作:
ln -s
: 这是创建软链接(符号链接)的命令。软链接类似于 Windows 中的快捷方式,它是指向另一个文件或目录的引用。
/etc/ssl/*
: 这是源路径,*
表示选择/etc/ssl
目录下的所有文件和目录。
"/Library/Frameworks/Python.framework/Versions/3.12/etc/openssl"
: 这是目标路径,即您要创建软链接的地方。这个路径指向特定版本(在这个例子中是 3.12 版本)的 Python 安装的openssl
目录。
以上,针对Apple Silicon, X86版本的MacOS Python可能安装位置不同,自己找到相关目录。
最后,贴上websockets库的使用示例,这个案例还挺好的,涉及到asycio的异步处理,虽然刚开始理解起来优点晦涩,但是搞懂了协程,就又进步了一个台阶。
from AccountInfo import getAccountInfo#自己的库
import sqlite3
from TradeConfig import * #自己的库
from utils import * #自己的库
import asyncio
import websockets
import json
import ssl
import certifi
import time
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(certifi.where())
account, wssUrl = getAccountInfo(MODE)
accountAPI = "XXX"
marketDataAPI = "XXXX"
db = sqlite3.connect("Trade.db")
cursor = db.cursor()
URL = wssUrl
subscription = "xxxxxx"
async def websocket_client():
while True:
try:
# 连接到WebSocket服务器
async with websockets.connect(URL) as websocket:
# 这里处理你的消息逻辑
await websocket.send(json.dumps(subscription))
while True:
response = await websocket.recv()
response = json.loads(response)
if "data" in response:
data = response["data"][0]
last_price = convertToInteger(data["last"])
ask_price = convertToInteger(data["askPx"])
bid_price = convertToInteger(data["bidPx"])
ts = int(data["ts"])
cursor.execute(
f"INSERT INTO market_price (inst_id, lastPx, askPx, bidPx, ts) VALUES ('{INSTID}', {last_price}, {ask_price}, {bid_price}, {ts})"
)
db.commit()
print(f"[{datetime.now()}]最新成交价:{last_price},卖一价:{ask_price},买一价:{bid_price}")
else:
print(response)
except (websockets.ConnectionClosed, OSError) as e:
print(f"WebSocket connection failed: {e}, retrying in 5 seconds...")
# 等待一段时间后再次尝试连接
time.sleep(5)
# 运行客户端
asyncio.get_event_loop().run_until_complete(websocket_client())
以上自己记录,下次碰到时,可以有地方找了。
这是我的第一篇文章,后面有些踩坑和新知识,会继续分享。