You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用asyncio获取多交易所余额遇事件循环及协程问题求助

异步获取多交易所余额的问题解析与修复

嘿,我来帮你拆解下你遇到的异步编程问题,这些都是异步入门时很常见的坑,慢慢理顺就好啦~

核心错误原因分析

1. RuntimeError: This event loop is already running

这个错误是因为你当前的运行环境(比如Jupyter Notebook、IPython,或者某些GUI框架)已经自动启动了一个事件循环,而asyncio规定同一个线程里不能同时运行多个事件循环:

  • 当你调用loop.run_until_complete()时,它会尝试启动一个新的循环,但发现已有循环在运行,就抛出了这个错误;
  • 同样,asyncio.run()内部会创建一个新的事件循环,所以也会和已有的循环冲突,导致报错。

2. RuntimeWarning: coroutine 'xxx.fetch_balance' was never awaited

这个警告有两个可能的触发点:

  • 你测试时直接写了print(exchanges[0].fetchBalance()),这里fetchBalance()是协程函数,调用它只会返回一个协程对象,必须用await才能执行它,所以会触发警告;
  • 当事件循环冲突的错误发生时,你的get_balance协程可能没被正确调度执行,导致内部的await exchange.fetchBalance()也没真正被处理,间接触发了警告。

为什么loop.create_task(main())能正常运行?

asyncio.get_running_loop()会获取当前已经在运行的事件循环,然后create_task()是把你的协程添加到这个已有的循环里排队执行,不会尝试创建新循环,所以避开了冲突问题。

针对不同场景的解决方案

场景1:在已有事件循环的环境(Jupyter/IPython等)

直接利用已有的循环,用以下方式提交协程:

async def main():
    exchanges = [exchange1, exchange2, ...]
    results = await get_balances(exchanges)
    print(results)

# 获取当前运行的循环,添加任务
loop = asyncio.get_running_loop()
task = loop.create_task(main())
# 如果需要等待结果,可以用await(在交互式环境里直接写await task即可)
# await task

或者在Jupyter这类支持直接await的交互式环境里,直接执行:

exchanges = [exchange1, exchange2, ...]
results = await get_balances(exchanges)
print(results)

场景2:普通Python脚本(无预运行的事件循环)

用asyncio.run()作为程序入口,确保整个异步逻辑都在一个主协程里:

import asyncio

# 你的get_balance和get_balances函数保持不变
async def get_balance(exchange, symbol='BTC/USD'):
    freeBalance = await exchange.fetchBalance()
    symbol1_balance = freeBalance[symbol.split('/')[0]]
    symbol2_balance = freeBalance[symbol.split('/')[1]]
    return symbol1_balance, symbol2_balance

async def get_balances(exchanges):
    futures = [get_balance(exchange) for exchange in exchanges]
    balances = await asyncio.gather(*futures)
    return balances

async def main():
    # 初始化你的交易所实例,比如用ccxt的话:
    # import ccxt
    # exchanges = [ccxt.binance({'enableRateLimit': True}), ccxt.bitfinex({'enableRateLimit': True})]
    exchanges = [exchange1, exchange2, ...]
    results = await get_balances(exchanges)
    print(results)

if __name__ == "__main__":
    asyncio.run(main())

额外注意点

  • 你提到print(exchanges[0].fetchBalance())输出协程对象,这是正常的,协程函数必须通过await或者添加到事件循环才能执行;
  • 注意ccxt库的fetchBalance()返回结构:大部分交易所返回的是嵌套字典,比如{'BTC': {'free': 0.01, 'used': 0.0}, 'USD': {'free': 100, 'used': 0}},所以你可能需要调整get_balance里的取值逻辑,比如取freeBalance[symbol.split('/')[0]]['free']来获取可用余额;
  • 异步和多线程的核心区别:多线程是操作系统切换线程,异步是单线程内通过await主动交出控制权,让其他协程运行,所以所有异步函数都需要被await或者通过任务调度才能执行。

内容的提问来源于stack exchange,提问作者Nicolas Rey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 07:12:32