贡献给CCXT库#

如何提交一个问题#

如果你想提交一个问题,并希望快速解决,请遵循以下清单:

  • 阅读手册,尤其是仔细阅读以下部分:

  • 阅读故障排除部分,并按照故障排除步骤操作。

  • 阅读常见问题以了解最常见的问题。

  • 阅读你所使用的交易所的API文档

  • 首先搜索类似的问题,以避免重复。

  • 如果你的问题是独特的,除了基本的故障描述之外,还需要提供以下内容 必填项

    • 在调用函数或方法之前,设置 exchange.verbose = true 属性给交易所实例

    • 不要发布代码或错误的截图,请以纯文本形式提交代码和输出!

    • 使用三个反引号(`)将代码和输出包围起来:GOOD

    • 不要将反引号符号(`)与引号符号(‘)混淆:’’’BAD’’’

    • 不要将单个反引号与三个反引号混淆:BAD

    • 粘贴你遇到困难的完整代码段,避免一行代码

    • 粘贴完整的冗长输出,请勿包含你的密钥

    • 冗长输出应包括与交易所的请求和响应(不仅仅是错误的调用堆栈)

    • 将你使用的语言及其版本写出来

    • 写出ccxt库的版本

    • 它是哪个交易所

    • 正在尝试调用的方法是什么

报告安全漏洞和关键问题#

如果你发现了一个安全问题或重大漏洞,并且在公开报告可能会带来风险,请随时发送消息给我们,发送到info@ccxt.trade

