Skip to content

Latest commit

 

History

History
371 lines (277 loc) · 19.3 KB

CONTRIBUTING.md

File metadata and controls

371 lines (277 loc) · 19.3 KB

Contributing To The CCXT Library

- This file is a work in progress, guidelines for contributing are being developed right now!

How To Submit An Issue

If you want to submit an issue and you want your issue to be resolved quickly, here's a checklist for you:

  • Read the Manual, and especially carefully read the following sections:
  • Read the Troubleshooting section and follow troubleshooting steps.
  • Read the FAQ for most frequently asked questions.
  • Read the API docs for your exchange.
  • Search for similar issues first to avoid duplicates.
  • If your issue is unique, along with a basic description of the failure, the following IS REQUIRED:
    • set exchange.verbose = true property on the exchange instance before calling its functions or methods
    • surround code and output with triple backticks: ```GOOD```
    • don't confuse the backtick symbol (`) with the quote symbol ('): '''BAD'''
    • don't confuse a single backtick with triple backticks: `BAD`
    • paste a complete code snippet you're having difficulties with, avoid one-liners
    • paste the full verbose output of the failing method without your keys
    • the verbose output should include the request and response from the exchange (not just an error callstack)
    • write your language and version
    • write ccxt library version
    • which exchange it is
    • which method you're trying to call

