Python开发高频量化策略 速度优化避坑指南

前言

因为目前已经切换成Rust做交易系统了,并且使用我们的交易系统Http下单比Rust的ureq库快1ms,比Python的request库快3ms,所以分享一些我使用Python做高频策略所遇到的坑。

我们目前技术栈:

1.纯Golang交易系统和策略,系统延迟大概在80us左右

2.Rust交易系统加载Python策略,纯Rust系统延迟10us以内,一旦加载Python的策略就会增加50us。

3.Golang开发Web后台后端

为什么做策略要用Python?不纯Rust? 因为很多做策略开发的研究员,学习Rust开发策略,原来Python一周就可以上线的策略,用Rust要至少一个月起步。 而且Python很赚钱的策略,研究员去复现成Rust,结果还不如Python赚钱,因为写法不能完全复制,哈哈哈。

我本身是做黑客和Web全栈+Python爬虫做了好几年后面转量化。 像我这样,技术特别强,策略也特别强,少之又少。 (别问我为什么不继续黑客,我不想出国也不想包吃包住)

1.使用事件驱动而非轮询

一般的研究员,习惯用循环去查询当前最新价格,比如:

ini 复制代码
while 1:
    price = ....
    time.sleep(0.001)

这个速度也不错,但是缺陷很明显,天然就比竞争对手慢1ms,并且大量消耗CPU

正确的做法,建立一个websocket订阅最新价格bbo,并且收到行情就执行一次策略:

scss 复制代码
def trade(price):
    策略code

websocket("BTCUSDT", callback=trade)

2.不用多线程

很多Quant做交易系统和策略开发会习惯启动多线程去做一些事情,但是Python创建多线程所需要的系统耗时平均需要0.1ms!如果波动大,同时创建的多,耗时1ms也会经常出现,所以策略code里面不要用多线程。

测试代码:

scss 复制代码
import threading
import time
def NowTimeMs():
    return int(time.time()*1_000_000)
def worker(t):
    print((NowTimeMs() - t) / 1000, 'ms')
print('TEST')
threads = []
for i in range(100):
    t = threading.Thread(target=worker, args=(NowTimeMs(),))
    #t.setDaemon(True)
    t.start()

测试结果:

3.不用loop.create_task()

这个比多线程还糟糕,行情多,在策略里面多次创建create_task任务,直接1秒之后再执行你的操作。

4.少用字符串 + 号拼接

这个是Python的一个特性,用+号拼接会增加系统延迟,具体原因可以问chatgpt,应该用f"{}",或者format

5.不要用print函数

没想到吧?这个也会增加你的系统耗时,平均一次10us,而且很多哦~ 如果有统计的需求,可以把要打印的内容append到数组里,最后策略运行结束再打印

测试代码:

scss 复制代码
import time
def NowTimeMs():
    return int(time.time()*1_000_000)
for i in range(10):

    t = NowTimeMs()

    for i in range(10):
        print("啦啦啦啦啦啦")

    print((NowTimeMs() - t) / 1000, 'ms')

测试结果:

6.不要用loguru

看过很多人的策略代码,都用loguru去记录日志,执行大概0.27ms一次吧

python 复制代码
from loguru import logger

# 设置异步日志记录到文件
logger.add("multiArbitrage1.log", rotation="10 MB", enqueue=True, backtrace=True, diagnose=True)

import time
def NowTimeMs():
    return int(time.time()*1_000_000)
for i in range(10):

    t = NowTimeMs()

    for i in range(10):
        # print("啦啦啦啦啦啦")
        logger.debug(f'666666666')

    print((NowTimeMs() - t) / 1000, 'ms')

测试结果:

7.用aiohttp

aiohttp天然就比request库快,所以用aiohttp库会更好,然后有websocket的下单api 都用websocket

当然我们也编写了一个用rust封装的http库,python调用去下单比request快个1-2ms

8.最后

事件驱动 + 协程 + aiohttp + Websocket下单

相关推荐
VidDown13 小时前
24fps 转 25fps 不只是改个数字:帧率转换、PAL/NTSC 与音频补偿
python·网络协议·音视频·视频编解码·视频
死磕音视频15 小时前
让小模型做选择题:限定候选项并读取 logits 概率的入门实践(附 Python 代码)
开发语言·python·大模型·文本分类·transformers
凤山老林15 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
pride.li17 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
databook17 小时前
用 Pydantic AI 从零搭建一个能干活的 AI Agent
人工智能·python
康小旺17 小时前
把 markitdown 做成 Windows 桌面版:拖进文档就出 Markdown,附 233MB→64MB 打包裁剪全过程
python
启观川18 小时前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
南笙北梦18 小时前
本地开源大模型多轮对话显存泄漏排查与修复实录
python
小羊没烦恼!18 小时前
如何在IIS6,7中部署ASP.NET网站?
数据库·后端·python·web安全·asp.net
钱栈up19 小时前
扫完 3.5 万文件后,我发现了 521 组散落的同名文件
爬虫·python·数据分析