如何贡献代码#

  • 确保你的代码是统一的!

    ↑ 这是最重要的规则!!!- 请不要在拉取请求中提交以下文件:

    • /build/*(这些是自动生成的)

    • /js/*(这些是从TypeScript版本编译而来的)

    • /php/*(除了基础类)

    • /python/*(除了基础类)

    • /ccxt.js

    • /README.md(交易所列表是自动生成的)

    • /package.json

    • /package.lock

    • /wiki/*(除非是真正的编辑,交易所列表是自动生成的)

    • /dist/ccxt.browser.js(这也是自动进行Browserify处理的)

    这些文件是自动生成的(下文有解释)并且会在构建时被覆盖。请不要提交它们,以免使已经非常庞大的代码库更加臃肿。在提交交易所的实现时,通常只需提交一个源文件。

  • 请按原子方式进行编辑,每个交易所提交一个拉取请求,不要混在一起

  • 确保你的代码通过运行 npm run build 的语法检查

待处理任务#

下面是我们希望在库中首先实现并完全统一的功能列表。其中大多数任务已经在进行中,已经为一些交易所实施,但并不是所有交易所都实施了:

  • 杠杆交易

  • 借贷

  • 衍生品(期货、期权)

  • 主账户/子账户

  • 条件订单(止损、止盈)

  • 子账户和主账户之间的transfer

  • fetchTransfer

  • fetchTransfers

  • fetchLedger

  • fetchPositions

  • closePosition

  • closePositions

如果你想通过提交部分实现来做贡献,请确保查找库中已经实现的部分的示例,并且复制采用的做法。

如果你的建议、建议或改进与以上任务列表无关,请在提交之前确保它符合以下要求:

  1. 绝大多数ccxt用户都真正需要它

  2. 被设计为通用解决方案,而不是针对特定需求进行硬编码

  3. 以一种通用的方式兼容所有交易所(不是特定于某个交易所)

  4. 可移植(在所有支持的语言中可用)

  5. 健壮

  6. 明确地表达其功能

  7. 不会破坏任何功能(如果你更改一个方法,请确保调用该方法的所有其他方法不会被破坏)

以下是对贡献ccxt库代码库的一些规则。## 你需要什么

如果你不打算开发CCXT并向CCXT库贡献代码,那么你不需要Docker镜像,也不需要CCXT代码库。如果你只想在项目中使用CCXT,只需像在手册中所述,将其作为一个常规包安装到项目文件夹中:

使用Docker#

最简单的方式是使用Docker在一个隔离的构建和测试环境中运行,并安装所有的依赖项:```shell docker-compose run –rm ccxt


这将构建一个容器并打开一个 shell,在其中 `npm run build` 和 `node run-tests` 命令应该可以直接运行。

CCXT 文件夹映射到容器内部,除了 `node_modules` 文件夹——容器将有自己的临时副本——这样就不会破坏你本地安装的模块。这意味着你可以使用你喜欢的编辑器在本地机器上编辑源代码,并在运行中的容器中构建和测试它们。

这样,你可以保持构建工具和流程的隔离,而不必通过手动安装所有这些依赖项来解决痛苦的过程。

### 没有 Docker

#### 依赖项

- Git
- [Node.js](https://nodejs.org/en/download/) 8+
- [Python](https://www.python.org/downloads/) 3.5.3+
  - requests (`pip install requests`)
  - [aiohttp](https://docs.aiohttp.org/) (`pip install aiohttp`)
  - [tox](https://tox.readthedocs.io)
    - 通过 pip 安装:`pip install tox`
    - 使用 [brew](https://brew.sh) 安装的 MacOS:`brew install tox`
    - Ubuntu Linux:`apt-get install tox`
- [PHP](https://secure.php.net/downloads.php) 5.3+ 并安装并启用以下扩展:
  - cURL
  - iconv
  - mbstring
  - PCRE
  - bcmath (php<7.1)

#### 构建步骤

```shell
git clone https://github.com/ccxt/ccxt.git
cd ccxt
``````shell
npm install
npm run build

你需要知道的内容#

仓库结构#

仓库的内容结构如下:

/                          # 根目录,也是 Node.js 的 npm 模块/包文件夹
/.babelrc                  # 用于创建库的 ES5 版本的 Babel 配置
/.eslintrc                 # 代码检查工具
/.gitattributes            # 包含用于语言检测的 Linguist 设置
/.gitignore                # 忽略此文件
/.npmignore                # 从 NPM 包中要排除的文件
/.travis.yml               # 用于 travis-ci(持续集成)的 YAML 配置文件
/CONTRIBUTING.md           # 此文件
/LICENSE.txt               # MIT 许可证
/README.md                 # 主要用于 GitHub、npmjs.com、npms.io、yarn 等站点的 Markdown 文件
/build/                    # 构建脚本
/build/export-exchanges.js # 用于在构建过程中在文档中创建交易所表的脚本
/build/transpile.js        # 编译脚本
/build/update-badges.js    # 用于更新 README 和文档中徽章的 JavaScript 脚本
/build/vss.js              # 从 package.json 中读取单一源代码版本,并在各处写入它
/dist/                     # 存放 CCXT 生成的浏览器捆绑包的文件夹
/ccxt.js                   # CCXT 主要 JavaScript 版本的入口点
/ccxt.php                  # CCXT PHP 版本的入口点
/js/                       # CCXT JavaScript 版本的代码
/ts/                       # CCXT TypeScript 版本的代码
/php/                      # CCXT PHP 版本的模块/包文件夹
/python/                   # CCXT Python 版本的模块/包文件夹,用于 PyPI
/python/__init__.py        # CCXT Python 版本的入口点
/python/async_support/     # Python 3.5.3+ asyncio 支持的 CCXT Python 版本的异步版
/python/base/              # CCXT Python 版本的基本代码
/python/MANIFEST.in        # PyPI 包文件,列出了其他包文件(许可证、配置文件等)
/python/README.md          # PyPI 的 README.md 的副本
/python/setup.cfg          # 用于 Python 包的 wheels 配置文件
/python/setup.py           # 用于 Python 的 pip/setuptools 脚本(构建/安装)CCXT
/python/tox.ini            # Python 的 tox 配置
/examples/                 # 自述
/examples/js               # ...
/examples/php              # ...
/examples/py               # ...
/exchanges.cfg             # 包含要包括的交易所的自定义捆绑配置
/package.json              # npm 包文件,也用于 setup.py 的版本来源
/run-tests.js              # 用于运行所有交易所在所有语言(JS/PHP/Python)的单独测试的前端
/wiki/                     # 所有文档的来源(在此处编辑)

多语言支持#

ccxt 库提供了多种不同的语言版本(TypeScript、JavaScript、Python、PHP、C# 等等)。我们鼓励开发人员设计可移植的代码,以便单一语言的用户也能够轻松阅读其他语言的代码并理解。这有助于该库的推广。主要目标是为尽可能多的现有加密货币交易所提供一个通用、统一、一致和强大的接口。

最初,所有特定语言的版本是并行开发的,但彼此之间是独立的。但当在所有支持的语言之间保持代码一致性和可维护性变得困难时,我们决定采用我们所称的“源码/生成”流程切换。现在,有一个源语言版本,即 TypeScript。其他特定语言版本是从源版本自动语法派生(编译、生成)而来的。但这并不意味着您必须是 TS 或 JS 程序员才能进行贡献。可移植性原则也允许 Python 和 PHP 开发人员有效地参与开发源版本。

模块的入口点包括:

  • Python pip 包:./python/__init__.py

  • Python 3.5.3+ ccxt.async_support 子包:./python/async/__init__.py

  • Node.js npm 包:./js/ccxt.js

  • TypeScript:./ts/ccxt.ts

  • 浏览器捆绑包:./dist/ccxt.browser.js

  • PHP:./ccxt.php生成的版本和文档是通过npm run build命令从源代码ts/src文件夹转译而来的。

转译(生成)文件#

  • 所有派生的交换类都是通过tsc从TypeScript转译成JavaScript,再通过我们的自定义转译器从TypeScript转译成PHP和Python。源文件与语言无关,可以轻松地一一映射到其他任何语言,并以跨语言兼容的方式编写。任何程序员都可以阅读它(这是设计的目的)。

  • 所有基类都不会经过转译,那些是特定于语言的。

JavaScript#

ccxt.browser.js是通过Babel从源代码生成的。

Python#

这些文件包含从TS转译成Python的派生交换类:

  • ts/[_a-z].tspython/ccxt/async/[_a-z].py

  • python/ccxt/async[_a-z].pypython/ccxt/[_a-z].py(Python 3中的asyncio → Python同步转译阶段)

  • python/ccxt/test/test_async.pypython/ccxt/test/test_sync.py(从异步测试生成同步测试)

这些Python基类和文件不进行转译:

  • python/ccxt/base/*

  • python/ccxt/async/base/*#### PHP

这些包含派生交换类的文件是从JS转译到PHP的:

  • ts/[_a-z].tsphp/[_a-z].php

这些PHP的基类和文件不被转译:

  • php/Exchange.php php/ExchangeError.php php/Precise.php ...

Typescript#

  • 使用这些文件进行开发

基类#

正在施工中转译器是基于正则表达式的,并且非常依赖于特定的格式规则。如果你违反了这些规则,转译器要么无法生成 Python/PHP 类,要么会生成格式错误的 Python/PHP 语法。

以下是如何保持 JS 代码可转译的关键注意事项。

在构建之前使用 linter npm run lint js/your-exchange-implementation.js。它将解决许多(但不是全部)问题,如果转译失败仍需要手动检查。

如果在 npm run build 时出现 [TypeError] Cannot read property '1' of null 异常或其他转译错误,请检查你的代码是否满足以下规则:

  • 不要在你的方法内放空行

  • 总是使用 Python 风格的缩进,它在所有语言中保持不变

  • 使用准确的 4 个空格缩进,避免使用制表符

  • 在每个方法之间都要留一个空行

  • 避免混合的注释样式,在 JS 中使用双斜杠 // 进行单行注释

  • 避免多行注释

如果转译进程成功完成,但生成的 Python/PHP 语法有误,请检查以下内容:

  • 每个开括号,如 ({,前面应加一个空格!

  • 不要使用特定语言的代码语法糖,即使你真的很想使用

  • 将所有映射和推导展开为基本的 for 循环

  • 不要更改继承方法中的参数,在所有交易所中保持参数统一

  • 所有操作都必须只使用基类方法(例如,使用 this.json() 将对象转换为 json)

  • 每个语句的末尾都要加上分号 ;,像 PHP/C 风格一样

  • 所有关联的键必须在任何地方都使用单引号字符串(array['good']),不要使用点表示法(array.bad)大多数交易所的 API 端点都需要在请求中指定交易所特定的市场符号、交易对或仪器。

我们不直接向交易所发送统一的符号! 它们不能互换使用!交易所特定的市场 ID统一符号 之间存在重大差异!这在手册中有解释。

永远不要这样做:

async fetchTicker (symbol, params = {}) {
   const request = {
      'pair': symbol, // 非常糟糕,直接向交易所发送统一的符号
   };
   const response = await this.publicGetEndpoint (request);
   // 以统一方式解析...
}

也不要这样做:

async fetchTicker (symbol, params = {}) {
   const request = {
      'symbol': symbol, // 非常糟糕,直接向交易所发送统一的符号
   };
   const response = await this.publicGetEndpoint (request);
   // 以统一方式解析...
}

与其将统一的 CCXT 符号发送到交易所,我们始终使用与该符号对应的交易所特定的市场“id”。如果碰巧交易所特定的市场 ID 正好与 CCXT 统一符号相同,那是一种幸运的巧合,但在统一的 CCXT API 中我们永远不依赖于此。

要通过统一的 CCXT 符号获取交易所特定的市场 ID,请使用以下方法:

  • this.market (symbol) – 返回完整的统一市场结构,包括 idbaseIdquoteId 等其他有趣的信息

  • this.marketId (symbol) – 根据统一符号仅返回市场的交易所特定 id(如果你不需要其他信息)好的例子:

async fetchTicker (symbol, params = {}) {
   const market = this.market (symbol); // 整个市场结构
   const request = {
      'pair': market['id'], // 好的,它们可能相等,但通常不同,没问题
   };
   const response = await this.publicGetEndpoint (this.extend (request, params));
   // 以统一的方式解析...
}
async fetchTicker (symbol, params = {}) {
   const marketId = this.marketId (symbol); // 只有id
   const request = {
      'symbol': marketId, // 好的,它们可能相等,但通常不同,没问题
   };
   const response = await this.publicGetEndpoint (this.extend (request, params));
   // 以统一的方式解析...
}

解析符号#

在向交易所发送请求时,统一符号必须“转换”为特定交易所的市场id,如上所示。同样,当接收到交易所响应时,其中包含一个特定交易所的市场id,它必须“转换回”统一的CCXT符号。

我们不会直接将特定交易所的市场id放入统一的结构中! 我们不能自由地交换符号和id!“特定交易所的市场id”和“统一符号”之间存在着重要的区别!这在手册中有解释,可以参考以下链接:

  • https://github.com/ccxt/ccxt/wiki/Manual#markets

  • https://github.com/ccxt/ccxt/wiki/Manual#symbols-and-market-ids

决不要这样做:

parseTrade (trade, market = undefined) {
   // 解析代码...
   return {
      'info': trade,
      'symbol': trade['pair'], // 非常糟糕,在统一的结构中返回特定交易所的市场id!
      // 其他字段...
   };
}

同样不要这样做

parseTrade (trade, market = undefined) {
   // 解析代码...
   return {
      'info' trade
      'symbol' trade ['symbol']//非常糟糕,在统一的结构中返回特定于交易所的市场id!
      //其他字段...
   };
}

为了正确处理市场id,需要使用loadMarkets()在此交易所上查找其缓存的信息:

好的例子:

parseTrade (trade, market = undefined) {
    const marketId = this.safeStringtrade 'pair';
    // safeSymbol被用来将市场id解析为统一的符号
    const symbol = this.safeSymbolmarketId market;
    return {
       'info' trade
       'symbol' symbol//非常好,在此处使用统一的符号
       //其他字段...
    };
}

访问字典键#

在JavaScript中,可以使用两种表示法访问字典的键:

  • object [ 'key'](单引号字符串键标记)

  • object.key(属性表示法)

这两种表示法几乎可以互换,执行JavaScript代码时会将其中一种隐式转换为另一种。

虽然上述方法在JavaScript中可以工作,但是在Python或PHP中不起作用。在大多数语言中,关联字典键不像属性那样处理。因此,在Python中,object.keyobject ['key']不是相同的。在PHP中,$object->key$object ['key']也不相同。区分关联键和属性的语言对两者使用不同的符号。

为了保持代码的可转换性,请记住这个简单的规则:在库的任何地方,在所有语言中始终使用单引号字符串键表示法object ['key']来访问所有关联字典键!#### 用safe方法对输入进行清理

JavaScript比其他语言的限制要少。它会容忍尝试解引用不存在的键的情况,而其他语言会抛出异常:

// JavaScript

const someObject = {}

if (someObject['nonExistentKey']) {
    // JavaScript中此条件语句的代码块不会执行
    // 因为someObject['nonExistentKey'] === undefined === false
    // 但是JavaScript不会在if语句中抛出异常
}

然而,上述使用“默认值为undefined”的逻辑在Python或PHP中将无效。

# Python
some_dictionary = {}

# 报错
if some_dictionary['nonExistentKey']:
    # 在Python中,尝试解引用非存在的键会抛出一个标准内置的KeyError异常

# 正常工作
if 'nonExistentKey' in some_dictionary and some_dictionary['nonExistentKey']:
    # 这是一种在访问值之前检查键是否存在的方法

# 也可以正常工作
if some_dictionary.get('nonExistentKey'):
    # 这是另一种在访问值之前检查键是否存在的方法...

大多数语言不容忍尝试访问一个不存在的对象键。

因此,请绝不要在编译后的JS文件中这样做:

// JavaScript
const value = object['key'] || other_value; // 在Python或PHP中无法工作!
if (object['key'] || other_value) { /* 在Python或PHP中无法工作! */ }

因此我们有了一系列的safe*函数:

  • safeInteger (object, key, default)safeInteger2 (object, key1, key2, default) – 用于解析毫秒级时间戳

  • safeNumber (object, key, default)safeNumber2 (object, key1, key2, default) – 用于解析金额、价格、成本

  • safeString (object, key, default)safeString2 (object, key1, key2, default) – 用于解析ID、类型、状态

  • safeStringLower (object, key, default)safeStringLower2 (object, key1, key2, default) – 用于解析并转为小写

  • safeStringUpper (object, key, default)safeStringUpper2 (object, key1, key2, default) – 用于解析并转为大写

  • safeValue (object, key, default)safeValue2 (object, key1, key2, default) – 用于解析对象和数组

  • safeTimestamp (object, key, default)safeTimestamp2 (object, key1, key2, default) – 用于解析以秒为单位的UNIX时间戳

safeValue函数用于解析对象内的对象、对象内的数组和布尔值true/false

如果你需要在一个对象中搜索多个不同的键,则可以使用safeMethodN函数系列,它允许通过接受一个键数组作为参数进行任意数量的键搜索。

const price = this.safeStringN (object, [ 'key1', 'key2', 'key3' ], default)

对于上面列出的每个safe方法,也有对应的safeMethodN

上述的safe函数将检查对象中是否存在key(或key1key2)并正确地返回JS/Python/PHP的undefined/None/null值。每个函数还接受最后一个参数作为返回undefined/None/null之外的default值。

或者,您可以先检查键的存在…#### 使用基类密码学方法进行身份验证

不要重复造轮子。始终使用基类方法进行密码学操作。

CCXT库支持以下身份验证算法和密码学算法:

  • HMAC

  • JWT(JSON Web Token)

  • RSA

  • ECDSA 椭圆曲线密码学

    • NIST P256

    • secp256k1

  • OTP 2FA(一次性密码2步身份验证)基本的 Exchange 类提供了几个在此库中实际上所有的加密中都至关重要的方法。派生的交换实现不应使用外部依赖进行加密,一切都应该只使用基本方法完成。

  • hash (message, hash = 'md5', digest = 'hex')

  • hmac (message, secret, hash = 'sha256', digest = 'hex')

  • jwt (message, secret, hash = 'HS256')

  • rsa (message, secret, alg = 'RS256')

  • ecdsa (request, secret, algorithm = 'p256', hash = undefined)

  • totp (secret)

  • stringToBase64(), base64ToBinary(), binaryToBase64()

hash() 方法支持以下 hash 算法:

  • 'md5'

  • 'sha1'

  • 'sha3'

  • 'sha256'

  • 'sha384'

  • 'sha512'

  • 'keccak'

digest 编码参数接受以下值:

  • 'hex'

  • 'binary'

hmac() 方法还支持 hmac() 函数的 digest 参数为 'base64'。这仅适用于 hmac() 函数,其他实现应使用 'binary'binaryToBase64()

时间戳#

此库内所有统一结构中的所有时间戳都是以整数 UTC 毫秒为单位的!

为了将时间戳转换为毫秒级时间戳,CCXT 实现了以下方法:```javascript const data = { ‘unixTimestampInSeconds’: 1565242530, ‘unixTimestampInMilliseconds’: 1565242530165, ‘unixTimestampAsDecimal’: 1565242530.165, ‘stringInSeconds’: ‘1565242530’, };

// 如果底层值已经是毫秒,则转换为整数 const timestamp = this.safeInteger(data, ‘unixTimestampInMilliseconds’); // === 1565242530165

// 如果值的小数点后有毫秒,则转换为整数并乘以一千 const timestamp = this.safeTimestamp(data, ‘unixTimestampAsDecimal’); // === 1565242530165

// 如果值是以秒为单位的 UNIX 时间戳,则转换为整数并乘以一千 const timestamp = this.safeTimestamp(data, ‘unixTimestampInSeconds’); // === 1565242530000

// 如果值是以秒为单位的,则转换为整数并乘以一千 const timestamp = this.safeTimestamp(data, ‘stringInSeconds’); // === 1565242530000


#### 使用数组长度

在 JavaScript 中,获取字符串或数组长度的常用语法是通过引用 `.length` 属性,如下所示:

```javascript
someArray.length

// 或者

someString.length
```它适用于字符串和数组。在Python中,可以以类似的方式完成:

```python
len(some_array)

# 或者

len(some_string)

因此,字符串和数组的长度都可以通过相同的方式访问,并且都可以正常工作。

然而,对于PHP来说情况就不同了,因此字符串长度和数组长度的语法是不同的:

count(some_array);

// 或者

strlen(some_string); // 或者 mb_strlen

由于转译器是逐行工作,无法通过代码内省区分数组和字符串,并且无法正确地将.length转译为PHP,除非额外提供提示。转译器会将JS的.length始终转译为PHP的strlen,并且会优先使用字符串长度而不是数组长度。为了正确表示数组的长度,我们必须按照以下方式操作:```javascript const quantity = parseFloat(responseData.quantity); const price = parseFloat(responseData.price);

const total = quantity * price;

return total.toFixed(2);


And here's the updated pseudocode showing how it will be done **after** the switch to string-based representations:

```javascript
const quantity = Precise.fromString(responseData.quantity);
const price = Precise.fromString(responseData.price);