How To Contribute Code

  • PLEASE, DO NOT COMMIT THE FOLLOWING FILES IN PULL REQUESTS:

    • /doc/*
    • /build/*
    • /php/* (except for base classes)
    • /python/* (except for base classes)

    These files are generated (explained below) and will be overwritten upon build. Please don't commit them to avoid bloating the repository which is already quite large. Most often, you have to commit just one single source file to submit an edit to the implementation of an exchange.

  • PLEASE, SUBMIT ATOMIC EDITS, ONE PULL REQUEST PER ONE EXCHANGE, DO NOT MIX THEM

  • MAKE SURE YOUR CODE PASSES ALL SYNTAX CHECKS BY RUNNING npm run build

Pending Tasks

Below is a list of functionality we would like to have implemented in the library in the first place. Most of these tasks are already in progress, implemented for some exchanges, but not all of them:

  • Unified fetchOrder
  • Unified fetchOrders, fetchOpenOrders, fetchClosedOrders
  • Unified fetchMyTrades, fetchOrderTrades
  • Unified fetchDepositAddress, createDepositAddress
  • Unified withdraw
  • Unified fees
  • Unified fetchTransactions, fetchDeposits, fetchWithdrawals
  • Improved proxy support
  • WebSocket interfaces:
    • Pub: Methods for trading and private calls where supported
    • Sub: Real-time balance, orderbooks and other properties with each exchange

If you want to contribute by submitting partial implementations be sure to look up examples of how it's done inside the library (where implemented already) and copy the adopted practices.

If your proposal, suggestion or improvement does not relate to the above list of tasks before submitting it make sure it is:

  1. really needed by the majority of ccxt users
  2. designed to be a general-purpose solution, not hardcoded for your specific needs
  3. done in a generalized way compatible with all exchanges (not exchange-specific)
  4. portable (available in all supported languages)
  5. robust
  6. explicit in what it's doing
  7. doesn't break anything (if you change a method, make sure that all other methods calling the edited method are not broken)

The following is a set of rules for contributing to the ccxt library codebase.

What You Need To Have

  • Node.js 8+
  • Python 3.5.3+ and Python 2.7+
  • PHP 5.3+ with the following extensions installed and enabled:
    • cURL
    • iconv
    • mbstring
    • PCRE
  • Pandoc 1.19+

What You Need To Know

Repository Structure

The contents of the repository are structured as follows:

/                          # root directory aka npm module/package folder for Node.js
/.babelrc                  # babel config used for making the ES5 version of the library
/.eslintrc                 # linter
/.gitattributes            # contains linguist settings for language detection in repo
/.gitignore                # ignore it
/.npmignore                # files to exclude from the NPM package
/.travis.yml               # a YAML config for travis-ci (continuous integration)
/CHANGELOG.md              # self-explanatory
/CONTRIBUTING.md           # this file
/LICENSE.txt               # MIT
/README.md                 # master markdown for GitHub, npmjs.com, npms.io, yarn and others
/build/                    # a folder for the generated files
/ccxt.js                   # entry point for the master JS version of the ccxt library
/ccxt.php                  # entry point for the PHP version of the ccxt library
/doc/                      # Sphinx-generated rst-docs for http://ccxt.readthedocs.io/
/js/                       # the JS version of the library
/php/                      # PHP ccxt module/package folder
/python/                   # Python ccxt module/package folder for PyPI
/python/__init__.py        # entry point for the Python version of the ccxt.library
/python/async/__init__.py  # asynchronous version of the ccxt.library for Python 3.5.3+ asyncio
/python/base/              # base code for the Python version of the ccxt library
/python/MANIFEST.in        # a PyPI-package file listing extra package files (license, configs, etc...)
/python/README.rst         # generated reStructuredText for PyPI
/python/setup.cfg          # wheels config file for the Python package
/python/setup.py           # pip/setuptools script (build/install) for ccxt in Python
/python/tox.ini            # tox config for Python
/countries.js              # a list of ISO 2-letter country codes in JS for testing, not very important
/examples/                 # self-explanatory
/examples/js               # ...
/examples/php              # ...
/examples/py               # ...
/exchanges.cfg             # custom bundle config for including only the exchanges you need
/export-exchanges.js       # used to create tables of exchanges in the docs during the build
/package.json              # npm package file, also used in setup.py for version single-sourcing
/run-tests.js              # a front-end to run invididual tests of all exchanges in all languages (JS/PHP/Python)
/transpile.js              # the transpilation script
/update-badges.js          # a JS script to update badges in the README and in docs
/vss.js                    # reads single-sourced version from package.json and writes it everywhere
/wiki/                     # the source of all docs (edits go here)

Multilanguage Support

The ccxt library is available in three different languages (more to come). We encourage developers to design portable code, so that a single-language user can read code in other languages and understand it easily. This helps the adoption of the library. The main goal is to provide a generalized, unified, consistent and robust interface to as many existing cryptocurrency exchanges as possible.

At first, all language-specific versions were developed in parallel, but separately from each other. But when it became too hard to maintain and keep the code consistent among all supported languages we decided to switch to what we call a source/generated process. There is now a single source version in one language, that is JavaScript. Other language-specific versions are syntactically derived (transpiled, generated) from the source version. But it doesn't mean that you have to be a JS coder to contribute. The portability principle allows Python and PHP devs to effectively participate in developing the source version as well.

The module entry points are:

  • ./python/__init__.py for the Python pip package
  • ./python/async/__init__.py for the Python 3.5.3+ ccxt.async_support subpackage
  • ./ccxt.js for the Node.js npm package
  • ./build/ccxt.browser.js for the browser bundle
  • ./ccxt.php for PHP

Generated versions and docs are transpiled from the source ccxt.js file and files in ./js/ by the npm run build command.

Transpiled (generated) files

  • All derived exchange classes are transpiled from source JS files. The source files are language-agnostic, easily mapped line-to-line to any other language and written in a cross-language-compatible way. Any coder can read it (by design).
  • All base classes are not transpiled, those are language-specific.
JavaScript

The ccxt.browser.js is generated with Babel from source.

Python

These files containing derived exchange classes are transpiled from JS into Python:

  • js/[_a-z].jspython/ccxt/async/[_a-z].py
  • python/ccxt/async[_a-z].pypython/ccxt/[_a-z].py (Python 3 asyncio → Python 2 sync transpilation stage)
  • python/test/test_async.pypython/test/test.py (the sync test is generated from the async test)

These Python base classes and files are not transpiled:

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

These files containing derived exchange classes are transpiled from JS into PHP:

  • js/[_a-z].jsphp/[_a-z].php

These PHP base classes and files are not transpiled:

  • php/base/*
Typescript
  • js/[_a-z].jsccxt.d.ts

Base Class

UNDER CONSTRUCTION

Derived Exchange Classes

Transpiler is regex-based and heavily relies on specific formatting rules. If you break them then the transpiler will either fail to generate Python/PHP classes at all or will generate malformed Python/PHP syntax.

Below are key notes on how to keep the JS code transpileable.

Use the linter npm run lint js/your-exchange-implementation.js before you build. It will cover many (but not all) the issues, so manual checking will still be required if transpilation fails.

If you see a [TypeError] Cannot read property '1' of null exception or any other transpilation error when you npm run build, check if your code satisifes the following rules:

  • don't put empty lines inside your methods
  • always use Python-style indentation, it is preserved as is for all languages
  • indent with 4 spaces exactly, avoid tabs
  • put an empty line between each of your methods
  • avoid mixed comment styles, use double-slash // in JS for line comments
  • avoid multi-line comments

If the transpiling process finishes successfully, but generates incorrect Python/PHP syntax, check for the following:

  • every opening bracket like ( or { should have a space before it!
  • do not use language-specific code syntax sugar, even if you really want to
  • unfold all maps and comprehensions to basic for-loops
  • don't change the arguments of overrided inherited methods, keep them uniform across all exchanges
  • do everything with base class methods only (for example, use this.json () for converting objects to json).
  • always put a semicolon ; at the end of each statement, as in PHP/C-style
  • all associative keys must be single-quoted strings everywhere, array['good'], array.bad
  • all local variables should be declared with the let keyword

And structurally:

  • if you need another base method you will have to implement it in all three languages
  • do not issue more than one HTTP request from a unified method
  • try to reduce syntax to basic one-liner expressions
  • multiple lines are ok, but you should avoid deep nesting with lots of brackets
  • avoid changing the contents of the arguments and params passed by reference into function calls
  • do not use conditional statements that are too complex (heavy if-bracketing)
  • do not use heavy ternary conditionals
  • avoid operators clutter (don't do this: a && b || c ? d + 80 : e ** f)
  • do not use .includes(), use .indexOf() instead!
  • never use .toString() on floats: Number (0.00000001).toString () === '1e-8'
  • do not use the in operator to check if a value is in a non-associative array (list)
  • don't add custom currency or symbol/pair conversions and formatting, copy from existing code instead
  • keep it simple, don't do more than one statement in one line

If you want to add (support for) another exchange, or implement a new method for a particular exchange, then the best way to make it a consistent improvement is to learn from example. Take a look at how same things are implemented in other exchanges and try to copy the code flow and style.

The basic JSON-skeleton for a new exchange integration is as follows:

{
   "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"
         ]
      }
   }
}

Implicit API Methods

In the code for each exchange, you'll notice that the functions that make API requests aren't explicitly defined. This is because the api definition in the exchange description JSON is used to create magic functions (aka partial functions or closures) inside the exchange subclass. That implicit injection is done by the defineRestApi/define_rest_api base exchange method.

Each partial function takes a dictionary of params and returns the API response. In the example JSON above, the 'endpoint/example' results in the injection of a this.publicGetEndpointExample function. Similarly, the 'orderbook/{pair}/full' results in a this.publicGetOrderbookPairFull function, that takes a pair parameter (again, passed in the params argument).

Upon instantiation the base exchange class takes each URL from its list of endpoints, splits it into words, and then makes up a callable function name from those words by using a partial construct. That process is the same in JS, Python and PHP as well. It is also described here:

UNDER CONSTRUCTION

Continuous Integration

Builds are automated with Travis CI. The build steps for Travis CI are described in the .travis.yml file.

Windows builds are automated with Appveyor. The build steps for Appveyor are in the appveyor.yml file.

Incoming pull requests are automatically validated by the CI service. You can watch the build process online here: travis-ci.org/ccxt/ccxt/builds.

How To Build & Run Tests On Your Local Machine

Before building for the first time, install Node dependencies:

npm install

The command below will build everything and generate PHP/Python versions from source JS files:

npm run build

The following command will test the built generated files (for all exchanges, symbols and languages):

node run-tests

You can restrict tests to a specific language, a particular exchange or symbol:

node run-tests [--php] [--js] [--python] [--python3] [exchange] [symbol]

For example, the first of the following lines will only test the source JS version of the library (ccxt.js). It does not require an npm run build before running it (can be useful if you need to verify quickly whether your changes break the code or not):

node run-tests --js             # test master ccxt.js, all exchanges

# other examples require the 'npm run build' to run

node run-tests --python         # test Python 2 version, all exchanges
node run-tests --php bitfinex   # test Bitfinex with PHP
node run-tests --python3 kraken # test Kraken with Python 3, requires 'npm run build'

UNDER CONSTRUCTION

Financial contributions

We also welcome financial contributions in full transparency on our open collective. Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.

Credits

Contributors

Thank you to all the people who have already contributed to ccxt!

Backers

Thank you to all our backers! [Become a backer]

Sponsors

Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor)