CCXT-07-错误处理

1. 错误处理概述 - try/catch

ccxt采用各种语言中原生的异常机制进行错误处理。 要处理错误,你需要使用try代码块来保护调用ccxt统一API的代码,然后使用catch代码块捕捉异常。示例代码如下。 JavaScript:

// try to call a unified method
try {
    const response = await exchange.fetchTicker ('ETH/BTC')
    console.log (response)
} catch (e) {
    // if the exception is thrown, it is "caught" and can be handled here
    // the handling reaction depends on the type of the exception
    // and on the purpose or business logic of your application
    if (e instanceof ccxt.NetworkError) {
        console.log (exchange.id, 'fetchTicker failed due to a network error:', e.message)
        // retry or whatever
        // ...
    } else if (e instanceof ccxt.ExchangeError) {
        console.log (exchange.id, 'fetchTicker failed due to exchange error:', e.message)
        // retry or whatever
        // ...
    } else {
        console.log (exchange.id, 'fetchTicker failed with:', e.message)
        // retry or whatever
        // ...
    }
}

Python:

try:
    response = await exchange.fetch_order_book('ETH/BTC')
    print(response)
except ccxt.NetworkError as e:
    print(exchange.id, 'fetch_order_book failed due to a network error:', str(e))
    # retry or whatever
    # ...
except ccxt.ExchangeError as e:
    print(exchange.id, 'fetch_order_book failed due to exchange error:', str(e))
    # retry or whatever
    # ...
except Exception as e:
    print(exchange.id, 'fetch_order_book failed with:', str(e))
    # retry or whatever
    # ...

PHP:

// try to call a unified method
try {
    $response = $exchange->fetch_trades('ETH/BTC');
    print_r($response);
} catch (\ccxt\NetworkError $e) {
    echo $exchange->id . ' fetch_trades failed due to a network error: ' . $e->getMessage () . "\n";
    // retry or whatever
    // ...
} catch (\ccxt\ExchangeError $e) {
    echo $exchange->id . ' fetch_trades failed due to exchange error: ' . $e->getMessage () . "\n";
    // retry or whatever
    // ...
} catch (Exception $e) {
    echo $exchange->id . ' fetch_trades failed with: ' . $e->getMessage () . "\n";
    // retry or whatever
    // ...
}

2. 异常类的体系

ccxt中所有的异常都派生自BaseError基类,其定义如下: JavaScript:

class BaseError extends Error {
    constructor () {
        super ()
        // a workaround to make `instanceof BaseError` work in ES5
        this.constructor = BaseError
        this.__proto__   = BaseError.prototype
    }
}

Python:

class BaseError (Exception):
    pass

PHP:

class BaseError extends \Exception {}

下面是ccxt异常类的继承体系:

+ BaseError
|
+---+ ExchangeError
|   |
|   +---+ AuthenticationError
|   |   |
|   |   +---+ PermissionDenied
|   |   |
|   |   +---+ AccountSuspended
|   |
|   +---+ ArgumentsRequired
|   |
|   +---+ BadRequest
|   |
|   +---+ BadResponse
|   |   |
|   |   +---+ NullResponse
|   |
|   +---+ InsufficientFunds
|   |
|   +---+ InvalidAddress
|   |   |
|   |   +---+ AddressPending
|   |
|   +---+ InvalidOrder
|   |   |
|   |   +---+ OrderNotFound
|   |   |
|   |   +---+ OrderNotCached
|   |   |
|   |   +---+ CancelPending
|   |   |
|   |   +---+ OrderImmediatelyFillable
|   |   |
|   |   +---+ OrderNotFillable
|   |   |
|   |   +---+ DuplicateOrderId
|   |
|   +---+ NotSupported
|
+---+ NetworkError (recoverable)
    |
    +---+ DDoSProtection
    |
    +---+ ExchangeNotAvailable
    |
    +---+ InvalidNonce
    |
    +---+ RequestTimeout

BaseError类是各种错误的一般性描述,包括可用性错误、请求/响应错误等。 开发者至少应该捕捉这个异常,如果不需要区分具体是什么错误的话。 在错误体系中有两个子树,都派生自BaseError:

  • NetworkError
  • ExchangeError