const total = Precise.multiply(quantity, price);

return Precise.toFixed(total, 2);

The switch to string-based representations ensures that precise calculations can be done for numbers, without any loss of accuracy due to floating point errors. It also allows for consistency in handling numbers across different programming languages.

This change will be implemented gradually, starting with the parsers that handle financial data. Other parts of the codebase will be updated in subsequent phases. The goal is to provide a seamless transition for users and maintain backward compatibility.

The use of the Precise class for string-based mathematics adds a small overhead in terms of performance. However, the benefits of improved accuracy and consistency outweigh this trade-off in most cases.

Note: The example pseudocode provided above is for illustrative purposes only and may not reflect the actual implementation.```javascript const amount = this.safeFloat (order, ‘amount’); const remaining = this.safeFloat (order, ‘remaining’); if (remaining > 0) { status = ‘open’; } else { status = ‘closed’; } // … return { // … ‘amount’: amount, ‘remaining’: remaining, ‘status’: status, // … };


这是我们 **从现在开始** 应该这样做的:

```javascript
const amount = this.safeNumber (order, 'amount'); // 内部字符串层
const remaining = this.safeString (order, 'remaining'); // 内部字符串层
if (Precise.stringGt (remaining, '0')) { // 内部字符串层
    status = 'open';
} else {
    status = 'closed';
}
// ...
return {
    // ...
    'amount': amount, // 外部层,提供给用户
    'remaining': this.parseNumber (remaining), // 外部层,提供给用户
    'status': status,
    // ...
};

在所有解析器的新代码中,我们应该在解析器的主体中始终使用基于字符串的数值。同时,我们应该在返回结果给调用者时,作为处理数值的最后一步,添加 parseNumber。上述两个片段仅为示例,展示了在 safeStringparseNumber 中使用 Precise 的用法。实际的订单解析器还涉及到 safeOrder 方法:https://github.com/ccxt/ccxt/pulls?q=safeOrder2.

用户最终将有选择的余地,可以选择他想要的 parseNumber 实现:返回浮点数或返回字符串。这样每个人都会保持满意,并且该库将以非破坏性的方式同时支持两种方式运行。

原则是:+ 仅用于字符串连接(!)所有算术操作都必须使用 Precise

格式化十进制小数精度#

本部分涵盖了请求组装部分。.toFixed () 方法在 JavaScript 中有已知的舍入错误问题,其他编程语言中的舍入方法也是如此。为了一致地处理数值格式化,请使用手册中描述的decimalToPrecision方法

转义的控制字符#

使用包含控制字符的字符串时,比如 "\n""\t",请始终使用双引号 ("),而不是单引号 (')!单引号字符串在许多语言中(除了 JavaScript 外)不会解析控制字符,并且会按原样处理。因此,为了使 PHP 中的制表符和换行符正常工作,我们需要用双引号将它们括起来(特别是在 sign() 的实现中)。#### 使用三元条件运算符

不要使用繁重的三元条件 (?:) 运算符,在三元运算符中始终使用括号!

尽管编程语言本身有运算符优先级,但转译器是基于正则表达式的,它不会进行代码内省,因此它将所有内容都视为纯文本。

需要使用括号来提示转译器条件的哪一部分是什么。如果没有括号,很难理解代码行和开发者的意图。

下面是一些设计不良的代码示例,这些代码会破坏转译器:```javascript // 这是一个糟糕的代码风格的示例,很可能会导致转译器出错 const foo = { ‘bar’: ‘a’ + qux === ‘baz’ ? this.a () : this.b () + ‘b’, };


```javascript
// 这会让转译器和开发者都感到困惑
const foo = 'bar' + baz + qux ? 'a' : '' + this.c ();

在对应的部分添加括号会是一个比较正确的解决方法。

const foo = {
   'bar': (qux === 'baz') ? this.a () : this.b (), // 现在好多了
};

解决它的通用方法就是将复杂的行拆分成几行更简单的行,即使需要增加额外的行和条件语句的代价也是可以接受的:

// before:
// const foo = {
//    'bar': 'a' + qux === 'baz' ? this.a () : this.b () + 'b',
// };
// ----------------------------------------------------------------------------
// after:
const bar = (qux === 'baz') ? this.a () : this.b ();
const foo = {
   'bar': 'a' + bar + 'b',
};

甚至可以这样:

// before:
// const foo = 'bar' + baz + qux ? 'a' : '' + this.c ();
// ----------------------------------------------------------------------------
// after:
let foo = 'bar' + baz;
if (qux) {
   foo += 'a';
};
foo += this.c ();

新的交易所整合#

记住:这个库被使用的主要原因是统一性。当开发新的交易文件时,目标不是以某种方式实现它,而是以非常严谨、精确和准确的方式实现它,就像其他交易所一样。为此,我们必须从其他交易所中复制部分逻辑,并确保新的交易所符合以下方面的手册要求:

  • 所有解析方法(fetchMarketsfetchCurrenciesparseTradeparseOrder等)中的市场ID、交易对符号、货币ID、代币代码、符号统一性和commonCurrencies必须标准化。

  • 所有统一API方法的名称和参数都是标准的 - 不能随意添加或更改它们。

  • 所有解析器输入必须进行“safe”清理,就像上面所述。

  • 对于批量操作,应使用基础方法(parseTradesparseOrders,注意s的复数形式)。

  • 尽量使用基本功能,不要重新发明轮子,也不要重新发明自行车,更不要重新发明自行车轮子。

  • 尊重“fetch”方法中的默认参数值,检查sincelimit是否为undefined,如果是,则不要将它们发送到交易所,我们有意在这种情况下使用交易所的默认值。

  • 当实现一个带有一些参数的统一方法时,我们不能忽略或遗漏任何一个参数。

  • 所有从统一方法返回的结构必须符合手册中的规定。

  • 所有API端点都必须列出,并正确支持URL中的参数替换。

请查看以下文档以了解新的集成:https://github.com/ccxt/ccxt/wiki/Requirements

快速合并新交易所集成的拉取请求取决于遵守上述统一规则和标准的一致性。违反其中一个规则是不合并拉取请求的主要原因。

如果您想添加(支持)另一个交易所,或者为特定的交易所实现新的方法,那么使其成为一个一致的改进的最佳方法是学习示例。看看其他交易所(我们推荐认证的交易所)是如何实施相同的事物,并尽量复制代码流程和风格。

新交易所集成的基本JSON框架如下所示:

{
   'id': 'example',
   'name': 'Example Exchange',
   'country': [ 'US', 'EU', 'CN', 'RU' ],
   'rateLimit': 1000,
   'version': '1',
   'comment': 'This comment is optional',
   'urls': {
      'logo': 'https://example.com/image.jpg',
      'api': 'https://api.example.com/api',
      'www': 'https://www.example.com',
      'doc': [
         'https://www.example.com/docs/api',
         'https://www.example.com/docs/howto',
         'https://github.com/example/docs',
      ],
   },
   'api': {
      'public': {
         'get': [
            'endpoint/example',
            'orderbook/{pair}/full',
            '{pair}/ticker',
         ],
      },
      'private': {
         'post': [
            'balance',
         ],
      },
   },
}

隐式API方法#

在每个交易所的代码中,您会注意到用于进行API请求的函数没有显式定义。这是因为交易所描述JSON中的api定义被用来在交易所子类中创建“魔法函数”(也称为“部分函数”或“闭包”)。这种隐式注入是通过defineRestApi/define_rest_api基本交易所方法来实现的。

每个部分函数接受一个params字典并返回API响应。在上面的示例JSON中,'endpoint/example'导致注入一个this.publicGetEndpointExample函数。类似地,'orderbook/{pair}/full'会导致一个this.publicGetOrderbookPairFull函数,它接受一个pair参数(同样通过params参数传递)。### 实例化

在实例化基本交换类时,它会从端点列表中获取每个 URL,将其拆分为单词,然后通过使用 partial 构建来生成可调用的函数名。这个过程在JS、Python和PHP中是相同的。在下面的链接中也有描述:

正在建设中

文档字符串#

  • 当一个方法将另一个参数作为 params 的属性(例如 params['something'])时,将该参数添加到文档字符串中,如 params.something

    • 如果该参数是必需的,则类型为 {str}{int}{etc};如果该参数是可选的,则类型为 {str|undefined}{int|undefined}{etc|undefined}

  • 当一个参数的默认值为 undefined,但方法中包含类似 if (symbol === undefined) { throw new ArgumentsRequired('...')} 的内容时,请将该参数的类型设置为 {str}{int}{etc}。如果没有抛出错误,则类型为 {str|undefined}{int|undefined}{etc|undefined}

  • 如果一个方法不使用其中的一个统一参数,请将该参数的描述设置为 not used by exchange_name.method_name () (将 exchange_namemethod_name 替换为真实的交易所和方法名称)

  • 如果该方法有任何其他特殊用途,请将这些用途放在文档字符串的描述中,这些特例也可以包含在类的文档字符串中

持续集成#

构建使用 Travis CI 实现自动化。Travis CI 的构建步骤在 .travis.yml 文件中有描述。

Windows 构建使用 Appveyor 实现自动化。Appveyor 的构建步骤在 appveyor.yml 文件中描述。

传入的拉取请求会自动由 CI 服务进行验证。您可以在此处在线观看构建过程:app.travis-ci.com/github/ccxt/ccxt/builds

如何在本地构建和运行测试#

添加交易所凭据#

CCXT对公共API和私有API进行了测试。默认情况下,CCXT的内置测试只测试公共API,因为代码仓库中不包括用于私有API测试的API密钥。另外,包含的私有测试不会以任何方式改变账户余额,所有测试都是非侵入式的。为了启用私有API测试,必须配置API密钥。这可以在keys.local.json文件中或使用env变量进行配置。

keys.local.json文件中配置API凭据和选项#

可以将交易所API凭据添加到存储库内根文件夹中的keys.local.json中。如果该文件在您的端上不存在,请先创建它。该文件在.gitignore.npmignore中。您可以向keys.local.json文件中添加交易所凭据和不同交易所的各种选项。

keys.local.json文件的示例:

{
    "ftx": {
        "apiKey": "XXX",
        "secret": "YYY"
    },
    "binance": {
        "apiKey": "XXX",
        "secret": "YYY",
        "options": {
            "some-option": "some value"
        }
    },
    // ...
}
将API凭据配置为环境变量#

您还可以将API凭据定义为env变量:

请参考您的操作系统和shell的文档,了解如何设置环境变量。大多数情况下,set命令或export命令都可以正常工作。env命令可能有助于检查已设置的环境变量。env 变量的示例:BINANCE_APIKEYBINANCE_SECRETKRAKEN_APIKEYKRAKEN_SECRET,等等。

构建#

首次构建之前,安装 Node 依赖(如果你使用我们的 Docker 镜像,可以跳过此步骤):

npm install

以下命令将构建所有内容,并从源 TS 文件生成 PHP/Python 版本:

npm run build

测试#

以下命令将对生成的文件进行测试(适用于所有交易所、符号和语言):

node run-tests

你可以将测试限制在特定的语言、特定的交易所或特定的符号中:``` node run-tests [–js] [–python] [–python-async] [–php] [–php-async] [exchange] [symbol]


`node run-tests exchangename`会执行5个测试:`js`,`python`,`python-async`,`php`,`php-async`。您可以像下面这样控制它:

node run-tests exchange –js node run-tests exchange –js –python-async node run-tests exchange –js –php node run-tests exchange –python –python-async …


然而,如果失败了,您可能需要深入到更低一级来运行特定语言的测试,以查看出了什么问题:

node js/test/test exchange –verbose python3 python/ccxt/test/test_sync.py exchange –verbose python3 python/ccxt/test/test_async.py exchange –verbose php -f php/test/test_sync.php exchange –verbose php -f php/test/test_async.php exchange –verbose


`test_sync`只是`test_async`的同步版本,所以在大多数情况下,只需要运行`test_async.py`和`test_async.php`即可:

node js/test/test exchange –verbose python3 python/ccxt/test/test_async.py exchange –verbose php -f php/test/test_async.php exchange –verbose


当所有特定语言的测试都通过时,`node run-tests`也将成功运行。为了运行这些测试,您需要确保构建成功完成。

例如,以下代码中的第一行只测试库的源JS版本(`ccxt.js`)。在运行之前不需要`npm run build`(如果需要快速验证更改是否破坏了代码,这可能很有用):

```shell```bash
node run-tests --js                  # 测试主要的 ccxt.js,所有交易所

# 其他示例需要运行 'npm run build'

node run-tests --python              # 测试 Python 同步版本,所有交易所
node run-tests --php bitfinex        # 使用 PHP 测试 Bitfinex
node run-tests --python-async kraken # 使用 Python 异步测试 Kraken,需要运行 'npm run build'

编写测试#

按照以下步骤添加一个测试:

将更改提交到存储库#

构建过程会生成许多转换后的交易所文件,例如 Python 和 PHP 文件。您不应将它们提交到 GitHub,请仅提交基本(TS)文件的更改

财务捐款#

我们也欢迎全透明地接受财务捐款,详情请参阅我们的开放集体页面。## 鸣谢

贡献者#

感谢所有已经为 ccxt 做出贡献的人!

赞助者#

感谢所有的赞助者![成为赞助者]

支持者#

通过成为支持者来支持这个项目。您的头像将以链接到您的网站显示在这里。

[成为支持者]Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective

赞助商#

感谢所有的赞助商! (请让您的公司也通过成为赞助商来支持这个开源项目)

[成为赞助商]

Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective Open Collective