NetworkError表示不严重的错误,某种意义上说并不是真正的错误,更可能是临时性的不可用情况,可能原因包括交易所维护、DDoS保护和临时性访问阻断。 相比之下,ExchangeError是严重的错误 – 如果捕捉到这个错误,那么你使用相同的输入应该都会得到同样的错误。 这两族错误的区别在于NetworkError是可恢复的,而ExchangeError是不可恢复的。

3. 交易所异常 - ExchangeError

当交易所服务器返回的JSON响应中包含了错误信息时,ccxt就会抛出这个异常。可能的原因包括:

  • 访问端结点被交易所关闭
  • 交易所未找到指定的交易对符号
  • 请求的参数缺失
  • 参数格式不正确
  • 交易所响应含义不明确

其他从ExchangeError派生的异常包括:

  • NotSupported:如果交易所的API不支持所访问的端结点,就会抛出这个异常
  • AuthenticationError:如果API需要身份验证而请求中没有提供或者提供的不正确,就会抛出这个异常
  • PermissionDenied:如果请求中指定的api key没有足够的权限,就会抛出这个异常
  • InsufficientFunds:如果账户余额不足以执行当前请求的操作,例如委托下单,就会抛出这个异常
  • InvalidAddress:如果请求中的地址格式不正确,就会抛出这个异常
  • InvalidOrder:这是统一API中order系列方法异常类的基类
  • OrderNotFound:试图查询或取消不存在的委托单时,就会抛出这个异常

4. 网络异常 - NetworkError

所有网络相关的错误通常是可恢复的,网络故障、流量阻塞、服务器不可用这些通常都是时间相关的,稍后重新请求通常就能解决问题。

4.1 DDoS保护异常 - DDoSProtection

当有以下情况之一发生时,就会抛出这个异常:

  • 当Cloudflare或Incapsula限流时
  • 当交易所限流时

除了默认的错误处理,ccxt库会使用以下关键字搜索交易所的响应内容:

  • cloudflare
  • incapsula
  • overload
  • ddos

4.2 请求超时异常 - RequestTimeout

当与交易所的连接失败或没有在指定时间内收到交易所响应的完整数据时, 就会抛出RequestTimeout异常。 因此建议采用以下方式处理这一类的异常:

  • 对于查询请求,只需要重新尝试调用即可
  • 对于cancelOrder请求,要求用户进行二次尝试。如果没有进行二次尝试而是立即调用了fetchOrder, fetchOrders, fetchOpenOrders 或 fetchClosedOrders, 那么可能导致.orders缓存不同步。二次尝试调用cancelOrder可能返回以下结果之一:
    • 成功完成,表示委托单已经正确地取消了
    • 抛出OrderNotFound异常,表示委托单要么已经在上次请求时取消, 要么已经在两次请求的间隔执行(完成或成交)。这是需要调用fetchOrder来 正确地更新缓存
  • 如果createOrder请求抛出RequestTimeout异常,开发者应当:
    • 使用fetchOrders, fetchOpenOrders, fetchClosedOrders检查上个请求是否成功下单 并更新orders缓存。
    • 如果委托单不是敞口状态,那么开发者需要调用fetchBalance检查账户余额是否变化。注意fetchBlanace依靠orders缓存进行余额推理,因此只能在更新缓存后进行调用!

4.3 交易所不可用异常 - ExchangeNotAvailable

如果在响应中检测到如下任何关键字,ccxt库会抛出ExchangeNotAvailable异常:

  • offline
  • unavailable
  • busy
  • retry
  • wait
  • maintain
  • maintenance
  • maintenancing

4.4 无效Nonce异常 - InvalidNonce

当你使用的Nonce比之前的请求中的nonce还要小的时候,ccxt就会抛出InvalidNoce异常。 在以下情况中会抛出这一类异常:

  • 你没有进行请求限流,或者发送太多请求
  • 你的API key没有刷新,可能在其他软件或脚本中使用了同样的api key
  • 在多个交易所实例中使用相同的api密钥对
  • 系统时钟没有同步。