IPC removed, replaced by WS

This commit is contained in:
Luis Fernando Molina committed 2020-04-06 08:33:07 +02:00
1 parent ba4217fb24
commit 656c193511
503 files changed
+28128 -34498

No files matched your search

-220
View File
@@ -1,220 +0,0 @@
{
"_from": "@types/node@^13.1.0",
"_id": "@types/node@13.7.0",
"_inBundle": false,
"_integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==",
"_location": "/@types/node",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@types/node@^13.1.0",
"name": "@types/node",
"escapedName": "@types%2fnode",
"scope": "@types",
"rawSpec": "^13.1.0",
"saveSpec": null,
"fetchSpec": "^13.1.0"
},
"_requiredBy": [
"/telegraf"
],
"_resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz",
"_shasum": "b417deda18cf8400f278733499ad5547ed1abec4",
"_spec": "@types/node@^13.1.0",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\TaskServer\\node_modules\\telegraf",
"bugs": {
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "Microsoft TypeScript",
"url": "https://github.com/Microsoft"
},
{
"name": "DefinitelyTyped",
"url": "https://github.com/DefinitelyTyped"
},
{
"name": "Alberto Schiabel",
"url": "https://github.com/jkomyno"
},
{
"name": "Alexander T.",
"url": "https://github.com/a-tarasyuk"
},
{
"name": "Alvis HT Tang",
"url": "https://github.com/alvis"
},
{
"name": "Andrew Makarov",
"url": "https://github.com/r3nya"
},
{
"name": "Benjamin Toueg",
"url": "https://github.com/btoueg"
},
{
"name": "Bruno Scheufler",
"url": "https://github.com/brunoscheufler"
},
{
"name": "Chigozirim C.",
"url": "https://github.com/smac89"
},
{
"name": "Christian Vaagland Tellnes",
"url": "https://github.com/tellnes"
},
{
"name": "David Junger",
"url": "https://github.com/touffy"
},
{
"name": "Deividas Bakanas",
"url": "https://github.com/DeividasBakanas"
},
{
"name": "Eugene Y. Q. Shen",
"url": "https://github.com/eyqs"
},
{
"name": "Flarna",
"url": "https://github.com/Flarna"
},
{
"name": "Hannes Magnusson",
"url": "https://github.com/Hannes-Magnusson-CK"
},
{
"name": "Hoàng Văn Khải",
"url": "https://github.com/KSXGitHub"
},
{
"name": "Huw",
"url": "https://github.com/hoo29"
},
{
"name": "Kelvin Jin",
"url": "https://github.com/kjin"
},
{
"name": "Klaus Meinhardt",
"url": "https://github.com/ajafff"
},
{
"name": "Lishude",
"url": "https://github.com/islishude"
},
{
"name": "Mariusz Wiktorczyk",
"url": "https://github.com/mwiktorczyk"
},
{
"name": "Mohsen Azimi",
"url": "https://github.com/mohsen1"
},
{
"name": "Nicolas Even",
"url": "https://github.com/n-e"
},
{
"name": "Nicolas Voigt",
"url": "https://github.com/octo-sniffle"
},
{
"name": "Nikita Galkin",
"url": "https://github.com/galkin"
},
{
"name": "Parambir Singh",
"url": "https://github.com/parambirs"
},
{
"name": "Sebastian Silbermann",
"url": "https://github.com/eps1lon"
},
{
"name": "Simon Schick",
"url": "https://github.com/SimonSchick"
},
{
"name": "Thomas den Hollander",
"url": "https://github.com/ThomasdenH"
},
{
"name": "Wilco Bakker",
"url": "https://github.com/WilcoBakker"
},
{
"name": "wwwy3y3",
"url": "https://github.com/wwwy3y3"
},
{
"name": "Zane Hannan AU",
"url": "https://github.com/ZaneHannanAU"
},
{
"name": "Samuel Ainsworth",
"url": "https://github.com/samuela"
},
{
"name": "Kyle Uehlein",
"url": "https://github.com/kuehlein"
},
{
"name": "Jordi Oliveras Rovira",
"url": "https://github.com/j-oliveras"
},
{
"name": "Thanik Bhongbhibhat",
"url": "https://github.com/bhongy"
},
{
"name": "Marcin Kopacz",
"url": "https://github.com/chyzwar"
},
{
"name": "Trivikram Kamat",
"url": "https://github.com/trivikr"
},
{
"name": "Minh Son Nguyen",
"url": "https://github.com/nguymin4"
},
{
"name": "Junxiao Shi",
"url": "https://github.com/yoursunny"
},
{
"name": "Ilia Baryshnikov",
"url": "https://github.com/qwelias"
}
],
"dependencies": {},
"deprecated": false,
"description": "TypeScript definitions for Node.js",
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped#readme",
"license": "MIT",
"main": "",
"name": "@types/node",
"repository": {
"type": "git",
"url": "git+https://github.com/DefinitelyTyped/DefinitelyTyped.git",
"directory": "types/node"
},
"scripts": {},
"typeScriptVersion": "2.8",
"types": "index.d.ts",
"typesPublisherContentHash": "b0cd8dccd6d2eca8bb1de5a05bebf13796984567eb809f63164972122c4ddbaf",
"typesVersions": {
">=3.5.0-0": {
"*": [
"ts3.5/*"
]
}
},
"version": "13.7.0"
}
+2 -2
View File
@@ -26,9 +26,9 @@ function cleanupOldTags () {
const tagsByMajorMinor = values (groupBy (tags, 'key')).sort ((a, b) => a[0].key - b[0].key)
// Preserve all tags for first 3 minor versions
// Preserve all tags for first 5 minor versions
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 5; i++) {
const tags = tagsByMajorMinor.pop ()
+175 -112
View File
@@ -93,49 +93,51 @@ function createExchanges (ids) {
// ----------------------------------------------------------------------------
// TODO: REWRITE THIS ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
function exportSupportedAndCertifiedExchanges (exchanges, wikiPath) {
function exportSupportedAndCertifiedExchanges (exchanges, { allExchangesPaths, certifiedExchangesPaths, exchangesByCountriesPaths, proExchangesPaths }) {
// ............................................................................
// markup constants and helper functions
const countryName = (code) => countries[code] || code
const ccxtCertifiedBadge = '[![CCXT Certified](https://img.shields.io/badge/CCXT-certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification)'
const ccxtCertifiedBadge = '[![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification)'
const ccxtProBadge = '[![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro)'
const logoHeading = '&nbsp;'.repeat (7) + 'logo' + '&nbsp;'.repeat (7)
const tableHeadings = [ logoHeading, 'id', 'name', 'ver', 'doc', 'certified', ]
const exchangesByCountryHeading = [ 'country / region', ... tableHeadings ]
const tableHeadings = [ logoHeading, 'id', 'name', 'ver', 'doc', 'certified', 'pro' ]
const exchangesByCountryHeading = [ 'country / region', ... tableHeadings.slice (0, 5) ]
// ----------------------------------------------------------------------------
// list all supported exchanges
const exchangesNotListedInDocs = []
const exchangesNotListedInDocs = [ 'hitbtc2' ]
let tableData = values (exchanges)
.filter (exchange => !exchangesNotListedInDocs.includes (exchange.id))
.map (exchange => {
let logo = exchange.urls['logo']
let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www
let url = exchange.urls.referral || website
let countries = Array.isArray (exchange.countries) ? exchange.countries.map (countryName).join (', ') : countryName (exchange.countries)
let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc
let version = exchange.version ? exchange.version : '\*'
let matches = version.match (/[^0-9]*([0-9].*)/)
if (matches) {
version = matches[1];
}
return [
'[![' + exchange.id + '](' + logo + ')](' + url + ')',
exchange.id,
'[' + exchange.name + '](' + url + ')',
version,
'[API](' + doc + ')',
exchange.certified ? ccxtCertifiedBadge : '',
countries,
]
})
function makeTableData (exchanges) {
return (
values (exchanges)
.filter (exchange => !exchangesNotListedInDocs.includes (exchange.id))
.map (exchange => {
let logo = exchange.urls['logo']
let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www
let url = exchange.urls.referral || website
let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc
let version = exchange.version ? exchange.version : '\*'
let matches = version.match (/[^0-9]*([0-9].*)/)
if (matches) {
version = matches[1];
}
return [
'[![' + exchange.id + '](' + logo + ')](' + url + ')',
exchange.id,
'[' + exchange.name + '](' + url + ')',
version,
'[API](' + doc + ')',
exchange.certified ? ccxtCertifiedBadge : '',
exchange.pro ? ccxtProBadge : '',
]
})
)
}
// prepend the table header
tableData.splice (0, 0, tableHeadings)
function makeTable (jsonArray) {
let table = asTable.configure ({ 'delimiter': ' | ' }) (jsonArray)
@@ -148,88 +150,128 @@ function exportSupportedAndCertifiedExchanges (exchanges, wikiPath) {
return lines.map (line => '|' + line + '|').join ("\n")
}
const exchangesTable = makeTable (tableData)
const numExchanges = keys (exchanges).length
const beginning = "The ccxt library currently supports the following "
const ending = " cryptocurrency exchange markets and trading APIs:\n\n"
const totalString = beginning + numExchanges + ending
const allExchanges = totalString + exchangesTable + "$1"
const allExchangesRegex = new RegExp ("[^\n]+[\n]{2}\\|[^`]+\\|([\n][\n]|[\n]$|$)", 'm')
logExportExchanges ('README.md', allExchangesRegex, allExchanges)
logExportExchanges (wikiPath + '/Manual.md', allExchangesRegex, allExchanges)
logExportExchanges (wikiPath + '/Exchange-Markets.md', allExchangesRegex, allExchanges)
if (allExchangesPaths) {
const certifiedFieldIndex = tableHeadings.indexOf ('certified')
const certified = tableData.filter ((x) => x[certifiedFieldIndex] !== '' )
const certifiedExchangesRegex = new RegExp ("^(## Certified Cryptocurrency Exchanges\n{3})(?:\\|.+\\|$\n)+", 'm')
const certifiedExchangesTable = makeTable (certified)
const certifiedExchanges = '$1' + certifiedExchangesTable + "\n"
logExportExchanges ('README.md', certifiedExchangesRegex, certifiedExchanges)
const tableData = makeTableData (exchanges)
const numExchanges = tableData.length
// prepend the table header
tableData.splice (0, 0, tableHeadings)
const exchangesTable = makeTable (tableData)
const beginning = "The CCXT library currently supports the following "
const ending = " cryptocurrency exchange markets and trading APIs:\n\n"
const totalString = beginning + numExchanges + ending
const allExchanges = totalString + exchangesTable + "$1"
const allExchangesRegex = new RegExp ("[^\n]+[\n]{2}\\|[^`]+\\|([\n][\n]|[\n]$|$)", 'm')
for (const path of allExchangesPaths) {
logExportExchanges (path, allExchangesRegex, allExchanges)
}
let exchangesByCountries = []
keys (countries).forEach (code => {
let country = countries[code]
let result = []
keys (exchanges).forEach (id => {
let exchange = exchanges[id]
let logo = exchange.urls['logo']
let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www
let url = exchange.urls.referral || website
let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc
let version = exchange.version ? exchange.version : '\*'
let matches = version.match (/[^0-9]*([0-9].*)/)
if (matches)
version = matches[1];
let shouldInclude = false
if (Array.isArray (exchange.countries)) {
if (exchange.countries.indexOf (code) > -1)
shouldInclude = true
// logExportExchanges ('README.md', allExchangesRegex, allExchanges)
// logExportExchanges (wikiPath + '/Manual.md', allExchangesRegex, allExchanges)
// logExportExchanges (wikiPath + '/Exchange-Markets.md', allExchangesRegex, allExchanges)
}
if (proExchangesPaths) {
const pro = values (exchanges).filter (exchange => exchange.pro)
const tableData = makeTableData (pro)
const numExchanges = tableData.length
// prepend the table header
tableData.splice (0, 0, tableHeadings)
const exchangesTable = makeTable (tableData)
const beginning = "The CCXT Pro library currently supports the following "
const ending = " cryptocurrency exchange markets and WebSocket trading APIs:\n\n"
const totalString = beginning + numExchanges + ending
const proExchanges = totalString + exchangesTable + "$1"
const proExchangesRegex = new RegExp ("[^\n]+[\n]{2}\\|[^`]+\\|([\n][\n]|[\n]$|$)", 'm')
for (const path of proExchangesPaths) {
logExportExchanges (path, proExchangesRegex, proExchanges)
}
}
if (certifiedExchangesPaths) {
const certified = values (exchanges).filter (exchange => exchange.certified)
const tableData = makeTableData (certified)
// prepend the table header
tableData.splice (0, 0, tableHeadings)
const certifiedExchangesRegex = new RegExp ("^(## Certified Cryptocurrency Exchanges\n{3})(?:\\|.+\\|$\n)+", 'm')
const certifiedExchangesTable = makeTable (tableData)
const certifiedExchanges = '$1' + certifiedExchangesTable + "\n"
for (const path of certifiedExchangesPaths) {
logExportExchanges (path, certifiedExchangesRegex, certifiedExchanges)
}
// logExportExchanges ('README.md', certifiedExchangesRegex, certifiedExchanges)
}
if (exchangesByCountriesPaths) {
let exchangesByCountries = []
keys (countries).forEach (code => {
let country = countries[code]
let result = []
keys (exchanges).forEach (id => {
let exchange = exchanges[id]
let logo = exchange.urls['logo']
let website = Array.isArray (exchange.urls.www) ? exchange.urls.www[0] : exchange.urls.www
let url = exchange.urls.referral || website
let doc = Array.isArray (exchange.urls.doc) ? exchange.urls.doc[0] : exchange.urls.doc
let version = exchange.version ? exchange.version : '\*'
let matches = version.match (/[^0-9]*([0-9].*)/)
if (matches)
version = matches[1];
let shouldInclude = false
if (Array.isArray (exchange.countries)) {
if (exchange.countries.indexOf (code) > -1)
shouldInclude = true
} else {
if (code == exchange.countries)
shouldInclude = true
}
if (shouldInclude) {
let entry = [
country,
'[![' + exchange.id + '](' + logo + ')](' + url + ')',
exchange.id,
'[' + exchange.name + '](' + url + ')',
version,
'[API](' + doc + ')',
// doesn't fit in width
// exchange.certified ? ccxtCertifiedBadge : '',
]
result.push (entry)
}
})
exchangesByCountries = exchangesByCountries.concat (result)
});
const countryKeyIndex = exchangesByCountryHeading.indexOf ('country / region')
exchangesByCountries = exchangesByCountries.sort ((a, b) => {
const countryA = a[countryKeyIndex].toLowerCase ()
const countryB = b[countryKeyIndex].toLowerCase ()
if (countryA > countryB) {
return 1
} else if (countryA < countryB) {
return -1;
} else {
if (code == exchange.countries)
shouldInclude = true
}
if (shouldInclude) {
let entry = [
country,
'[![' + exchange.id + '](' + logo + ')](' + url + ')',
exchange.id,
'[' + exchange.name + '](' + url + ')',
version,
'[API](' + doc + ')',
// doesn't fit in width
// exchange.certified ? ccxtCertifiedBadge : '',
]
result.push (entry)
if (a['id'] > b['id'])
return 1;
else if (a['id'] < b['id'])
return -1;
else
return 0;
}
})
exchangesByCountries = exchangesByCountries.concat (result)
});
let countryKeyIndex = exchangesByCountryHeading.indexOf ('country / region')
exchangesByCountries = exchangesByCountries.sort ((a, b) => {
let countryA = a[countryKeyIndex].toLowerCase ()
let countryB = b[countryKeyIndex].toLowerCase ()
if (countryA > countryB) {
return 1
} else if (countryA < countryB) {
return -1;
} else {
if (a['id'] > b['id'])
return 1;
else if (a['id'] < b['id'])
return -1;
else
return 0;
exchangesByCountries.splice (0, 0, exchangesByCountryHeading)
const lines = makeTable (exchangesByCountries)
const result = "# Exchanges By Country\n\nThe ccxt library currently supports the following cryptocurrency exchange markets and trading APIs:\n\n" + lines + "\n\n"
for (const path of exchangesByCountriesPaths) {
fs.truncateSync (path)
fs.writeFileSync (path, result)
}
})
exchangesByCountries.splice (0, 0, exchangesByCountryHeading)
let lines = makeTable (exchangesByCountries)
let result = "# Exchanges By Country\n\nThe ccxt library currently supports the following cryptocurrency exchange markets and trading APIs:\n\n" + lines + "\n\n"
let filename = wikiPath + '/Exchange-Markets-By-Country.md'
fs.truncateSync (filename)
fs.writeFileSync (filename, result)
}
}
// TODO: REWRITE THIS ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
@@ -237,7 +279,9 @@ function exportSupportedAndCertifiedExchanges (exchanges, wikiPath) {
function exportExchangeIdsToExchangesJson (exchanges) {
log.bright ('Exporting exchange ids to'.cyan, 'exchanges.json'.yellow)
fs.writeFileSync ('exchanges.json', JSON.stringify ({ ids: keys (exchanges) }, null, 4))
const ids = keys (exchanges)
console.log (ids)
fs.writeFileSync ('exchanges.json', JSON.stringify ({ ids }, null, 4))
}
// ----------------------------------------------------------------------------
@@ -252,6 +296,9 @@ function exportWikiToGitHub (wikiPath, gitWikiPath) {
'Manual.md': 'Manual.md',
'Exchange-Markets.md': 'Exchange-Markets.md',
'Exchange-Markets-By-Country.md': 'Exchange-Markets-By-Country.md',
'ccxt.pro.md': 'ccxt.pro.md',
'ccxt.pro.install.md': 'ccxt.pro.install.md',
'ccxt.pro.manual.md': 'ccxt.pro.manual.md',
}
for (const [ sourceFile, destinationFile ] of entries (ccxtWikiFiles)) {
@@ -281,18 +328,13 @@ function exportKeywordsToPackageJson (exchanges) {
}
packageJSON.keywords = [...keywords]
fs.writeFileSync ('./package.json', JSON.stringify (packageJSON, null, 2))
fs.writeFileSync ('./package.json', JSON.stringify (packageJSON, null, 2) + "\n")
}
// ----------------------------------------------------------------------------
function exportEverything () {
const wikiPath = 'wiki'
, gitWikiPath = 'build/ccxt.wiki'
cloneGitHubWiki (gitWikiPath)
const ids = getIncludedExchangeIds ()
const replacements = [
@@ -333,7 +375,28 @@ function exportEverything () {
// strategically placed exactly here (we can require it AFTER the export)
const exchanges = createExchanges (ids)
exportSupportedAndCertifiedExchanges (exchanges, wikiPath)
const wikiPath = 'wiki'
, gitWikiPath = 'build/ccxt.wiki'
cloneGitHubWiki (gitWikiPath)
exportSupportedAndCertifiedExchanges (exchanges, {
allExchangesPaths: [
'README.md',
wikiPath + '/Manual.md',
wikiPath + '/Exchange-Markets.md'
],
certifiedExchangesPaths: [
'README.md',
],
exchangesByCountriesPaths: [
wikiPath + '/Exchange-Markets-By-Country.md'
],
proExchangesPaths: [
wikiPath + '/ccxt.pro.manual.md',
],
})
exportExchangeIdsToExchangesJson (exchanges)
exportWikiToGitHub (wikiPath, gitWikiPath)
exportKeywordsToPackageJson (exchanges)
+155 -38
View File
@@ -5,16 +5,23 @@
"use strict";
const fs = require ('fs')
, log = require ('ololog')
, log = require ('ololog').unlimited
, _ = require ('ansicolor').nice
, errors = require ('../js/base/errors.js')
, { unCamelCase, precisionConstants, safeString } = require ('../js/base/functions.js')
, functions = require ('../js/base/functions.js')
, {
unCamelCase,
precisionConstants,
safeString,
unique,
} = functions
, { basename } = require ('path')
, {
createFolderRecursively,
replaceInFile,
overwriteFile,
} = require ('./fs.js')
, Exchange = require ('../js/base/Exchange.js')
class Transpiler {
@@ -60,6 +67,7 @@ class Transpiler {
[ /\.parseTrades\s/g, '.parse_trades'],
[ /\.parseTrade\s/g, '.parse_trade'],
[ /\.parseTradingViewOHLCV\s/g, '.parse_trading_view_ohlcv'],
[ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'],
[ /\.parseTransaction\s/g, '.parse_transaction'],
[ /\.parseTransactions\s/g, '.parse_transactions'],
[ /\.parseOrderBook\s/g, '.parse_order_book'],
@@ -299,7 +307,9 @@ class Transpiler {
[ /this\.stringToBase64\s/g, 'base64_encode' ],
[ /this\.binaryToBase16\s/g, 'bin2hex' ],
[ /this\.base64ToBinary\s/g, 'base64_decode' ],
[ /this\.deepExtend\s/g, 'array_replace_recursive'],
// deepExtend is commented for PHP because it does not overwrite linear arrays
// a proper \ccxt\Exchange::deep_extend() base method is implemented instead
// [ /this\.deepExtend\s/g, 'array_replace_recursive'],
[ /(\w+)\.shift\s*\(\)/g, 'array_shift($1)' ],
[ /(\w+)\.pop\s*\(\)/g, 'array_pop($1)' ],
[ /Number\.MAX_SAFE_INTEGER/g, 'PHP_INT_MAX' ],
@@ -382,12 +392,27 @@ class Transpiler {
])
}
getBaseClass () {
return new Exchange ()
}
getBaseMethods () {
const baseExchange = this.getBaseClass ()
let object = baseExchange
let properties = []
while (object !== Object.prototype) {
properties = properties.concat (Object.getOwnPropertyNames (object))
object = Object.getPrototypeOf (object)
}
return properties.filter (x => typeof baseExchange[x] === 'function')
}
getPythonBaseMethods () {
return []
return this.getBaseMethods ()
}
getPHPBaseMethods () {
return []
return this.getBaseMethods ()
}
//-------------------------------------------------------------------------
@@ -589,10 +614,10 @@ class Transpiler {
methods = methods.concat (this.getPHPBaseMethods ())
for (let method of methods) {
const regex = new RegExp ('\\$this->(' + method + ')(\\s\\(|[^a-zA-Z0-9_])', 'g')
const regex = new RegExp ('\\$this->(' + method + ')\\s?(\\(|[^a-zA-Z0-9_])', 'g')
bodyAsString = bodyAsString.replace (regex,
(match, p1, p2) => {
return ((p2 === ' (') ?
return ((p2 === '(') ?
('$this->' + unCamelCase (p1) + p2) : // support direct php calls
("array($this, '" + unCamelCase (p1) + "')" + p2)) // as well as passing instance methods as callables
})
@@ -748,7 +773,7 @@ class Transpiler {
// ------------------------------------------------------------------------
transpileDerivedExchangeClass (contents) {
transpileDerivedExchangeClass (contents, methodNames = undefined) {
let exchangeClassDeclarationMatches = contents.match (/^module\.exports\s*=\s*class\s+([\S]+)\s+extends\s+([\S]+)\s+{([\s\S]+?)^};*/m)
@@ -763,7 +788,7 @@ class Transpiler {
let python3 = []
let php = []
let methodNames = []
methodNames = [] // methodNames || []
// run through all methods
for (let i = 0; i < methods.length; i++) {
@@ -839,7 +864,7 @@ class Transpiler {
// compile signature + body for PHP
php.push ('');
php.push (' public function ' + method + ' (' + phpArgs + ') {');
php.push (' public function ' + method + '(' + phpArgs + ') {');
php.push (phpBody);
php.push (' }')
@@ -866,8 +891,25 @@ class Transpiler {
try {
const { python2Folder, python3Folder, phpFolder } = options
const contents = fs.readFileSync (jsFolder + filename, 'utf8')
const { python2, python3, php, className, baseClass } = this.transpileDerivedExchangeClass (contents)
const path = jsFolder + filename
const contents = fs.readFileSync (path, 'utf8')
// function getMethodNames (object) {
// let functions = []
// let o = object
// do {
// functions = functions.concat (Object.getOwnPropertyNames (o))
// } while (o = Object.getPrototypeOf (o))
// return unique (functions.filter (f => (typeof object[f] === 'function')))
// }
// const exchangeClass = require ('.' + path)
// const exchange = new exchangeClass ()
// const methodNames = getMethodNames (exchange)
// const { python2, python3, php, className, baseClass } =
// this.transpileDerivedExchangeClass (contents, methodNames)
const { python2, python3, php, className, baseClass } =
this.transpileDerivedExchangeClass (contents)
log.cyan ('Transpiling from', filename.yellow)
@@ -943,11 +985,12 @@ class Transpiler {
// ========================================================================
exportTypeScriptDeclarations (classes) {
exportTypeScriptDeclarations (file, classes) {
const file = './ccxt.d.ts'
const regex = /(?: export class [^\s]+ extends [^\s]+ \{\}[\r]?[\n])+/
const replacement = Object.keys (classes).map (className => {
log.bright.cyan ('Exporting TypeScript declarations →', file.yellow)
const regex = /\/[\n]{2}(?: export class [^\s]+ extends [^\s]+ \{\}[\r]?[\n])+/
const replacement = "/\n\n" + Object.keys (classes).map (className => {
const baseClass = classes[className]
return ' export class ' + className + ' extends ' + baseClass + " {}"
}).join ("\n") + "\n"
@@ -968,34 +1011,91 @@ class Transpiler {
[ /module\.exports = [^\;]+\;\n/s, '' ],
]).trim ()
const message = 'Transpiling error hierachy →'
const root = errorHierarchy['BaseError']
const { python3Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js })
const python3BodyIntellisense = python3Body + '\n\n' + Array.from (function* intellisense (map) {
for (const key in map) {
yield key + ' = Exception'
yield* intellisense (map[key])
// a helper to generate a list of exception class declarations
// properly derived from corresponding parent classes according
// to the error hierarchy
function intellisense (map, parent, generate, classes) {
function* generator(map, parent, generate, classes) {
for (const key in map) {
yield generate (key, parent, classes)
yield* generator (map[key], key, generate, classes)
}
}
} (errorHierarchy['BaseError'])).join ('\n')
const message = 'Transpiling error hierachy →'
const python = {
filename: './python/ccxt/base/errors.py',
regex: /error_hierarchy = [\S\s]+= Exception/s,
replacement: python3BodyIntellisense,
return Array.from (generator (map, parent, generate, classes))
}
log.bright.cyan (message, python.filename.yellow)
replaceInFile (... Object.values (python))
// Python -------------------------------------------------------------
const php = {
filename:'./php/base/errors.php',
regex: /\$error_hierarchy = .+?\n\)\;/s,
replacement: phpBody,
function pythonDeclareErrorClass (name, parent, classes) {
classes.push (name)
return [
'class ' + name + '(' + parent + '):',
' pass',
'',
'',
].join ('\n');
}
log.bright.cyan (message, php.filename.yellow)
replaceInFile (... Object.values (php))
const pythonBaseError = [
'class BaseError(Exception):',
' pass',
'',
'',
].join ('\n');
const quote = (s) => "'" + s + "'" // helper to add quotes around class names
const pythonExports = [ 'error_hierarchy' ]
const pythonErrors = intellisense (root, 'BaseError', pythonDeclareErrorClass, pythonExports)
const pythonAll = '__all__ = [\n ' + pythonExports.map (quote).join (',\n ') + '\n]'
const python3BodyIntellisense = python3Body + '\n\n\n' + pythonBaseError + '\n' + pythonErrors.join ('\n') + '\n' + pythonAll + '\n'
const pythonFilename = './python/ccxt/base/errors.py'
log.bright.cyan (message, pythonFilename.yellow)
fs.writeFileSync (pythonFilename, python3BodyIntellisense)
// PHP ----------------------------------------------------------------
function phpDeclareErrorClass (name, parent) {
return 'class ' + name + ' extends ' + parent + ' {};'
}
const phpHeader = '<?php\n\nnamespace ccxt;\n\nuse Exception;\n\n'
const phpBaseError = 'class BaseError extends Exception {};'
const phpErrors = intellisense (root, 'BaseError', phpDeclareErrorClass)
const phpBodyIntellisense = phpHeader + phpBody + '\n\n' + phpBaseError + '\n' + phpErrors.join ('\n')
const phpFilename = './php/base/errors.php'
log.bright.cyan (message, phpFilename.yellow)
fs.writeFileSync (phpFilename, phpBodyIntellisense)
// TypeScript ---------------------------------------------------------
function tsDeclareErrorClass (name, parent) {
return 'export class ' + name + ' extends ' + parent + ' {}'
}
const tsBaseError = [
'export class BaseError extends Error {',
' constructor(message: string);',
'}',
].join ('\n ')
const tsErrors = intellisense (root, 'BaseError', tsDeclareErrorClass)
const tsBodyIntellisense = tsBaseError + '\n\n ' + tsErrors.join ('\n ') + '\n\n'
const tsFilename = './ccxt.d.ts'
log.bright.cyan (message, tsFilename.yellow)
const regex = /export class BaseError[^}]+\}[\n][\n](?:\s+export class [a-zA-Z]+ extends [a-zA-Z]+ \{\}[\n])+[\n]/m
replaceInFile (tsFilename, regex, tsBodyIntellisense)
}
//-----------------------------------------------------------------------------
@@ -1070,8 +1170,15 @@ class Transpiler {
"from ccxt.base.decimal_to_precision import PAD_WITH_ZERO # noqa F401",
"from ccxt.base.decimal_to_precision import NO_PADDING # noqa F401",
"from ccxt.base.decimal_to_precision import number_to_string # noqa F401",
"from ccxt.base.exchange import Exchange # noqa F401",
"",
"# ----------------------------------------------------------------------------",
"",
"def toWei(amount, decimals):",
" return Exchange.to_wei(amount, decimals)",
"",
"",
"def fromWei(amount, decimals):",
" return Exchange.from_wei(amount, decimals)",
"",
"",
].join ("\n")
@@ -1095,6 +1202,12 @@ class Transpiler {
"function number_to_string ($x) {",
" return Exchange::number_to_string ($x);",
"}",
"function toWei ($amount, $decimals) {",
" return Exchange::to_wei ($amount, $decimals);",
"}",
"function fromWei ($amount, $decimals) {",
" return Exchange::from_wei ($amount, $decimals);",
"}",
"",
].join ("\n")
@@ -1190,6 +1303,8 @@ class Transpiler {
createFolderRecursively (python3Folder)
createFolderRecursively (phpFolder)
//*
const classes = this.transpileDerivedExchangeFiles ('./js/', options, pattern)
if (classes === null) {
@@ -1199,7 +1314,9 @@ class Transpiler {
// HINT: if we're going to support specific class definitions
// this process won't work anymore as it will override the definitions
this.exportTypeScriptDeclarations (classes)
this.exportTypeScriptDeclarations ('./ccxt.d.ts', classes)
//*/
this.transpileErrorHierarchy ()
+7 -8
View File
@@ -35,7 +35,7 @@ const Exchange = require ('./js/base/Exchange')
//-----------------------------------------------------------------------------
// this is updated by vss.js when building
const version = '1.22.39'
const version = '1.25.80'
Exchange.ccxtVersion = version
@@ -46,6 +46,7 @@ const exchanges = {
'acx': require ('./js/acx.js'),
'adara': require ('./js/adara.js'),
'anxpro': require ('./js/anxpro.js'),
'aofex': require ('./js/aofex.js'),
'bcex': require ('./js/bcex.js'),
'bequant': require ('./js/bequant.js'),
'bibox': require ('./js/bibox.js'),
@@ -62,7 +63,6 @@ const exchanges = {
'bitforex': require ('./js/bitforex.js'),
'bithumb': require ('./js/bithumb.js'),
'bitkk': require ('./js/bitkk.js'),
'bitlish': require ('./js/bitlish.js'),
'bitmart': require ('./js/bitmart.js'),
'bitmax': require ('./js/bitmax.js'),
'bitmex': require ('./js/bitmex.js'),
@@ -76,17 +76,16 @@ const exchanges = {
'braziliex': require ('./js/braziliex.js'),
'btcalpha': require ('./js/btcalpha.js'),
'btcbox': require ('./js/btcbox.js'),
'btcchina': require ('./js/btcchina.js'),
'btcmarkets': require ('./js/btcmarkets.js'),
'btctradeim': require ('./js/btctradeim.js'),
'btctradeua': require ('./js/btctradeua.js'),
'btcturk': require ('./js/btcturk.js'),
'buda': require ('./js/buda.js'),
'bw': require ('./js/bw.js'),
'bybit': require ('./js/bybit.js'),
'bytetrade': require ('./js/bytetrade.js'),
'cex': require ('./js/cex.js'),
'chilebit': require ('./js/chilebit.js'),
'cobinhood': require ('./js/cobinhood.js'),
'coinbase': require ('./js/coinbase.js'),
'coinbaseprime': require ('./js/coinbaseprime.js'),
'coinbasepro': require ('./js/coinbasepro.js'),
@@ -118,6 +117,7 @@ const exchanges = {
'gemini': require ('./js/gemini.js'),
'hitbtc': require ('./js/hitbtc.js'),
'hitbtc2': require ('./js/hitbtc2.js'),
'hollaex': require ('./js/hollaex.js'),
'huobipro': require ('./js/huobipro.js'),
'huobiru': require ('./js/huobiru.js'),
'ice3x': require ('./js/ice3x.js'),
@@ -139,10 +139,8 @@ const exchanges = {
'mercado': require ('./js/mercado.js'),
'mixcoins': require ('./js/mixcoins.js'),
'oceanex': require ('./js/oceanex.js'),
'okcoincny': require ('./js/okcoincny.js'),
'okcoinusd': require ('./js/okcoinusd.js'),
'okcoin': require ('./js/okcoin.js'),
'okex': require ('./js/okex.js'),
'okex3': require ('./js/okex3.js'),
'paymium': require ('./js/paymium.js'),
'poloniex': require ('./js/poloniex.js'),
'rightbtc': require ('./js/rightbtc.js'),
@@ -155,6 +153,7 @@ const exchanges = {
'tidebit': require ('./js/tidebit.js'),
'tidex': require ('./js/tidex.js'),
'timex': require ('./js/timex.js'),
'topq': require ('./js/topq.js'),
'upbit': require ('./js/upbit.js'),
'vaultoro': require ('./js/vaultoro.js'),
'vbtc': require ('./js/vbtc.js'),
@@ -167,6 +166,6 @@ const exchanges = {
//-----------------------------------------------------------------------------
module.exports = Object.assign ({ version, Exchange, exchanges: Object.keys (exchanges) }, exchanges, functions, errors)
module.exports = Object.assign ({ version, Exchange, 'exchanges': Object.keys (exchanges) }, exchanges, functions, errors)
//-----------------------------------------------------------------------------
+9859 -5846
View File
File diff suppressed because it is too large. Load diff
+7
View File
@@ -91,6 +91,7 @@ module.exports = class _1btcxe extends Exchange {
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostBalancesAndInfo (params);
const balance = response['balances-and-info'];
const result = { 'info': balance };
@@ -108,6 +109,7 @@ module.exports = class _1btcxe extends Exchange {
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'currency': this.marketId (symbol),
};
@@ -116,6 +118,7 @@ module.exports = class _1btcxe extends Exchange {
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const request = {
'currency': this.marketId (symbol),
};
@@ -158,6 +161,7 @@ module.exports = class _1btcxe extends Exchange {
}
async fetchOHLCV (symbol, timeframe = '1d', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const response = await this.publicGetHistoricalPrices (this.extend ({
'currency': market['id'],
@@ -202,6 +206,7 @@ module.exports = class _1btcxe extends Exchange {
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'currency': market['id'],
@@ -215,6 +220,7 @@ module.exports = class _1btcxe extends Exchange {
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'side': side,
'type': type,
@@ -232,6 +238,7 @@ module.exports = class _1btcxe extends Exchange {
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'id': id,
};
+8
View File
@@ -92,6 +92,9 @@ module.exports = class acx extends Exchange {
'withdraw': {}, // There is only 1% fee on withdrawals to your bank account.
},
},
'commonCurrencies': {
'PLA': 'Plair',
},
'exceptions': {
'2002': InsufficientFunds,
'2003': OrderNotFound,
@@ -131,6 +134,8 @@ module.exports = class acx extends Exchange {
'quoteId': quoteId,
'precision': precision,
'info': market,
'active': undefined,
'limits': this.limits,
});
}
return result;
@@ -322,6 +327,7 @@ module.exports = class acx extends Exchange {
const id = this.safeString (order, 'id');
return {
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -336,6 +342,8 @@ module.exports = class acx extends Exchange {
'trades': undefined,
'fee': undefined,
'info': order,
'cost': undefined,
'average': undefined,
};
}
+2 -1
View File
@@ -18,7 +18,7 @@ module.exports = class adara extends Exchange {
'certified': false,
// new metainfo interface
'has': {
'CORS': true,
'CORS': false,
'fetchCurrencies': true,
'fetchOrderBooks': false,
'createMarketOrder': false,
@@ -970,6 +970,7 @@ module.exports = class adara extends Exchange {
const result = {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
+5
View File
@@ -469,6 +469,7 @@ module.exports = class anxpro extends Exchange {
'cost': cost,
'fee': undefined,
'info': trade,
'takerOrMaker': undefined,
};
}
@@ -974,6 +975,7 @@ module.exports = class anxpro extends Exchange {
const cost = executedAverageRate * filled;
return {
'id': this.safeString (order, 'orderId'),
'clientOrderId': undefined,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -989,6 +991,7 @@ module.exports = class anxpro extends Exchange {
'fee': undefined,
'trades': trades,
'info': order,
'average': undefined,
};
}
@@ -1066,6 +1069,7 @@ module.exports = class anxpro extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -1080,6 +1084,7 @@ module.exports = class anxpro extends Exchange {
'status': status,
'fee': fee,
'trades': trades,
'average': undefined,
};
}
+1119
View File
File diff suppressed because it is too large. Load diff
+118 -166
View File
@@ -48,17 +48,13 @@ const BN = require ('../static_dependencies/BN/bn')
// ----------------------------------------------------------------------------
// web3 / 0x imports
let Web3 = undefined
, ethAbi = undefined
let ethAbi = undefined
, ethUtil = undefined
, BigNumber = undefined
try {
const requireFunction = require;
Web3 = requireFunction ('web3') // eslint-disable-line global-require
ethAbi = requireFunction ('ethereumjs-abi') // eslint-disable-line global-require
ethUtil = requireFunction ('ethereumjs-util') // eslint-disable-line global-require
BigNumber = requireFunction ('bignumber.js') // eslint-disable-line global-require
// we prefer bignumber.js over BN.js
// BN = requireFunction ('bn.js') // eslint-disable-line global-require
} catch (e) {
@@ -77,6 +73,7 @@ module.exports = class Exchange {
'enableRateLimit': false,
'rateLimit': 2000, // milliseconds = seconds * 1000
'certified': false,
'pro': false,
'has': {
'cancelAllOrders': false,
'cancelOrder': true,
@@ -169,6 +166,7 @@ module.exports = class Exchange {
'429': RateLimitExceeded,
'404': ExchangeNotAvailable,
'409': ExchangeNotAvailable,
'410': ExchangeNotAvailable,
'500': ExchangeNotAvailable,
'501': ExchangeNotAvailable,
'502': ExchangeNotAvailable,
@@ -276,8 +274,6 @@ module.exports = class Exchange {
this.last_json_response = undefined
this.last_response_headers = undefined
this.arrayConcat = (a, b) => a.concat (b)
const unCamelCaseProperties = (obj = this) => {
if (obj !== null) {
for (const k of Object.getOwnPropertyNames (obj)) {
@@ -315,11 +311,6 @@ module.exports = class Exchange {
if (this.markets)
this.setMarkets (this.markets)
if (this.requiresWeb3 && !this.web3 && Web3) {
const provider = (this.web3ProviderURL) ? new Web3.providers.HttpProvider (this.web3ProviderURL) : new Web3.providers.HttpProvider ()
this.web3 = new Web3 (Web3.givenProvider || provider)
}
}
defaults () {
@@ -367,7 +358,6 @@ module.exports = class Exchange {
throw new Error (this.id + '.rateLimit property is not configured')
this.tokenBucket = this.extend ({
refillRate: 1 / this.rateLimit,
delay: 1,
capacity: 1,
defaultCost: 1,
@@ -472,42 +462,50 @@ module.exports = class Exchange {
}
}
print (... args) {
console.log (... args)
}
fetch (url, method = 'GET', headers = undefined, body = undefined) {
if (isNode && this.userAgent) {
if (typeof this.userAgent === 'string')
if (typeof this.userAgent === 'string') {
headers = extend ({ 'User-Agent': this.userAgent }, headers)
else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent))
} else if ((typeof this.userAgent === 'object') && ('User-Agent' in this.userAgent)) {
headers = extend (this.userAgent, headers)
}
}
if (typeof this.proxy === 'function') {
url = this.proxy (url)
if (isNode)
if (isNode) {
headers = extend ({ 'Origin': this.origin }, headers)
}
} else if (typeof this.proxy === 'string') {
if (this.proxy.length)
if (isNode)
headers = extend ({ 'Origin': this.origin }, headers)
if (this.proxy.length && isNode) {
headers = extend ({ 'Origin': this.origin }, headers)
}
url = this.proxy + url
}
headers = extend (this.headers, headers)
if (this.verbose)
console.log ("fetch:\n", this.id, method, url, "\nRequest:\n", headers, "\n", body, "\n")
if (this.verbose) {
this.print ("fetch:\n", this.id, method, url, "\nRequest:\n", headers, "\n", body, "\n")
}
return this.executeRestRequest (url, method, headers, body)
}
async fetch2 (path, type = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
if (this.enableRateLimit)
await this.throttle ()
if (this.enableRateLimit) {
await this.throttle (this.rateLimit)
}
const request = this.sign (path, type, method, params, headers, body)
return this.fetch (request.url, request.method, request.headers, request.body)
@@ -622,8 +620,9 @@ module.exports = class Exchange {
this.last_json_response = json // FIXME: for those classes that haven't switched to handleErrors yet
}
if (this.verbose)
console.log ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n")
if (this.verbose) {
this.print ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n")
}
this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody)
this.defaultErrorHandler (response.status, response.statusText, url, method, responseHeaders, responseBody, json)
@@ -645,7 +644,7 @@ module.exports = class Exchange {
if (currencies) {
this.currencies = deepExtend (currencies, this.currencies)
} else {
const baseCurrencies =
let baseCurrencies =
values.filter (market => 'base' in market)
.map (market => ({
id: market.baseId || market.base,
@@ -653,7 +652,7 @@ module.exports = class Exchange {
code: market.base,
precision: market.precision ? (market.precision.base || market.precision.amount) : 8,
}))
const quoteCurrencies =
let quoteCurrencies =
values.filter (market => 'quote' in market)
.map (market => ({
id: market.quoteId || market.quote,
@@ -661,6 +660,10 @@ module.exports = class Exchange {
code: market.quote,
precision: market.precision ? (market.precision.quote || market.precision.price) : 8,
}))
baseCurrencies = sortBy (baseCurrencies, 'code')
quoteCurrencies = sortBy (quoteCurrencies, 'code')
this.baseCurrencies = indexBy (baseCurrencies, 'code')
this.quoteCurrencies = indexBy (quoteCurrencies, 'code')
const allCurrencies = baseCurrencies.concat (quoteCurrencies)
const groupedCurrencies = groupBy (allCurrencies, 'code')
const currencies = Object.keys (groupedCurrencies).map (code =>
@@ -673,7 +676,7 @@ module.exports = class Exchange {
return this.markets
}
async loadMarkets (reload = false, params = {}) {
async loadMarketsHelper (reload = false, params = {}) {
if (!reload && this.markets) {
if (!this.markets_by_id) {
return this.setMarkets (this.markets)
@@ -688,6 +691,21 @@ module.exports = class Exchange {
return this.setMarkets (markets, currencies)
}
// is async (returns a promise)
loadMarkets (reload = false, params = {}) {
if ((reload && !this.reloadingMarkets) || !this.marketsLoading) {
this.reloadingMarkets = true
this.marketsLoading = this.loadMarketsHelper (reload, params).then ((resolved) => {
this.reloadingMarkets = false
return resolved
}, (error) => {
this.reloadingMarkets = false
throw error
})
}
return this.marketsLoading
}
async loadAccounts (reload = false, params = {}) {
if (reload) {
this.accounts = await this.fetchAccounts (params)
@@ -725,43 +743,42 @@ module.exports = class Exchange {
}
parseTradingViewOHLCV (ohlcvs, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
const result = this.convertTradingViewToOHLCV (ohlcvs);
return this.parseOHLCVs (result, market, timeframe, since, limit);
const result = this.convertTradingViewToOHLCV (ohlcvs)
return this.parseOHLCVs (result, market, timeframe, since, limit)
}
convertTradingViewToOHLCV (ohlcvs) {
convertTradingViewToOHLCV (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) {
const result = [];
for (let i = 0; i < ohlcvs['t'].length; i++) {
for (let i = 0; i < ohlcvs[t].length; i++) {
result.push ([
ohlcvs['t'][i] * 1000,
ohlcvs['o'][i],
ohlcvs['h'][i],
ohlcvs['l'][i],
ohlcvs['c'][i],
ohlcvs['v'][i],
]);
ms ? ohlcvs[t][i] : (ohlcvs[t][i] * 1000),
ohlcvs[o][i],
ohlcvs[h][i],
ohlcvs[l][i],
ohlcvs[c][i],
ohlcvs[v][i],
])
}
return result;
return result
}
convertOHLCVToTradingView (ohlcvs) {
const result = {
't': [],
'o': [],
'h': [],
'l': [],
'c': [],
'v': [],
};
convertOHLCVToTradingView (ohlcvs, t = 't', o = 'o', h = 'h', l = 'l', c = 'c', v = 'v', ms = false) {
const result = {}
result[t] = []
result[o] = []
result[h] = []
result[l] = []
result[c] = []
result[v] = []
for (let i = 0; i < ohlcvs.length; i++) {
result['t'].push (parseInt (ohlcvs[i][0] / 1000));
result['o'].push (ohlcvs[i][1]);
result['h'].push (ohlcvs[i][2]);
result['l'].push (ohlcvs[i][3]);
result['c'].push (ohlcvs[i][4]);
result['v'].push (ohlcvs[i][5]);
result[t].push (ms ? ohlcvs[i][0] : parseInt (ohlcvs[i][0] / 1000))
result[o].push (ohlcvs[i][1])
result[h].push (ohlcvs[i][2])
result[l].push (ohlcvs[i][3])
result[c].push (ohlcvs[i][4])
result[v].push (ohlcvs[i][5])
}
return result;
return result
}
fetchTicker (symbol, params = {}) {
@@ -993,8 +1010,8 @@ module.exports = class Exchange {
async fetchStatus (params = {}) {
if (this.has['fetchTime']) {
const time = await this.fetchTime(params)
return this.status = this.extend(this.status, {
const time = await this.fetchTime (params)
this.status = this.extend (this.status, {
'updated': time,
})
}
@@ -1026,27 +1043,34 @@ module.exports = class Exchange {
return this.markets;
}
filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp') {
if (since !== undefined && since !== null)
array = array.filter (entry => entry[key] >= since)
if (limit !== undefined && limit !== null)
array = array.slice (0, limit)
filterBySinceLimit (array, since = undefined, limit = undefined, key = 'timestamp', tail = false) {
const sinceIsDefined = (since !== undefined && since !== null)
if (sinceIsDefined) {
array = array.filter ((entry) => entry[key] >= since)
}
if (limit !== undefined && limit !== null) {
array = (tail && !sinceIsDefined) ? array.slice (-limit) : array.slice (0, limit)
}
return array
}
filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined) {
filterByValueSinceLimit (array, field, value = undefined, since = undefined, limit = undefined, key = 'timestamp', tail = false) {
const valueIsDefined = value !== undefined && value !== null
const sinceIsDefined = since !== undefined && since !== null
// single-pass filter for both symbol and since
if (valueIsDefined || sinceIsDefined)
array = Object.values (array).filter (entry =>
((valueIsDefined ? (entry[field] === value) : true) &&
(sinceIsDefined ? (entry.timestamp >= since) : true)))
if (valueIsDefined || sinceIsDefined) {
array = Object.values (array).filter ((entry) =>
((valueIsDefined ? (entry[field] === value) : true) &&
(sinceIsDefined ? (entry[key] >= since) : true)))
}
if (limit !== undefined && limit !== null)
array = Object.values (array).slice (0, limit)
if (limit !== undefined && limit !== null) {
array = ((tail && !sinceIsDefined) ?
Object.values (array).slice (-limit) :
Object.values (array).slice (0, limit))
}
return array
}
@@ -1064,13 +1088,15 @@ module.exports = class Exchange {
objects = Object.values (objects)
// return all of them if no values were passed
if (values === undefined || values === null)
if (values === undefined || values === null) {
return indexed ? indexBy (objects, key) : objects
}
const result = []
for (let i = 0; i < objects.length; i++) {
if (values.includes (objects[i][key]))
if (values.includes (objects[i][key])) {
result.push (objects[i])
}
}
return indexed ? indexBy (result, key) : result
@@ -1081,7 +1107,7 @@ module.exports = class Exchange {
// if (!this.isArray (trades)) {
// throw new ExchangeError (this.id + ' parseTrades expected an array in the trades argument, but got ' + typeof trades);
// }
let result = Object.values (trades || []).map (trade => this.extend (this.parseTrade (trade, market), params))
let result = Object.values (trades || []).map ((trade) => this.extend (this.parseTrade (trade, market), params))
result = sortBy (result, 'timestamp')
const symbol = (market !== undefined) ? market['symbol'] : undefined
return this.filterBySymbolSinceLimit (result, symbol, since, limit)
@@ -1092,7 +1118,7 @@ module.exports = class Exchange {
// if (!this.isArray (transactions)) {
// throw new ExchangeError (this.id + ' parseTransactions expected an array in the transactions argument, but got ' + typeof transactions);
// }
let result = Object.values (transactions || []).map (transaction => this.extend (this.parseTransaction (transaction, currency), params))
let result = Object.values (transactions || []).map ((transaction) => this.extend (this.parseTransaction (transaction, currency), params))
result = this.sortBy (result, 'timestamp');
const code = (currency !== undefined) ? currency['code'] : undefined;
return this.filterByCurrencySinceLimit (result, code, since, limit);
@@ -1248,97 +1274,32 @@ module.exports = class Exchange {
// ------------------------------------------------------------------------
// web3 / 0x methods
static hasWeb3 () {
return Web3 && ethUtil && ethAbi && BigNumber
return ethUtil && ethAbi
}
checkRequiredDependencies () {
if (!Exchange.hasWeb3 ()) {
throw new ExchangeError ("Required dependencies missing: \nnpm i web3 ethereumjs-util ethereumjs-abi bignumber.js --no-save");
throw new ExchangeError ('Required dependencies missing: \nnpm i ethereumjs-util ethereumjs-abi --no-save');
}
}
ethDecimals (unit = 'ether') {
const units = {
'wei': 0, // 1
'kwei': 3, // 1000
'babbage': 3, // 1000
'femtoether': 3, // 1000
'mwei': 6, // 1000000
'lovelace': 6, // 1000000
'picoether': 6, // 1000000
'gwei': 9, // 1000000000
'shannon': 9, // 1000000000
'nanoether': 9, // 1000000000
'nano': 9, // 1000000000
'szabo': 12, // 1000000000000
'microether': 12, // 1000000000000
'micro': 12, // 1000000000000
'finney': 15, // 1000000000000000
'milliether': 15, // 1000000000000000
'milli': 15, // 1000000000000000
'ether': 18, // 1000000000000000000
'kether': 21, // 1000000000000000000000
'grand': 21, // 1000000000000000000000
'mether': 24, // 1000000000000000000000000
'gether': 27, // 1000000000000000000000000000
'tether': 30, // 1000000000000000000000000000000
}
return this.safeValue (units, unit)
}
ethUnit (decimals = 18) {
const units = {
0: 'wei', // 1000000000000000000
3: 'kwei', // 1000000000000000
6: 'mwei', // 1000000000000
9: 'gwei', // 1000000000
12: 'szabo', // 1000000
15: 'finney', // 1000
18: 'ether', // 1
21: 'kether', // 0.001
24: 'mether', // 0.000001
27: 'gether', // 0.000000001
30: 'tether', // 0.000000000001
}
return this.safeValue (units, decimals)
}
fromWei (amount, unit = 'ether', decimals = 18) {
if (amount === undefined) {
return amount
}
if (decimals !== 18) {
amount = new BigNumber (amount).times (new BigNumber (10 ** (18 - decimals))).toFixed ()
} else {
amount = new BigNumber (amount).toFixed ()
}
return parseFloat (this.web3.utils.fromWei (amount, unit))
}
toWei (amount, unit = 'ether', decimals = 18) {
if (amount === undefined) {
return amount
}
if (decimals !== 18) {
amount = new BigNumber (this.numberToString (amount)).div (new BigNumber (10 ** (18 - decimals))).toFixed ()
} else {
amount = this.numberToString (amount)
}
return this.web3.utils.toWei (amount, unit)
}
soliditySha3 (array) {
const values = this.solidityValues (array);
const types = this.solidityTypes (values);
return '0x' + ethAbi.soliditySHA3 (types, values).toString ('hex')
}
solidityTypes (array) {
return array.map (value => (this.web3.utils.isAddress (value) ? 'address' : 'uint256'))
}
solidityValues (array) {
return array.map (value => (this.web3.utils.isAddress (value) ? value : (new BigNumber (value).toFixed ())))
// we only support address, uint256, and string solidity types
const encoded = []
for (const value of array) {
if (Number.isInteger (value) || value.match (/^[0-9]+$/)) {
encoded.push (this.numberToBE (this.numberToString (value), 32))
} else {
const noPrefix = Exchange.remove0xPrefix (value)
if (noPrefix.length === 40 && noPrefix.toLowerCase ().match (/^[0-9a-f]+$/)) { // check if it is an address
encoded.push (this.base16ToBinary (noPrefix))
} else {
encoded.push (this.stringToBinary (noPrefix))
}
}
}
const concated = this.binaryConcatArray (encoded)
return '0x' + this.hash (concated, 'keccak', 'hex')
}
getZeroExOrderHash (order) {
@@ -1424,15 +1385,6 @@ module.exports = class Exchange {
])).toString ('hex');
}
signZeroExOrder (order, privateKey) {
const orderHash = this.getZeroExOrderHash (order);
const signature = this.signMessage (orderHash, privateKey);
return this.extend (order, {
'orderHash': orderHash,
'ecSignature': signature, // todo fix v if needed
})
}
signZeroExOrderV2 (order, privateKey) {
const orderHash = this.getZeroExOrderHashV2 (order);
const signature = this.signMessage (orderHash, privateKey);
+9 -11
View File
@@ -31,6 +31,7 @@ module.exports =
, index
, ordered: x => x // a stub to keep assoc keys in order (in JS it does nothing, it's mostly for Python)
, unique: x => Array.from (index (x))
, arrayConcat: (a, b) => a.concat (b)
/* ............................................. */
@@ -199,7 +200,7 @@ module.exports =
return out
}
return x
}
@@ -217,22 +218,19 @@ module.exports =
/* ............................................. */
, deepExtend: function deepExtend (...xs) {
let out = undefined
for (const x of xs) {
if (isDictionary (x)) {
if (!isObject (out))
if (!isObject (out)) {
out = {}
for (const k in x)
}
for (const k in x) {
out[k] = deepExtend (out[k], x[k])
} else out = x
}
} else {
out = x
}
}
return out
}
+31 -6
View File
@@ -106,9 +106,13 @@ const decimalToPrecision = (x, roundingMode
/* handle tick size */
if (countingMode === TICK_SIZE) {
const missing = x % numPrecisionDigits
const reminder = x / numPrecisionDigits
if (reminder !== Math.floor (reminder)) {
const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING)
const newNumPrecisionDigits = precisionFromString (precisionDigitsString)
let missing = x % numPrecisionDigits
// See: https://github.com/ccxt/ccxt/pull/6486
missing = Number (decimalToPrecision (missing, ROUND, 8, DECIMAL_PLACES, NO_PADDING));
const fpError = decimalToPrecision (missing / numPrecisionDigits, ROUND, Math.max (newNumPrecisionDigits, 8), DECIMAL_PLACES, NO_PADDING)
if (precisionFromString (fpError) !== 0) {
if (roundingMode === ROUND) {
if (x > 0) {
if (missing >= numPrecisionDigits / 2) {
@@ -127,8 +131,6 @@ const decimalToPrecision = (x, roundingMode
x = x - missing
}
}
const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING)
const newNumPrecisionDigits = precisionFromString (precisionDigitsString)
return decimalToPrecision (x, ROUND, newNumPrecisionDigits, DECIMAL_PLACES, paddingMode);
}
@@ -282,10 +284,33 @@ const decimalToPrecision = (x, roundingMode
return String.fromCharCode (...out)
}
// toWei / fromWei
function fromWei (amount, decimals = 18) {
if (amount === undefined) {
return amount
}
const exponential = Math.floor (amount).toExponential () // wei must be whole numbers
const [ n, exponent ] = exponential.split ('e')
const newExponent = parseInt (exponent) - decimals
return parseFloat (n + 'e' + newExponent)
}
function toWei (amount, decimals = 18) {
if (amount === undefined) {
return amount
}
const exponential = parseFloat (amount).toExponential ()
const [ n, exponent ] = exponential.split ('e')
const newExponent = parseInt (exponent) + decimals
return numberToString (Math.floor (parseFloat (n + 'e' + newExponent))) // wei must be whole numbers
}
/* ------------------------------------------------------------------------ */
module.exports = {
toWei,
fromWei,
numberToString,
precisionFromString,
decimalToPrecision,
+2 -2
View File
@@ -18,7 +18,7 @@ module.exports = {
const queue = []
return Object.assign ((cost) => {
return Object.assign ((rateLimit, cost) => {
if (queue.length > cfg.maxCapacity)
throw new Error ('Backlog is over max capacity of ' + cfg.maxCapacity)
@@ -46,7 +46,7 @@ module.exports = {
const t = now ()
, elapsed = t - lastTimestamp
lastTimestamp = t
numTokens = Math.min (cfg.capacity, numTokens + elapsed * cfg.refillRate)
numTokens = Math.min (cfg.capacity, numTokens + elapsed * (1 / rateLimit))
await sleep (cfg.delay)
}
running = false
+2
View File
@@ -3,6 +3,7 @@
/* ------------------------------------------------------------------------ */
const isNumber = Number.isFinite
, isInteger = Number.isInteger
, isArray = Array.isArray
, hasProps = o => ((o !== undefined) && (o !== null))
, isString = s => (typeof s === 'string')
@@ -25,6 +26,7 @@ const asFloat = x => ((isNumber (x) || isString (x)) ? parseFloat (x) : Na
module.exports = {
isNumber
, isInteger
, isArray
, isObject
, isString
+8 -15
View File
@@ -29,7 +29,7 @@ module.exports = class bcex extends Exchange {
'fetchTradingLimits': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg',
'logo': 'https://user-images.githubusercontent.com/51840849/77231516-851c6900-6bac-11ea-8fd6-ee5c23eddbd4.jpg',
'api': 'https://www.bcex.top',
'www': 'https://www.bcex.top',
'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface',
@@ -75,8 +75,8 @@ module.exports = class bcex extends Exchange {
'trading': {
'tierBased': false,
'percentage': true,
'buy': 0.0,
'sell': 0.2 / 100,
'maker': 0.1 / 100,
'taker': 0.2 / 100,
},
'funding': {
'tierBased': false,
@@ -369,6 +369,7 @@ module.exports = class bcex extends Exchange {
'cost': cost,
'order': orderId,
'fee': undefined,
'takerOrMaker': undefined,
};
}
@@ -517,6 +518,8 @@ module.exports = class bcex extends Exchange {
'remaining': this.safeFloat (order, 'numberover'),
'status': status,
'fee': undefined,
'clientOrderId': undefined,
'trades': undefined,
};
}
@@ -543,6 +546,7 @@ module.exports = class bcex extends Exchange {
const result = {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -557,6 +561,7 @@ module.exports = class bcex extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
return result;
}
@@ -671,16 +676,4 @@ module.exports = class bcex extends Exchange {
}
}
}
calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) {
const market = this.markets[symbol];
const rate = market[side];
const cost = parseFloat (this.costToPrecision (symbol, amount * price));
return {
'type': takerOrMaker,
'currency': market['quote'],
'rate': rate,
'cost': parseFloat (this.feeToPrecision (symbol, rate * cost)),
};
}
};
+7 -3
View File
@@ -2,18 +2,22 @@
// ---------------------------------------------------------------------------
const hitbtc2 = require ('./hitbtc2');
const hitbtc = require ('./hitbtc');
// ---------------------------------------------------------------------------
module.exports = class bequant extends hitbtc2 {
module.exports = class bequant extends hitbtc {
describe () {
return this.deepExtend (super.describe (), {
'id': 'bequant',
'name': 'Bequant',
'countries': [ 'MT' ], // Malta
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg',
'api': 'https://api.bequant.io',
'api': {
'public': 'https://api.bequant.io',
'private': 'https://api.bequant.io',
},
'www': 'https://bequant.io',
'doc': [
'https://api.bequant.io/',
+8 -3
View File
@@ -46,14 +46,14 @@ module.exports = class bibox extends Exchange {
'1w': 'week',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/34902611-2be8bf1a-f830-11e7-91a2-11b2f292e750.jpg',
'logo': 'https://user-images.githubusercontent.com/51840849/77257418-3262b000-6c85-11ea-8fb8-20bdf20b3592.jpg',
'api': 'https://api.bibox.com',
'www': 'https://www.bibox.com',
'doc': [
'https://biboxcom.github.io/en/',
],
'fees': 'https://bibox.zendesk.com/hc/en-us/articles/360002336133',
'referral': 'https://www.bibox.com/signPage?id=11114745&lang=en',
'referral': 'https://w2.bibox.com/login/register?invite_code=05Kj3I',
},
'api': {
'public': {
@@ -62,11 +62,14 @@ module.exports = class bibox extends Exchange {
'mdata',
],
'get': [
'cquery',
'mdata',
],
},
'private': {
'post': [
'cquery',
'ctrade',
'user',
'orderpending',
'transfer',
@@ -83,7 +86,7 @@ module.exports = class bibox extends Exchange {
'tierBased': false,
'percentage': true,
'taker': 0.001,
'maker': 0.001,
'maker': 0.0008,
},
'funding': {
'tierBased': false,
@@ -710,6 +713,7 @@ module.exports = class bibox extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -724,6 +728,7 @@ module.exports = class bibox extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
+3 -2
View File
@@ -44,7 +44,7 @@ module.exports = class bigone extends Exchange {
'1w': 'week1',
'1M': 'month1',
},
'hostname': 'big.one', // set to 'b1.run' for China mainland
'hostname': 'big.one', // or 'bigone.com'
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg',
'api': {
@@ -735,6 +735,7 @@ module.exports = class bigone extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -922,7 +923,7 @@ module.exports = class bigone extends Exchange {
// }
//
const trades = this.safeValue (response, 'data', []);
return this.parseTrades (trades, market, since, limit, params);
return this.parseTrades (trades, market, since, limit);
}
parseOrderStatus (status) {
+120 -17
View File
@@ -3,8 +3,8 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress } = require ('./base/errors');
const { ROUND } = require ('./base/functions/number');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied } = require ('./base/errors');
const { ROUND, TRUNCATE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -16,6 +16,7 @@ module.exports = class binance extends Exchange {
'countries': [ 'JP', 'MT' ], // Japan, Malta
'rateLimit': 500,
'certified': true,
'pro': true,
// new metainfo interface
'has': {
'fetchDepositAddress': true,
@@ -120,6 +121,15 @@ module.exports = class binance extends Exchange {
'sub-account/margin/account',
'sub-account/margin/accountSummary',
'sub-account/status',
// lending endpoints
'lending/daily/product/list',
'lending/daily/userLeftQuota',
'lending/daily/userRedemptionQuota',
'lending/daily/token/position',
'lending/union/account',
'lending/union/purchaseRecord',
'lending/union/redemptionRecord',
'lending/union/interestHistory',
],
'post': [
'asset/dust',
@@ -135,6 +145,9 @@ module.exports = class binance extends Exchange {
'sub-account/futures/enable',
'userDataStream',
'futures/transfer',
// lending
'lending/daily/purchase',
'lending/daily/redeem',
],
'put': [
'userDataStream',
@@ -174,18 +187,20 @@ module.exports = class binance extends Exchange {
'historicalTrades',
'aggTrades',
'klines',
'fundingRate',
'premiumIndex',
'ticker/24hr',
'ticker/price',
'ticker/bookTicker',
'allForceOrders',
'openInterest',
'leverageBracket',
],
'put': [ 'listenKey' ],
'post': [ 'listenKey' ],
'delete': [ 'listenKey' ],
},
'fapiPrivate': {
'get': [
'allOrders',
'openOrder',
'openOrders',
'order',
'account',
@@ -200,10 +215,15 @@ module.exports = class binance extends Exchange {
'marginType',
'order',
'leverage',
'listenKey',
],
'put': [
'listenKey',
],
'delete': [
'order',
'allOpenOrders',
'listenKey',
],
},
'v3': {
@@ -266,7 +286,7 @@ module.exports = class binance extends Exchange {
},
// exchange-specific options
'options': {
'fetchTradesMethod': 'publicGetAggTrades',
'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades
'fetchTickersMethod': 'publicGetTicker24hr',
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
'defaultLimitOrderType': 'limit', // or 'limit_maker'
@@ -287,7 +307,10 @@ module.exports = class binance extends Exchange {
'Order would trigger immediately.': InvalidOrder,
'Account has insufficient balance for requested action.': InsufficientFunds,
'Rest API trading is not enabled.': ExchangeNotAvailable,
"You don't have permission.": PermissionDenied, // {"msg":"You don't have permission.","success":false}
'Market is closed.': ExchangeNotAvailable, // {"code":-1013,"msg":"Market is closed."}
'-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."}
'-1003': RateLimitExceeded, // {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."}
'-1013': InvalidOrder, // createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
'-1021': InvalidNonce, // 'your time is ahead of server'
'-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."}
@@ -311,13 +334,13 @@ module.exports = class binance extends Exchange {
const type = this.safeString2 (this.options, 'fetchTime', 'defaultType', 'spot');
const method = (type === 'spot') ? 'publicGetTime' : 'fapiPublicGetTime';
const response = await this[method] (params);
return this.safeFloat (response, 'serverTime');
return this.safeInteger (response, 'serverTime');
}
async loadTimeDifference () {
const serverTime = await this.fetchTime ();
const after = this.milliseconds ();
this.options['timeDifference'] = parseInt (after - serverTime);
this.options['timeDifference'] = after - serverTime;
return this.options['timeDifference'];
}
@@ -414,6 +437,7 @@ module.exports = class binance extends Exchange {
const spot = !future;
const marketType = spot ? 'spot' : 'future';
const id = this.safeString (market, 'symbol');
const lowercaseId = this.safeStringLower (market, 'symbol');
const baseId = market['baseAsset'];
const quoteId = market['quoteAsset'];
const base = this.safeCurrencyCode (baseId);
@@ -430,6 +454,7 @@ module.exports = class binance extends Exchange {
const active = (status === 'TRADING');
const entry = {
'id': id,
'lowercaseId': lowercaseId,
'symbol': symbol,
'base': base,
'quote': quote,
@@ -481,6 +506,13 @@ module.exports = class binance extends Exchange {
'max': this.safeFloat (filter, 'maxQty'),
};
}
if ('MARKET_LOT_SIZE' in filters) {
const filter = this.safeValue (filters, 'MARKET_LOT_SIZE', {});
entry['limits']['market'] = {
'min': this.safeFloat (filter, 'minQty'),
'max': this.safeFloat (filter, 'maxQty'),
};
}
if ('MIN_NOTIONAL' in filters) {
entry['limits']['cost']['min'] = this.safeFloat (filters['MIN_NOTIONAL'], 'minNotional');
}
@@ -655,7 +687,7 @@ module.exports = class binance extends Exchange {
}
async fetchStatus (params = {}) {
const response = await this.wapiGetSystemStatus ();
const response = await this.wapiGetSystemStatus (params);
let status = this.safeValue (response, 'status');
if (status !== undefined) {
status = (status === 0) ? 'ok' : 'maintenance';
@@ -865,13 +897,23 @@ module.exports = class binance extends Exchange {
// 'endTime': 789, // Timestamp in ms to get aggregate trades until INCLUSIVE.
// 'limit': 500, // default = 500, maximum = 1000
};
if (this.options['fetchTradesMethod'] === 'publicGetAggTrades') {
const defaultType = this.safeString2 (this.options, 'fetchTrades', 'defaultType', 'spot');
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
const defaultMethod = (type === 'future') ? 'fapiPublicGetTrades' : 'publicGetTrades';
let method = this.safeString (this.options, 'fetchTradesMethod', defaultMethod);
if (method === 'publicGetAggTrades') {
if (since !== undefined) {
request['startTime'] = since;
// https://github.com/ccxt/ccxt/issues/6400
// https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list
request['endTime'] = this.sum (since, 3600000);
}
if (type === 'future') {
method = 'fapiPublicGetAggTrades';
}
} else if ((method === 'publicGetHistoricalTrades') && (type === 'future')) {
method = 'fapiPublicGetHistoricalTrades';
}
if (limit !== undefined) {
request['limit'] = limit; // default = 500, maximum = 1000
@@ -885,8 +927,7 @@ module.exports = class binance extends Exchange {
// - 'tradeId' accepted and returned by this method is "aggregate" trade id
// which is different from actual trade id
// - setting both fromId and time window results in error
const method = this.safeValue (this.options, 'fetchTradesMethod', 'publicGetTrades');
const response = await this[method] (this.extend (request, params));
const response = await this[method] (this.extend (request, query));
//
// aggregate trades
//
@@ -933,6 +974,46 @@ module.exports = class binance extends Exchange {
}
parseOrder (order, market = undefined) {
//
// spot
//
// {
// "symbol": "LTCBTC",
// "orderId": 1,
// "clientOrderId": "myOrder1",
// "price": "0.1",
// "origQty": "1.0",
// "executedQty": "0.0",
// "cummulativeQuoteQty": "0.0",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "stopPrice": "0.0",
// "icebergQty": "0.0",
// "time": 1499827319559,
// "updateTime": 1499827319559,
// "isWorking": true
// }
//
// futures
//
// {
// "symbol": "BTCUSDT",
// "orderId": 1,
// "clientOrderId": "myOrder1",
// "price": "0.1",
// "origQty": "1.0",
// "executedQty": "1.0",
// "cumQuote": "10.0",
// "status": "NEW",
// "timeInForce": "GTC",
// "type": "LIMIT",
// "side": "BUY",
// "stopPrice": "0.0",
// "updateTime": 1499827319559
// }
//
const status = this.parseOrderStatus (this.safeString (order, 'status'));
let symbol = undefined;
const marketId = this.safeString (order, 'symbol');
@@ -1017,9 +1098,11 @@ module.exports = class binance extends Exchange {
cost = parseFloat (this.costToPrecision (symbol, cost));
}
}
const clientOrderId = this.safeString (order, 'clientOrderId');
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -1062,10 +1145,12 @@ module.exports = class binance extends Exchange {
};
if (uppercaseType === 'MARKET') {
const quoteOrderQty = this.safeFloat (params, 'quoteOrderQty');
const precision = market['precision']['price'];
if (quoteOrderQty !== undefined) {
request['quoteOrderQty'] = this.costToPrecision (symbol, quoteOrderQty);
request['quoteOrderQty'] = this.decimalToPrecision (quoteOrderQty, TRUNCATE, precision, this.precisionMode);
params = this.omit (params, 'quoteOrderQty');
} else if (price !== undefined) {
request['quoteOrderQty'] = this.costToPrecision (symbol, amount * price);
request['quoteOrderQty'] = this.decimalToPrecision (amount * price, TRUNCATE, precision, this.precisionMode);
} else {
request['quantity'] = this.amountToPrecision (symbol, amount);
}
@@ -1083,6 +1168,9 @@ module.exports = class binance extends Exchange {
timeInForceIsRequired = true;
} else if ((uppercaseType === 'STOP_LOSS') || (uppercaseType === 'TAKE_PROFIT')) {
stopPriceIsRequired = true;
if (market['future']) {
priceIsRequired = true;
}
} else if ((uppercaseType === 'STOP_LOSS_LIMIT') || (uppercaseType === 'TAKE_PROFIT_LIMIT')) {
stopPriceIsRequired = true;
priceIsRequired = true;
@@ -1234,11 +1322,18 @@ module.exports = class binance extends Exchange {
}
await this.loadMarkets ();
const market = this.market (symbol);
// https://github.com/ccxt/ccxt/issues/6507
const origClientOrderId = this.safeValue (params, 'origClientOrderId');
const request = {
'symbol': market['id'],
'orderId': parseInt (id),
// 'orderId': parseInt (id),
// 'origClientOrderId': id,
};
if (origClientOrderId === undefined) {
request['orderId'] = parseInt (id);
} else {
request['origClientOrderId'] = origClientOrderId;
}
const method = market['spot'] ? 'privateDeleteOrder' : 'fapiPrivateDeleteOrder';
const response = await this[method] (this.extend (request, params));
return this.parseOrder (response);
@@ -1418,6 +1513,8 @@ module.exports = class binance extends Exchange {
}
if (since !== undefined) {
request['startTime'] = since;
// max 3 months range https://github.com/ccxt/ccxt/issues/6495
request['endTime'] = this.sum (since, 7776000000);
}
const response = await this.wapiGetDepositHistory (this.extend (request, params));
//
@@ -1443,6 +1540,8 @@ module.exports = class binance extends Exchange {
}
if (since !== undefined) {
request['startTime'] = since;
// max 3 months range https://github.com/ccxt/ccxt/issues/6495
request['endTime'] = this.sum (since, 7776000000);
}
const response = await this.wapiGetWithdrawHistory (this.extend (request, params));
//
@@ -1726,7 +1825,7 @@ module.exports = class binance extends Exchange {
if (api === 'wapi') {
url += '.html';
}
const userDataStream = ((path === 'userDataStream') || (path === 'listenKey'));
const userDataStream = (path === 'userDataStream') || (path === 'listenKey');
if (path === 'historicalTrades') {
if (this.apiKey) {
headers = {
@@ -1830,9 +1929,13 @@ module.exports = class binance extends Exchange {
// checks against error codes
const error = this.safeString (response, 'code');
if (error !== undefined) {
// https://github.com/ccxt/ccxt/issues/6501
if (error === '200') {
return;
}
// a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
// despite that their message is very confusing, it is raised by Binance
// on a temporary ban (the API key is valid, but disabled for a while)
// on a temporary ban, the API key is valid, but disabled for a while
if ((error === '-2015') && this.options['hasAlreadyAuthenticatedSuccessfully']) {
throw new DDoSProtection (this.id + ' temporary banned: ' + body);
}
+1
View File
@@ -13,6 +13,7 @@ module.exports = class binanceje extends binance {
'name': 'Binance Jersey',
'countries': [ 'JE' ], // Jersey
'certified': false,
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/54874009-d526eb00-4df3-11e9-928c-ce6a2b914cd1.jpg',
'api': {
+1
View File
@@ -13,6 +13,7 @@ module.exports = class binanceus extends binance {
'name': 'Binance US',
'countries': [ 'US' ], // US
'certified': false,
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg',
'api': {
+2
View File
@@ -279,6 +279,7 @@ module.exports = class bit2c extends Exchange {
const status = this.safeString (order, 'status');
return {
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -294,6 +295,7 @@ module.exports = class bit2c extends Exchange {
'trades': undefined,
'fee': undefined,
'info': order,
'average': undefined,
};
}
+1
View File
@@ -313,6 +313,7 @@ module.exports = class bitbank extends Exchange {
const side = this.safeStringLower (order, 'side');
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
+72 -3
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound } = require ('./base/errors');
const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound, AccountSuspended, BadSymbol, OrderImmediatelyFillable, RateLimitExceeded } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -19,6 +19,22 @@ module.exports = class bitbay extends Exchange {
'withdraw': true,
'fetchMyTrades': true,
'fetchOpenOrders': true,
'fetchOHLCV': true,
},
'timeframes': {
'1m': '60',
'3m': '180',
'5m': '300',
'15m': '900',
'30m': '1800',
'1h': '3600',
'2h': '7200',
'4h': '14400',
'6h': '21600',
'12h': '43200',
'1d': '86400',
'3d': '259200',
'1w': '604800',
},
'urls': {
'referral': 'https://auth.bitbay.net/ref/jHlbB4mIkdS1',
@@ -134,13 +150,15 @@ module.exports = class bitbay extends Exchange {
'503': InvalidNonce, // Invalid moment parameter. Request time doesn't match current server time
'504': ExchangeError, // Invalid method
'505': AuthenticationError, // Key has no permission for this action
'506': AuthenticationError, // Account locked. Please contact with customer service
'506': AccountSuspended, // Account locked. Please contact with customer service
// codes 507 and 508 are not specified in their docs
'509': ExchangeError, // The BIC/SWIFT is required for this currency
'510': ExchangeError, // Invalid market name
'510': BadSymbol, // Invalid market name
'FUNDS_NOT_SUFFICIENT': InsufficientFunds,
'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS': InvalidOrder,
'OFFER_NOT_FOUND': OrderNotFound,
'OFFER_WOULD_HAVE_BEEN_PARTIALLY_FILLED': OrderImmediatelyFillable,
'ACTION_LIMIT_EXCEEDED': RateLimitExceeded,
},
});
}
@@ -272,6 +290,7 @@ module.exports = class bitbay extends Exchange {
}
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -743,6 +762,55 @@ module.exports = class bitbay extends Exchange {
return this.safeString (types, type, type);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
// [
// '1582399800000',
// {
// o: '0.0001428',
// c: '0.0001428',
// h: '0.0001428',
// l: '0.0001428',
// v: '4',
// co: '1'
// }
// ]
return [
parseInt (ohlcv[0]),
this.safeFloat (ohlcv[1], 'o'),
this.safeFloat (ohlcv[1], 'h'),
this.safeFloat (ohlcv[1], 'l'),
this.safeFloat (ohlcv[1], 'c'),
this.safeFloat (ohlcv[1], 'v'),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const tradingSymbol = market['baseId'] + '-' + market['quoteId'];
const request = {
'symbol': tradingSymbol,
'resolution': this.timeframes[timeframe],
// 'from': 1574709092000, // unix timestamp in milliseconds, required
// 'to': 1574709092000, // unix timestamp in milliseconds, required
};
if (limit === undefined) {
limit = 100;
}
const duration = this.parseTimeframe (timeframe);
const timerange = limit * duration * 1000;
if (since === undefined) {
request['to'] = this.milliseconds ();
request['from'] = request['to'] - timerange;
} else {
request['from'] = parseInt (since);
request['to'] = this.sum (request['from'], timerange);
}
const response = await this.v1_01PublicGetTradingCandleHistorySymbolResolution (this.extend (request, params));
const ohlcvs = this.safeValue (response, 'items', []);
return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit);
}
parseTrade (trade, market = undefined) {
//
// createOrder trades
@@ -978,6 +1046,7 @@ module.exports = class bitbay extends Exchange {
'average': undefined,
'fee': undefined,
'trades': trades,
'clientOrderId': undefined,
};
}
+15 -10
View File
@@ -17,6 +17,7 @@ module.exports = class bitfinex extends Exchange {
'version': 'v1',
'rateLimit': 1500,
'certified': true,
'pro': true,
// new metainfo interface
'has': {
'CORS': false,
@@ -274,6 +275,7 @@ module.exports = class bitfinex extends Exchange {
},
},
},
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
'commonCurrencies': {
'ABS': 'ABYSS',
'AIO': 'AION',
@@ -309,7 +311,9 @@ module.exports = class bitfinex extends Exchange {
'UST': 'USDT',
'UTN': 'UTNP',
'VSY': 'VSYS',
'WAX': 'WAXP',
'XCH': 'XCHF',
'ZBT': 'ZB',
},
'exceptions': {
'exact': {
@@ -411,7 +415,7 @@ module.exports = class bitfinex extends Exchange {
'YOYOW': 'yoyow',
'ZEC': 'zcash',
'ZRX': 'zrx',
'XTZ': 'tezos',
'XTZ': 'xtz',
},
'orderTypes': {
'limit': 'exchange limit',
@@ -626,6 +630,7 @@ module.exports = class bitfinex extends Exchange {
if (timestamp !== undefined) {
timestamp *= 1000;
}
timestamp = parseInt (timestamp);
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
@@ -840,6 +845,7 @@ module.exports = class bitfinex extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -853,6 +859,8 @@ module.exports = class bitfinex extends Exchange {
'filled': this.safeFloat (order, 'executed_amount'),
'status': status,
'fee': undefined,
'cost': undefined,
'trades': undefined,
};
}
@@ -927,6 +935,7 @@ module.exports = class bitfinex extends Exchange {
}
getCurrencyName (code) {
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
if (code in this.options['currencyNames']) {
return this.options['currencyNames'][code];
}
@@ -939,18 +948,12 @@ module.exports = class bitfinex extends Exchange {
'renew': 1,
};
const response = await this.fetchDepositAddress (code, this.extend (request, params));
const address = this.safeString (response, 'address');
this.checkAddress (address);
return {
'info': response['info'],
'currency': code,
'address': address,
'tag': undefined,
};
return response;
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
const name = this.getCurrencyName (code);
const request = {
'method': name,
@@ -1092,11 +1095,13 @@ module.exports = class bitfinex extends Exchange {
async withdraw (code, amount, address, tag = undefined, params = {}) {
this.checkAddress (address);
await this.loadMarkets ();
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
const name = this.getCurrencyName (code);
const request = {
'withdraw_type': name,
'walletselected': 'exchange',
'amount': amount.toString (),
'amount': this.numberToString (amount),
'address': address,
};
if (tag !== undefined) {
+597 -54
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const bitfinex = require ('./bitfinex.js');
const { ExchangeError, NotSupported, InsufficientFunds } = require ('./base/errors');
const { ExchangeError, InvalidAddress, ArgumentsRequired, InsufficientFunds, AuthenticationError, OrderNotFound, InvalidOrder, BadRequest, InvalidNonce, BadSymbol, OnMaintenance } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -15,21 +15,29 @@ module.exports = class bitfinex2 extends bitfinex {
'countries': [ 'VG' ],
'version': 'v2',
'certified': false,
'pro': false,
// new metainfo interface
'has': {
'CORS': true,
'createLimitOrder': false,
'createMarketOrder': false,
'createOrder': false,
'CORS': false,
'cancelAllOrders': true,
'createDepositAddress': true,
'createLimitOrder': true,
'createMarketOrder': true,
'createOrder': true,
'cancelOrder': true,
'deposit': false,
'editOrder': false,
'fetchDepositAddress': false,
'fetchDepositAddress': true,
'fetchClosedOrders': false,
'fetchFundingFees': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': false,
'fetchOrder': true,
'fetchOpenOrders': true,
'fetchOrder': false,
'fetchOpenOrder': true,
'fetchClosedOrder': true,
'fetchOrderTrades': true,
'fetchStatus': true,
'fetchTickers': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
@@ -74,7 +82,30 @@ module.exports = class bitfinex2 extends bitfinex {
},
'public': {
'get': [
'conf/pub:map:currency:label',
'conf/{config}',
'conf/pub:{action}:{object}',
'conf/pub:{action}:{object}:{detail}',
'conf/pub:map:{object}',
'conf/pub:map:{object}:{detail}',
'conf/pub:map:currency:{detail}',
'conf/pub:map:currency:sym', // maps symbols to their API symbols, BAB > BCH
'conf/pub:map:currency:label', // verbose friendly names, BNT > Bancor
'conf/pub:map:currency:unit', // maps symbols to unit of measure where applicable
'conf/pub:map:currency:undl', // maps derivatives symbols to their underlying currency
'conf/pub:map:currency:pool', // maps symbols to underlying network/protocol they operate on
'conf/pub:map:currency:explorer', // maps symbols to their recognised block explorer URLs
'conf/pub:map:tx:method',
'conf/pub:list:{object}',
'conf/pub:list:{object}:{detail}',
'conf/pub:list:currency',
'conf/pub:list:pair:exchange',
'conf/pub:list:pair:margin',
'conf/pub:list:competitions',
'conf/pub:info:{object}',
'conf/pub:info:{object}:{detail}',
'conf/pub:info:pair',
'conf/pub:info:tx:status', // [ deposit, withdrawal ] statuses 1 = active, 0 = maintenance
'conf/pub:fees',
'platform/status',
'tickers',
'ticker/{symbol}',
@@ -86,7 +117,11 @@ module.exports = class bitfinex2 extends bitfinex {
'book/{symbol}/P3',
'book/{symbol}/R0',
'stats1/{key}:{size}:{symbol}:{side}/{section}',
'stats1/{key}:{size}:{symbol}:{side}/last',
'stats1/{key}:{size}:{symbol}:{side}/hist',
'stats1/{key}:{size}:{symbol}/{section}',
'stats1/{key}:{size}:{symbol}/last',
'stats1/{key}:{size}:{symbol}/hist',
'stats1/{key}:{size}:{symbol}:long/last',
'stats1/{key}:{size}:{symbol}:long/hist',
'stats1/{key}:{size}:{symbol}:short/last',
@@ -94,6 +129,11 @@ module.exports = class bitfinex2 extends bitfinex {
'candles/trade:{timeframe}:{symbol}/{section}',
'candles/trade:{timeframe}:{symbol}/last',
'candles/trade:{timeframe}:{symbol}/hist',
'status/{type}',
'status/deriv',
'liquidations/hist',
'rankings/{key}:{timeframe}:{symbol}/{section}',
'rankings/{key}:{timeframe}:{symbol}/hist',
],
'post': [
'calc/trade/avg',
@@ -102,39 +142,69 @@ module.exports = class bitfinex2 extends bitfinex {
},
'private': {
'post': [
// 'auth/r/orders/{symbol}/new', // outdated
// 'auth/r/stats/perf:{timeframe}/hist', // outdated
'auth/r/wallets',
'auth/r/wallets/hist',
'auth/r/orders',
'auth/r/orders/{symbol}',
'auth/r/orders/{symbol}/new',
'auth/r/orders/{symbol}/hist',
'auth/r/order/{symbol}:{id}/trades',
'auth/w/order/submit',
'auth/r/trades/hist',
'auth/w/order/update',
'auth/w/order/cancel',
'auth/w/order/multi',
'auth/w/order/cancel/multi',
'auth/r/orders/{symbol}/hist',
'auth/r/orders/hist',
'auth/r/order/{symbol}:{id}/trades',
'auth/r/trades/{symbol}/hist',
'auth/r/trades/hist',
'auth/r/ledgers/{currency}/hist',
'auth/r/ledgers/hist',
'auth/r/info/margin/{key}',
'auth/r/info/margin/base',
'auth/r/info/margin/sym_all',
'auth/r/positions',
'auth/w/position/claim',
'auth/r/positions/hist',
'auth/r/positions/audit',
'auth/w/deriv/collateral/set',
'auth/r/funding/offers',
'auth/r/funding/offers/{symbol}',
'auth/w/funding/offer/submit',
'auth/w/funding/offer/cancel',
'auth/w/funding/offer/cancel/all',
'auth/w/funding/close',
'auth/w/funding/auto',
'auth/w/funding/keep',
'auth/r/funding/offers/{symbol}/hist',
'auth/r/funding/offers/hist',
'auth/r/funding/loans',
'auth/r/funding/loans/hist',
'auth/r/funding/loans/{symbol}',
'auth/r/funding/loans/{symbol}/hist',
'auth/r/funding/credits',
'auth/r/funding/credits/hist',
'auth/r/funding/credits/{symbol}',
'auth/r/funding/credits/{symbol}/hist',
'auth/r/funding/trades/{symbol}/hist',
'auth/r/info/margin/{key}',
'auth/r/funding/trades/hist',
'auth/r/info/funding/{key}',
'auth/r/ledgers/hist',
'auth/r/movements/hist',
'auth/r/info/user',
'auth/r/logins/hist',
'auth/w/transfer',
'auth/w/deposit/address',
'auth/w/deposit/invoice',
'auth/w/withdraw',
'auth/r/movements/{currency}/hist',
'auth/r/stats/perf:{timeframe}/hist',
'auth/r/movements/hist',
'auth/r/alerts',
'auth/w/alert/set',
'auth/w/alert/price:{symbol}:{price}/del',
'auth/w/alert/{type}:{symbol}:{price}/del',
'auth/calc/order/avail',
'auth/r/ledgers/{symbol}/hist',
'auth/r/settings',
'auth/w/settings/set',
'auth/r/settings',
'auth/w/settings/del',
'auth/r/info/user',
],
},
},
@@ -177,21 +247,31 @@ module.exports = class bitfinex2 extends bitfinex {
},
'options': {
'precision': 'R0', // P0, P1, P2, P3, P4, R0
'orderTypes': {
'MARKET': undefined,
// convert 'EXCHANGE MARKET' to lowercase 'market'
// convert 'EXCHANGE LIMIT' to lowercase 'limit'
// everything else remains uppercase
'exchangeTypes': {
// 'MARKET': undefined,
'EXCHANGE MARKET': 'market',
'LIMIT': undefined,
// 'LIMIT': undefined,
'EXCHANGE LIMIT': 'limit',
'STOP': undefined,
'EXCHANGE STOP': 'stopOrLoss',
'TRAILING STOP': undefined,
'EXCHANGE TRAILING STOP': undefined,
'FOK': undefined,
'EXCHANGE FOK': 'limit FOK',
'STOP LIMIT': undefined,
'EXCHANGE STOP LIMIT': 'limit stop',
'IOC': undefined,
'EXCHANGE IOC': 'limit ioc',
// 'STOP': undefined,
// 'EXCHANGE STOP': undefined,
// 'TRAILING STOP': undefined,
// 'EXCHANGE TRAILING STOP': undefined,
// 'FOK': undefined,
// 'EXCHANGE FOK': undefined,
// 'STOP LIMIT': undefined,
// 'EXCHANGE STOP LIMIT': undefined,
// 'IOC': undefined,
// 'EXCHANGE IOC': undefined,
},
// convert 'market' to 'EXCHANGE MARKET'
// convert 'limit' 'EXCHANGE LIMIT'
// everything else remains as is
'orderTypes': {
'market': 'EXCHANGE MARKET',
'limit': 'EXCHANGE LIMIT',
},
'fiat': {
'USD': 'USD',
@@ -200,6 +280,22 @@ module.exports = class bitfinex2 extends bitfinex {
'GBP': 'GBP',
},
},
'exceptions': {
'exact': {
'10020': BadRequest,
'10100': AuthenticationError,
'10114': InvalidNonce,
'20060': OnMaintenance,
},
'broad': {
'address': InvalidAddress,
'available balance is only': InsufficientFunds,
'not enough exchange balance': InsufficientFunds,
'Order not found': OrderNotFound,
'symbol: invalid': BadSymbol,
'Invalid order': InvalidOrder,
},
},
});
}
@@ -211,13 +307,27 @@ module.exports = class bitfinex2 extends bitfinex {
return 'f' + code;
}
async fetchStatus (params = {}) {
//
// [1] // operative
// [0] // maintenance
//
const response = await this.publicGetPlatformStatus (params);
const status = this.safeValue (response, 0);
const formattedStatus = (status === 1) ? 'ok' : 'maintenance';
this.status = this.extend (this.status, {
'status': formattedStatus,
'updated': this.milliseconds (),
});
return this.status;
}
async fetchMarkets (params = {}) {
const response = await this.v1GetSymbolsDetails (params);
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
let id = this.safeString (market, 'pair');
id = id.toUpperCase ();
let id = this.safeStringUpper (market, 'pair');
let baseId = undefined;
let quoteId = undefined;
if (id.indexOf (':') >= 0) {
@@ -407,6 +517,26 @@ module.exports = class bitfinex2 extends bitfinex {
return this.parseTicker (ticker, market);
}
parseSymbol (marketId) {
if (marketId === undefined) {
return marketId;
}
marketId = marketId.replace ('t', '');
let baseId = undefined;
let quoteId = undefined;
if (marketId.indexOf (':') >= 0) {
const parts = marketId.split (':');
baseId = parts[0];
quoteId = parts[1];
} else {
baseId = marketId.slice (0, 3);
quoteId = marketId.slice (3, 6);
}
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
return base + '/' + quote;
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
@@ -453,26 +583,34 @@ module.exports = class bitfinex2 extends bitfinex {
const timestamp = trade[timestampIndex];
if (isPrivate) {
const marketId = trade[1];
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
} else {
symbol = marketId;
}
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
} else {
symbol = this.parseSymbol (marketId);
}
orderId = trade[3].toString ();
takerOrMaker = (trade[8] === 1) ? 'maker' : 'taker';
const feeCost = trade[9];
let feeCost = trade[9];
const feeCurrency = this.safeCurrencyCode (trade[10]);
if (feeCost !== undefined) {
feeCost = -feeCost;
if (symbol in this.markets) {
feeCost = this.feeToPrecision (symbol, feeCost);
} else {
const currencyId = 'f' + feeCurrency;
if (currencyId in this.currencies_by_id) {
const currency = this.currencies_by_id[currencyId];
feeCost = this.currencyToPrecision (currency['code'], feeCost);
}
}
fee = {
'cost': Math.abs (feeCost),
'cost': parseFloat (feeCost),
'currency': feeCurrency,
};
}
const orderType = trade[6];
type = this.safeString (this.options['orderTypes'], orderType);
type = this.safeString (this.options['exchangeTypes'], orderType);
}
if (symbol === undefined) {
if (market !== undefined) {
@@ -555,24 +693,252 @@ module.exports = class bitfinex2 extends bitfinex {
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
parseOrderStatus (status) {
const statuses = {
'ACTIVE': 'open',
'PARTIALLY FILLED': 'open',
'EXECUTED': 'closed',
'CANCELED': 'canceled',
'INSUFFICIENT MARGIN': 'canceled',
'INSUFFICIENT BALANCE (G1) was: PARTIALLY FILLED': 'canceled',
'RSN_DUST': 'rejected',
'RSN_PAUSE': 'rejected',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
const id = this.safeString (order, 0);
let symbol = undefined;
const marketId = this.safeString (order, 3);
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
} else {
symbol = this.parseSymbol (marketId);
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
// https://github.com/ccxt/ccxt/issues/6686
// const timestamp = this.safeTimestamp (order, 5);
const timestamp = this.safeInteger (order, 5);
const remaining = Math.abs (this.safeFloat (order, 6));
const amount = Math.abs (this.safeFloat (order, 7));
const filled = amount - remaining;
const side = (order[7] < 0) ? 'sell' : 'buy';
const orderType = this.safeString (order, 8);
const type = this.safeString (this.safeValue (this.options, 'exchangeTypes'), orderType);
let status = undefined;
const statusString = this.safeString (order, 13);
if (statusString !== undefined) {
const parts = statusString.split (' @ ');
status = this.parseOrderStatus (this.safeString (parts, 0));
}
const price = this.safeFloat (order, 16);
const average = this.safeFloat (order, 17);
const cost = price * filled;
const clientOrderId = this.safeString (order, 2);
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'average': average,
'filled': filled,
'remaining': remaining,
'status': status,
'fee': undefined,
'trades': undefined,
};
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
throw new NotSupported (this.id + ' createOrder not implemented yet');
await this.loadMarkets ();
const market = this.market (symbol);
const orderTypes = this.safeValue (this.options, 'orderTypes', {});
const orderType = this.safeString (orderTypes, type, type);
amount = (side === 'sell') ? -amount : amount;
const request = {
'symbol': market['id'],
'type': orderType,
'amount': this.numberToString (amount),
};
if (type !== 'market') {
request['price'] = this.numberToString (price);
}
const response = await this.privatePostAuthWOrderSubmit (this.extend (request, params));
//
// [
// 1578784364.748, // Millisecond Time Stamp of the update
// "on-req", // Purpose of notification ('on-req', 'oc-req', 'uca', 'fon-req', 'foc-req')
// null, // Unique ID of the message
// null, // Ignore
// [
// [
// 37271830598, // Order ID
// null, // Group ID
// 1578784364748, // Client Order ID
// "tBTCUST", // Pair
// 1578784364748, // Millisecond timestamp of creation
// 1578784364748, // Millisecond timestamp of update
// -0.005, // Positive means buy, negative means sell
// -0.005, // Original amount
// "EXCHANGE LIMIT", // Order type (LIMIT, MARKET, STOP, TRAILING STOP, EXCHANGE MARKET, EXCHANGE LIMIT, EXCHANGE STOP, EXCHANGE TRAILING STOP, FOK, EXCHANGE FOK, IOC, EXCHANGE IOC)
// null, // Previous order type
// null, // Millisecond timestamp of Time-In-Force: automatic order cancellation
// null, // Ignore
// 0, // Flags (see https://docs.bitfinex.com/docs/flag-values)
// "ACTIVE", // Order Status
// null, // Ignore
// null, // Ignore
// 20000, // Price
// 0, // Average price
// 0, // The trailing price
// 0, // Auxiliary Limit price (for STOP LIMIT)
// null, // Ignore
// null, // Ignore
// null, // Ignore
// 0, // 1 - hidden order
// null, // If another order caused this order to be placed (OCO) this will be that other order's ID
// null, // Ignore
// null, // Ignore
// null, // Ignore
// "API>BFX", // Origin of action: BFX, ETHFX, API>BFX, API>ETHFX
// null, // Ignore
// null, // Ignore
// null // Meta
// ]
// ],
// null, // Error code
// "SUCCESS", // Status (SUCCESS, ERROR, FAILURE, ...)
// "Submitting 1 orders." // Text of the notification
// ]
//
const status = this.safeString (response, 6);
if (status !== 'SUCCESS') {
const errorCode = response[5];
const errorText = response[7];
throw new ExchangeError (this.id + ' ' + response[6] + ': ' + errorText + ' (#' + errorCode + ')');
}
const orders = this.safeValue (response, 4, []);
const order = this.safeValue (orders, 0);
return this.parseOrder (order, market);
}
cancelOrder (id, symbol = undefined, params = {}) {
throw new NotSupported (this.id + ' cancelOrder not implemented yet');
async cancelAllOrders (symbol = undefined, params = {}) {
const request = {
'all': 1,
};
const response = await this.privatePostAuthWOrderCancelMulti (this.extend (request, params));
const orders = this.safeValue (response, 4, []);
return this.parseOrders (orders);
}
async fetchOrder (id, symbol = undefined, params = {}) {
throw new NotSupported (this.id + ' fetchOrder not implemented yet');
async cancelOrder (id, symbol = undefined, params = {}) {
const cid = this.safeValue (params, 'cid'); // client order id
let request = undefined;
if (cid !== undefined) {
const cidDate = this.safeValue (params, 'cidDate'); // client order id date
if (cidDate === undefined) {
throw new InvalidOrder (this.id + " canceling an order by client order id ('cid') requires both 'cid' and 'cid_date' ('YYYY-MM-DD')");
}
request = {
'cid': cid,
'cid_date': cidDate,
};
} else {
request = {
'id': parseInt (id),
};
}
const response = await this.privatePostAuthWOrderCancel (this.extend (request, params));
const order = this.safeValue (response, 4);
return this.parseOrder (order);
}
async fetchDepositAddress (currency, params = {}) {
throw new NotSupported (this.id + ' fetchDepositAddress() not implemented yet.');
async fetchOpenOrder (id, symbol = undefined, params = {}) {
const request = {
'id': [ parseInt (id) ],
};
const orders = await this.fetchOpenOrders (symbol, undefined, undefined, this.extend (request, params));
const order = this.safeValue (orders, 0);
if (order === undefined) {
throw new OrderNotFound (this.id + ' order ' + id + ' not found');
}
return order;
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
throw new NotSupported (this.id + ' withdraw not implemented yet');
async fetchClosedOrder (id, symbol = undefined, params = {}) {
const request = {
'id': [ parseInt (id) ],
};
const orders = await this.fetchClosedOrders (symbol, undefined, undefined, this.extend (request, params));
const order = this.safeValue (orders, 0);
if (order === undefined) {
throw new OrderNotFound (this.id + ' order ' + id + ' not found');
}
return order;
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let market = undefined;
let response = undefined;
if (symbol === undefined) {
response = await this.privatePostAuthROrders (this.extend (request, params));
} else {
market = this.market (symbol);
request['symbol'] = market['id'];
response = await this.privatePostAuthROrdersSymbol (this.extend (request, params));
}
return this.parseOrders (response, market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
// returns the most recent closed or canceled orders up to circa two weeks ago
await this.loadMarkets ();
const request = {};
let market = undefined;
let response = undefined;
if (symbol === undefined) {
response = await this.privatePostAuthROrdersHist (this.extend (request, params));
} else {
market = this.market (symbol);
request['symbol'] = market['id'];
response = await this.privatePostAuthROrdersSymbolHist (this.extend (request, params));
}
if (since !== undefined) {
request['start'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 25, max 2500
}
return this.parseOrders (response, market, since, limit);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrderTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const orderId = parseInt (id);
const request = {
'id': orderId,
'symbol': market['id'],
};
// valid for trades upto 10 days old
const response = await this.privatePostAuthROrderSymbolIdTrades (this.extend (request, params));
return this.parseTrades (response, market, since, limit);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
@@ -597,6 +963,170 @@ module.exports = class bitfinex2 extends bitfinex {
return this.parseTrades (response, market, since, limit);
}
async createDepositAddress (code, params = {}) {
await this.loadMarkets ();
const request = {
'op_renew': 1,
};
const response = await this.fetchDepositAddress (code, this.extend (request, params));
return response;
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
const name = this.getCurrencyName (code);
const request = {
'method': name,
'wallet': 'exchange', // 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
'op_renew': 0, // a value of 1 will generate a new address
};
const response = await this.privatePostAuthWDepositAddress (this.extend (request, params));
//
// [
// 1582269616687, // MTS Millisecond Time Stamp of the update
// 'acc_dep', // TYPE Purpose of notification 'acc_dep' for account deposit
// null, // MESSAGE_ID unique ID of the message
// null, // not documented
// [
// null, // PLACEHOLDER
// 'BITCOIN', // METHOD Method of deposit
// 'BTC', // CURRENCY_CODE Currency code of new address
// null, // PLACEHOLDER
// '1BC9PZqpUmjyEB54uggn8TFKj49zSDYzqG', // ADDRESS
// null, // POOL_ADDRESS
// ],
// null, // CODE null or integer work in progress
// 'SUCCESS', // STATUS Status of the notification, SUCCESS, ERROR, FAILURE
// 'success', // TEXT Text of the notification
// ]
//
const result = this.safeValue (response, 4, []);
const poolAddress = this.safeString (result, 5);
const address = (poolAddress === undefined) ? this.safeString (result, 4) : poolAddress;
const tag = (poolAddress === undefined) ? undefined : this.safeString (result, 4);
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': response,
};
}
parseTransaction (transaction, currency = undefined) {
//
// withdraw
//
// [
// 1582271520931, // MTS Millisecond Time Stamp of the update
// "acc_wd-req", // TYPE Purpose of notification 'acc_wd-req' account withdrawal request
// null, // MESSAGE_ID unique ID of the message
// null, // not documented
// [
// 0, // WITHDRAWAL_ID Unique Withdrawal ID
// null, // PLACEHOLDER
// "bitcoin", // METHOD Method of withdrawal
// null, // PAYMENT_ID Payment ID if relevant
// "exchange", // WALLET Sending wallet
// 1, // AMOUNT Amount of Withdrawal less fee
// null, // PLACEHOLDER
// null, // PLACEHOLDER
// 0.0004, // WITHDRAWAL_FEE Fee on withdrawal
// ],
// null, // CODE null or integer Work in progress
// "SUCCESS", // STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
// "Invalid bitcoin address (abcdef)", // TEXT Text of the notification
// ]
//
// todo add support for all movements, deposits and withdrawals
//
const data = this.safeValue (transaction, 4, []);
const timestamp = this.safeInteger (transaction, 0);
let code = undefined;
if (currency !== undefined) {
code = currency['code'];
}
let feeCost = this.safeFloat (data, 8);
if (feeCost !== undefined) {
feeCost = Math.abs (feeCost);
}
const amount = this.safeFloat (data, 5);
let id = this.safeValue (data, 0);
let status = 'ok';
if (id === 0) {
id = undefined;
status = 'failed';
}
return {
'info': transaction,
'id': id,
'txid': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'address': undefined, // this is actually the tag for XRP transfers (the address is missing)
'tag': this.safeString (data, 3), // refix it properly for the tag from description
'type': 'withdrawal',
'amount': amount,
'currency': code,
'status': status,
'updated': undefined,
'fee': {
'currency': code,
'cost': feeCost,
'rate': undefined,
},
};
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
// todo rewrite for https://api-pub.bitfinex.com//v2/conf/pub:map:tx:method
const name = this.getCurrencyName (code);
const request = {
'method': name,
'wallet': 'exchange', // 'exchange', 'margin', 'funding' and also old labels 'exchange', 'trading', 'deposit', respectively
'amount': this.numberToString (amount),
'address': address,
};
if (tag !== undefined) {
request['payment_id'] = tag;
}
const response = await this.privatePostAuthWWithdraw (this.extend (request, params));
//
// [
// 1582271520931, // MTS Millisecond Time Stamp of the update
// "acc_wd-req", // TYPE Purpose of notification 'acc_wd-req' account withdrawal request
// null, // MESSAGE_ID unique ID of the message
// null, // not documented
// [
// 0, // WITHDRAWAL_ID Unique Withdrawal ID
// null, // PLACEHOLDER
// "bitcoin", // METHOD Method of withdrawal
// null, // PAYMENT_ID Payment ID if relevant
// "exchange", // WALLET Sending wallet
// 1, // AMOUNT Amount of Withdrawal less fee
// null, // PLACEHOLDER
// null, // PLACEHOLDER
// 0.0004, // WITHDRAWAL_FEE Fee on withdrawal
// ],
// null, // CODE null or integer Work in progress
// "SUCCESS", // STATUS Status of the notification, it may vary over time SUCCESS, ERROR, FAILURE
// "Invalid bitcoin address (abcdef)", // TEXT Text of the notification
// ]
//
const text = this.safeString (response, 7);
if (text !== 'success') {
this.throwBroadlyMatchedException (this.exceptions['broad'], text, text);
}
const transaction = this.parseTransaction (response, currency);
return this.extend (transaction, {
'address': address,
});
}
nonce () {
return this.milliseconds ();
}
@@ -646,4 +1176,17 @@ module.exports = class bitfinex2 extends bitfinex {
}
return response;
}
handleErrors (statusCode, statusText, url, method, responseHeaders, responseBody, response, requestHeaders, requestBody) {
if (statusCode === 500) {
// See https://docs.bitfinex.com/docs/abbreviations-glossary#section-errorinfo-codes
const errorCode = this.numberToString (response[1]);
const errorText = response[2];
const feedback = this.id + ' ' + errorText;
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
this.throwExactlyMatchedException (this.exceptions['exact'], errorText, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], errorText, feedback);
throw new ExchangeError (this.id + ' ' + errorText + ' (#' + errorCode + ')');
}
}
};
+3
View File
@@ -356,6 +356,7 @@ module.exports = class bitflyer extends Exchange {
const id = this.safeString (order, 'child_order_acceptance_id');
return {
'id': id,
'clientOrderId': undefined,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -370,6 +371,8 @@ module.exports = class bitflyer extends Exchange {
'filled': filled,
'remaining': remaining,
'fee': fee,
'average': undefined,
'trades': undefined,
};
}
+6
View File
@@ -220,6 +220,9 @@ module.exports = class bitforex extends Exchange {
},
},
},
'commonCurrencies': {
'UOS': 'UOS Network',
},
'exceptions': {
'4004': OrderNotFound,
'1013': AuthenticationError,
@@ -309,6 +312,7 @@ module.exports = class bitforex extends Exchange {
'cost': cost,
'order': orderId,
'fee': undefined,
'takerOrMaker': undefined,
};
}
@@ -462,6 +466,7 @@ module.exports = class bitforex extends Exchange {
const result = {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -476,6 +481,7 @@ module.exports = class bitforex extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
return result;
}
+2
View File
@@ -129,6 +129,8 @@ module.exports = class bithumb extends Exchange {
'max': undefined,
},
},
'baseId': undefined,
'quoteId': undefined,
});
}
return result;
-378
View File
@@ -1,378 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { NotSupported } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class bitlish extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'bitlish',
'name': 'Bitlish',
'countries': [ 'GB', 'EU', 'RU' ],
'rateLimit': 1500,
'version': 'v1',
'has': {
'CORS': false,
'fetchTickers': true,
'fetchOHLCV': true,
'withdraw': true,
},
'timeframes': {
'1h': 3600,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766275-dcfc6c30-5ed3-11e7-839d-00a846385d0b.jpg',
'api': 'https://bitlish.com/api',
'www': 'https://bitlish.com',
'doc': 'https://bitlish.com/api',
'fees': 'https://bitlish.com/fees',
},
'requiredCredentials': {
'apiKey': true,
'secret': false,
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'taker': 0.3 / 100, // anonymous 0.3%, verified 0.2%
'maker': 0.2 / 100, // anonymous 0.2%, verified 0.1%
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {
'BTC': 0.001,
'LTC': 0.001,
'DOGE': 0.001,
'ETH': 0.001,
'XMR': 0,
'ZEC': 0.001,
'DASH': 0.0001,
'EUR': 50,
},
'deposit': {
'BTC': 0,
'LTC': 0,
'DOGE': 0,
'ETH': 0,
'XMR': 0,
'ZEC': 0,
'DASH': 0,
'EUR': 0,
},
},
},
'api': {
'public': {
'get': [
'instruments',
'ohlcv',
'pairs',
'tickers',
'trades_depth',
'trades_history',
],
'post': [
'instruments',
'ohlcv',
'pairs',
'tickers',
'trades_depth',
'trades_history',
],
},
'private': {
'post': [
'accounts_operations',
'balance',
'cancel_trade',
'cancel_trades_by_ids',
'cancel_all_trades',
'create_bcode',
'create_template_wallet',
'create_trade',
'deposit',
'list_accounts_operations_from_ts',
'list_active_trades',
'list_bcodes',
'list_my_matches_from_ts',
'list_my_trades',
'list_my_trads_from_ts',
'list_payment_methods',
'list_payments',
'redeem_code',
'resign',
'signin',
'signout',
'trade_details',
'trade_options',
'withdraw',
'withdraw_by_id',
],
},
},
'commonCurrencies': {
'DSH': 'DASH',
'XDG': 'DOGE',
},
});
}
async fetchMarkets (params = {}) {
const response = await this.publicGetPairs (params);
const result = [];
const keys = Object.keys (response);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const market = response[key];
const id = this.safeString (market, 'id');
const name = this.safeString (market, 'name');
const [ baseId, quoteId ] = name.split ('/');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'info': market,
});
}
return result;
}
parseTicker (ticker, market) {
const timestamp = this.milliseconds ();
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const last = this.safeFloat (ticker, 'last');
return {
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'high': this.safeFloat (ticker, 'max'),
'low': this.safeFloat (ticker, 'min'),
'bid': this.safeFloat (ticker, 'bid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'ask'),
'askVolume': undefined,
'vwap': undefined,
'open': this.safeFloat (ticker, 'first'),
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': this.safeFloat (ticker, 'prc') * 100,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'sum'),
'quoteVolume': undefined,
'info': ticker,
};
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const tickers = await this.publicGetTickers (params);
const ids = Object.keys (tickers);
const result = {};
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const market = this.safeValue (this.markets_by_id, id);
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
} else {
const baseId = id.slice (0, 3);
const quoteId = id.slice (3, 6);
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
const ticker = tickers[id];
result[symbol] = this.parseTicker (ticker, market);
}
return result;
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const response = await this.publicGetTickers (params);
const marketId = market['id'];
return this.parseTicker (response[marketId], market);
}
async fetchOHLCV (symbol, timeframe = '1h', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
// let market = this.market (symbol);
const now = this.seconds ();
let start = now - 86400 * 30; // last 30 days
if (since !== undefined) {
start = parseInt (since / 1000);
}
const interval = [ start.toString (), undefined ];
const request = {
'time_range': interval,
};
return await this.publicPostOhlcv (this.extend (request, params));
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'pair_id': this.marketId (symbol),
};
const response = await this.publicGetTradesDepth (this.extend (request, params));
let timestamp = undefined;
const last = this.safeInteger (response, 'last');
if (last !== undefined) {
timestamp = parseInt (last / 1000);
}
return this.parseOrderBook (response, timestamp, 'bid', 'ask', 'price', 'volume');
}
parseTrade (trade, market = undefined) {
const side = (trade['dir'] === 'bid') ? 'buy' : 'sell';
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
let timestamp = this.safeInteger (trade, 'created');
if (timestamp !== undefined) {
timestamp = parseInt (timestamp / 1000);
}
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'amount');
let cost = undefined;
if (amount !== undefined) {
if (price !== undefined) {
cost = price * amount;
}
}
return {
'id': undefined,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': undefined,
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
};
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const response = await this.publicGetTradesHistory (this.extend ({
'pair_id': market['id'],
}, params));
return this.parseTrades (response['list'], market, since, limit);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostBalance (params);
const result = { 'info': response };
const currencyIds = Object.keys (response);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
const balance = this.safeValue (response, currencyId, {});
account['free'] = this.safeFloat (balance, 'funds');
account['used'] = this.safeFloat (balance, 'holded');
result[code] = account;
}
return this.parseBalance (result);
}
async signIn (params = {}) {
const request = {
'login': this.login,
'passwd': this.password,
};
return await this.privatePostSignin (this.extend (request, params));
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'pair_id': this.marketId (symbol),
'dir': (side === 'buy') ? 'bid' : 'ask',
'amount': amount,
};
if (type === 'limit') {
request['price'] = price;
}
const response = await this.privatePostCreateTrade (this.extend (request, params));
const id = this.safeString (response, 'id');
return {
'info': response,
'id': id,
};
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'id': id,
};
return await this.privatePostCancelTrade (this.extend (request, params));
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
if (code !== 'BTC') {
// they did not document other types...
throw new NotSupported (this.id + ' currently supports BTC withdrawals only, until they document other currencies...');
}
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currency': currency['id'],
'amount': parseFloat (amount),
'account': address,
'payment_method': 'bitcoin', // they did not document other types...
};
const response = await this.privatePostWithdraw (this.extend (request, params));
return {
'info': response,
'id': response['message_id'],
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'] + '/' + this.version + '/' + path;
if (api === 'public') {
if (method === 'GET') {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
} else {
body = this.json (params);
headers = { 'Content-Type': 'application/json' };
}
} else {
this.checkRequiredCredentials ();
body = this.json (this.extend ({ 'token': this.apiKey }, params));
headers = { 'Content-Type': 'application/json' };
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
};
+3
View File
@@ -150,6 +150,7 @@ module.exports = class bitmart extends Exchange {
},
'commonCurrencies': {
'ONE': 'Menlo One',
'PLA': 'Plair',
},
});
}
@@ -248,6 +249,7 @@ module.exports = class bitmart extends Exchange {
'precision': precision,
'limits': limits,
'info': market,
'active': undefined,
});
}
return result;
@@ -681,6 +683,7 @@ module.exports = class bitmart extends Exchange {
const type = undefined;
return {
'id': id,
'clientOrderId': undefined,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
+9 -2
View File
@@ -52,7 +52,7 @@ module.exports = class bitmax extends Exchange {
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg',
'api': 'https://bitmax.io',
'test': 'https://bitmax-test.io/api',
'test': 'https://bitmax-test.io',
'www': 'https://bitmax.io',
'doc': [
'https://github.com/bitmax-exchange/api-doc/blob/master/bitmax-api-doc-v1.2.md',
@@ -121,12 +121,17 @@ module.exports = class bitmax extends Exchange {
'exact': {
'2100': AuthenticationError, // {"code":2100,"message":"ApiKeyFailure"}
'5002': BadSymbol, // {"code":5002,"message":"Invalid Symbol"}
'6001': BadSymbol, // {"code":6001,"message":"Trading is disabled on symbol."}
'6010': InsufficientFunds, // {'code': 6010, 'message': 'Not enough balance.'}
'60060': InvalidOrder, // { 'code': 60060, 'message': 'The order is already filled or canceled.' }
'600503': InvalidOrder, // {"code":600503,"message":"Notional is too small."}
},
'broad': {},
},
'commonCurrencies': {
'BTCBEAR': 'BEAR',
'BTCBULL': 'BULL',
},
});
}
@@ -722,9 +727,11 @@ module.exports = class bitmax extends Exchange {
'currency': this.safeString (order, 'feeAsset'),
};
const average = this.safeFloat (order, 'avgPrice');
const clientOrderId = id;
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -1145,7 +1152,7 @@ module.exports = class bitmax extends Exchange {
headers['x-auth-coid'] = coid;
}
const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha256', 'base64');
headers['x-auth-signature'] = signature;
headers['x-auth-signature'] = this.decode (signature);
if (method === 'GET') {
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
+14 -2
View File
@@ -17,6 +17,7 @@ module.exports = class bitmex extends Exchange {
'version': 'v1',
'userAgent': undefined,
'rateLimit': 2000,
'pro': true,
'has': {
'CORS': false,
'fetchOHLCV': true,
@@ -52,7 +53,7 @@ module.exports = class bitmex extends Exchange {
'https://github.com/BitMEX/api-connectors/tree/master/official-http',
],
'fees': 'https://www.bitmex.com/app/fees',
'referral': 'https://www.bitmex.com/register/rm3C16',
'referral': 'https://www.bitmex.com/register/upZpOX',
},
'api': {
'public': {
@@ -1110,9 +1111,11 @@ module.exports = class bitmex extends Exchange {
const id = this.safeString (order, 'orderID');
const type = this.safeStringLower (order, 'ordType');
const side = this.safeStringLower (order, 'side');
const clientOrderId = this.safeString (order, 'clOrdID');
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -1127,6 +1130,7 @@ module.exports = class bitmex extends Exchange {
'remaining': remaining,
'status': status,
'fee': undefined,
'trades': undefined,
};
}
@@ -1211,7 +1215,15 @@ module.exports = class bitmex extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.privateDeleteOrder (this.extend ({ 'orderID': id }, params));
// https://github.com/ccxt/ccxt/issues/6507
const clOrdID = this.safeValue (params, 'clOrdID');
const request = {};
if (clOrdID === undefined) {
request['orderID'] = id;
} else {
request['clOrdID'] = clOrdID;
}
const response = await this.privateDeleteOrder (this.extend (request, params));
let order = response[0];
const error = this.safeString (order, 'error');
if (error !== undefined) {
+6 -1
View File
@@ -16,7 +16,7 @@ module.exports = class bitso extends Exchange {
'rateLimit': 2000, // 30 requests per minute
'version': 'v3',
'has': {
'CORS': true,
'CORS': false,
'fetchMyTrades': true,
'fetchOpenOrders': true,
'fetchOrder': true,
@@ -139,6 +139,7 @@ module.exports = class bitso extends Exchange {
'info': market,
'limits': limits,
'precision': precision,
'active': undefined,
});
}
return result;
@@ -369,9 +370,11 @@ module.exports = class bitso extends Exchange {
filled = amount - remaining;
}
}
const clientOrderId = this.safeString (order, 'client_id');
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -385,6 +388,8 @@ module.exports = class bitso extends Exchange {
'filled': filled,
'status': status,
'fee': undefined,
'average': undefined,
'trades': undefined,
};
}
+67 -15
View File
@@ -16,6 +16,7 @@ module.exports = class bitstamp extends Exchange {
'rateLimit': 1000,
'version': 'v2',
'userAgent': this.userAgents['chrome'],
'pro': true,
'has': {
'CORS': true,
'fetchDepositAddress': true,
@@ -31,6 +32,7 @@ module.exports = class bitstamp extends Exchange {
'api': {
'public': 'https://www.bitstamp.net/api',
'private': 'https://www.bitstamp.net/api',
'v1': 'https://www.bitstamp.net/api',
},
'www': 'https://www.bitstamp.net',
'doc': 'https://www.bitstamp.net/api',
@@ -233,8 +235,27 @@ module.exports = class bitstamp extends Exchange {
'pair': this.marketId (symbol),
};
const response = await this.publicGetOrderBookPair (this.extend (request, params));
const timestamp = this.safeTimestamp (response, 'timestamp');
return this.parseOrderBook (response, timestamp);
//
// {
// "timestamp": "1583652948",
// "microtimestamp": "1583652948955826",
// "bids": [
// [ "8750.00", "1.33685271" ],
// [ "8749.39", "0.07700000" ],
// [ "8746.98", "0.07400000" ],
// ]
// "asks": [
// [ "8754.10", "1.51995636" ],
// [ "8754.71", "1.40000000" ],
// [ "8754.72", "2.50000000" ],
// ]
// }
//
const microtimestamp = this.safeInteger (response, 'microtimestamp');
const timestamp = parseInt (microtimestamp / 1000);
const orderbook = this.parseOrderBook (response, timestamp);
orderbook['nonce'] = microtimestamp;
return orderbook;
}
async fetchTicker (symbol, params = {}) {
@@ -946,6 +967,7 @@ module.exports = class bitstamp extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': lastTradeTimestamp,
@@ -961,6 +983,7 @@ module.exports = class bitstamp extends Exchange {
'trades': trades,
'fee': fee,
'info': order,
'average': undefined,
};
}
@@ -1023,7 +1046,10 @@ module.exports = class bitstamp extends Exchange {
method += 'Post' + this.capitalize (name);
method += v1 ? 'Deposit' : '';
method += 'Address';
const response = await this[method] (params);
let response = await this[method] (params);
if (v1) {
response = JSON.parse (response);
}
const address = v1 ? response : this.safeString (response, 'address');
const tag = v1 ? undefined : this.safeString (response, 'destination_tag');
this.checkAddress (address);
@@ -1077,18 +1103,44 @@ module.exports = class bitstamp extends Exchange {
}
} else {
this.checkRequiredCredentials ();
const nonce = this.nonce ().toString ();
const auth = nonce + this.uid + this.apiKey;
const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret)));
query = this.extend ({
'key': this.apiKey,
'signature': signature.toUpperCase (),
'nonce': nonce,
}, query);
body = this.urlencode (query);
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
const authVersion = this.safeValue (this.options, 'auth', 'v2');
if ((authVersion === 'v1') || (api === 'v1')) {
const nonce = this.nonce ().toString ();
const auth = nonce + this.uid + this.apiKey;
const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret)));
query = this.extend ({
'key': this.apiKey,
'signature': signature.toUpperCase (),
'nonce': nonce,
}, query);
body = this.urlencode (query);
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
} else {
const xAuth = 'BITSTAMP ' + this.apiKey;
const xAuthNonce = this.uuid ();
const xAuthTimestamp = this.milliseconds ().toString ();
const xAuthVersion = 'v2';
let contentType = '';
headers = {
'X-Auth': xAuth,
'X-Auth-Nonce': xAuthNonce,
'X-Auth-Timestamp': xAuthTimestamp,
'X-Auth-Version': xAuthVersion,
};
if (method === 'POST') {
if (Object.keys (query).length) {
body = this.urlencode (query);
contentType = 'application/x-www-form-urlencoded';
headers['Content-Type'] = contentType;
}
}
const authBody = body ? body : '';
const auth = xAuth + method + url.replace ('https://', '') + contentType + xAuthNonce + xAuthTimestamp + xAuthVersion + authBody;
const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret)));
headers['X-Auth-Signature'] = signature;
}
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
+108 -14
View File
@@ -17,10 +17,11 @@ module.exports = class bittrex extends Exchange {
'version': 'v1.1',
'rateLimit': 1500,
'certified': true,
'pro': true,
// new metainfo interface
'has': {
'CORS': true,
'createMarketOrder': false,
'CORS': false,
'createMarketOrder': true,
'fetchDepositAddress': true,
'fetchClosedOrders': true,
'fetchCurrencies': true,
@@ -62,6 +63,7 @@ module.exports = class bittrex extends Exchange {
'https://bittrex.zendesk.com/hc/en-us/articles/115003684371-BITTREX-SERVICE-FEES-AND-WITHDRAWAL-LIMITATIONS',
'https://bittrex.zendesk.com/hc/en-us/articles/115000199651-What-fees-does-Bittrex-charge-',
],
'referral': 'https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B',
},
'api': {
'v3': {
@@ -214,6 +216,9 @@ module.exports = class bittrex extends Exchange {
'INVALID_CURRENCY': ExchangeError,
'INVALID_PERMISSION': AuthenticationError,
'INSUFFICIENT_FUNDS': InsufficientFunds,
'INVALID_CEILING_MARKET_BUY': InvalidOrder,
'INVALID_FIAT_ACCOUNT': InvalidOrder,
'INVALID_ORDER_TYPE': InvalidOrder,
'QUANTITY_NOT_PROVIDED': InvalidOrder,
'MIN_TRADE_REQUIREMENT_NOT_MET': InvalidOrder,
'ORDER_NOT_OPEN': OrderNotFound,
@@ -258,10 +263,10 @@ module.exports = class bittrex extends Exchange {
// see the implementation of fetchClosedOrdersV3 below
'fetchClosedOrdersMethod': 'fetch_closed_orders_v3',
'fetchClosedOrdersFilterBySince': true,
// 'createOrderMethod': 'create_order_v1',
},
'commonCurrencies': {
'BITS': 'SWIFT',
'CPC': 'Capricoin',
},
});
}
@@ -416,9 +421,6 @@ module.exports = class bittrex extends Exchange {
for (let i = 0; i < currencies.length; i++) {
const currency = currencies[i];
const id = this.safeString (currency, 'Currency');
// todo: will need to rethink the fees
// to add support for multiple withdrawal/deposit methods and
// differentiated fees for each particular method
const code = this.safeCurrencyCode (id);
const precision = 8; // default precision, todo: fix "magic constants"
const address = this.safeValue (currency, 'BaseAddress');
@@ -428,9 +430,9 @@ module.exports = class bittrex extends Exchange {
'code': code,
'address': address,
'info': currency,
'type': currency['CoinType'],
'name': currency['CurrencyLong'],
'active': currency['IsActive'],
'type': this.safeString (currency, 'CoinType'),
'name': this.safeString (currency, 'CurrencyLong'),
'active': this.safeValue (currency, 'IsActive'),
'fee': fee,
'precision': precision,
'limits': {
@@ -664,6 +666,74 @@ module.exports = class bittrex extends Exchange {
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
const uppercaseType = type.toUpperCase ();
const isMarket = (uppercaseType === 'MARKET');
const isCeilingLimit = (uppercaseType === 'CEILING_LIMIT');
const isCeilingMarket = (uppercaseType === 'CEILING_MARKET');
const isV3 = isMarket || isCeilingLimit || isCeilingMarket;
const defaultMethod = isV3 ? 'create_order_v3' : 'create_order_v1';
const method = this.safeValue (this.options, 'createOrderMethod', defaultMethod);
return await this[method] (symbol, type, side, amount, price, params);
}
async createOrderV3 (symbol, type, side, amount, price = undefined, params = {}) {
// A ceiling order is a market or limit order that allows you to specify
// the amount of quote currency you want to spend (or receive, if selling)
// instead of the quantity of the market currency (e.g. buy $100 USD of BTC
// at the current market BTC price)
await this.loadMarkets ();
const market = this.market (symbol);
const uppercaseType = type.toUpperCase ();
const reverseId = market['baseId'] + '-' + market['quoteId'];
const request = {
'marketSymbol': reverseId,
'direction': side.toUpperCase (),
'type': uppercaseType, // LIMIT, MARKET, CEILING_LIMIT, CEILING_MARKET
// 'quantity': this.amountToPrecision (symbol, amount), // required for limit orders, excluded for ceiling orders
// 'ceiling': this.priceToPrecision (symbol, price), // required for ceiling orders, excluded for non-ceiling orders
// 'limit': this.priceToPrecision (symbol, price), // required for limit orders, excluded for market orders
// 'timeInForce': 'GOOD_TIL_CANCELLED', // IMMEDIATE_OR_CANCEL, FILL_OR_KILL, POST_ONLY_GOOD_TIL_CANCELLED
// 'useAwards': false, // optional
};
const isCeilingLimit = (uppercaseType === 'CEILING_LIMIT');
const isCeilingMarket = (uppercaseType === 'CEILING_MARKET');
const isCeilingOrder = isCeilingLimit || isCeilingMarket;
if (isCeilingOrder) {
request['ceiling'] = this.priceToPrecision (symbol, price);
// bittrex only accepts IMMEDIATE_OR_CANCEL or FILL_OR_KILL for ceiling orders
request['timeInForce'] = 'IMMEDIATE_OR_CANCEL';
} else {
request['quantity'] = this.amountToPrecision (symbol, amount);
if (uppercaseType === 'LIMIT') {
request['limit'] = this.priceToPrecision (symbol, price);
request['timeInForce'] = 'GOOD_TIL_CANCELLED';
} else {
// bittrex does not allow GOOD_TIL_CANCELLED for market orders
request['timeInForce'] = 'IMMEDIATE_OR_CANCEL';
}
}
const response = await this.v3PostOrders (this.extend (request, params));
//
// {
// id: 'f03d5e98-b5ac-48fb-8647-dd4db828a297',
// marketSymbol: 'BTC-USDT',
// direction: 'SELL',
// type: 'LIMIT',
// quantity: '0.01',
// limit: '6000',
// timeInForce: 'GOOD_TIL_CANCELLED',
// fillQuantity: '0.00000000',
// commission: '0.00000000',
// proceeds: '0.00000000',
// status: 'OPEN',
// createdAt: '2020-03-18T02:37:33.42Z',
// updatedAt: '2020-03-18T02:37:33.42Z'
// }
//
return this.parseOrderV3 (response, market);
}
async createOrderV1 (symbol, type, side, amount, price = undefined, params = {}) {
if (type !== 'limit') {
throw new ExchangeError (this.id + ' allows limit orders only');
}
@@ -709,7 +779,10 @@ module.exports = class bittrex extends Exchange {
// }
// }
//
return this.extend (this.parseOrder (response), {
const result = this.safeValue (response, 'result', {});
return this.extend (this.parseOrder (result), {
'id': id,
'info': response,
'status': 'canceled',
});
}
@@ -977,6 +1050,7 @@ module.exports = class bittrex extends Exchange {
}
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -995,6 +1069,7 @@ module.exports = class bittrex extends Exchange {
'currency': feeCurrency,
},
'info': order,
'trades': undefined,
};
}
@@ -1119,6 +1194,7 @@ module.exports = class bittrex extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -1133,6 +1209,7 @@ module.exports = class bittrex extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
@@ -1175,6 +1252,7 @@ module.exports = class bittrex extends Exchange {
'datetime': this.iso8601 (timestamp),
'fee': this.safeValue (order, 'fee'),
'info': order,
'takerOrMaker': undefined,
};
}
@@ -1322,10 +1400,16 @@ module.exports = class bittrex extends Exchange {
}
} else if (api === 'v3') {
url += path;
if (Object.keys (params).length) {
url += '?' + this.rawencode (params);
let hashString = '';
if (method === 'POST') {
body = this.json (params);
hashString = body;
} else {
if (Object.keys (params).length) {
url += '?' + this.rawencode (params);
}
}
const contentHash = this.hash (this.encode (''), 'sha512', 'hex');
const contentHash = this.hash (this.encode (hashString), 'sha512', 'hex');
const timestamp = this.milliseconds ().toString ();
let auth = timestamp + url + method + contentHash;
const subaccountId = this.safeValue (this.options, 'subaccountId');
@@ -1342,6 +1426,9 @@ module.exports = class bittrex extends Exchange {
if (subaccountId !== undefined) {
headers['Api-Subaccount-Id'] = subaccountId;
}
if (method === 'POST') {
headers['Content-Type'] = 'application/json';
}
} else {
this.checkRequiredCredentials ();
url += api + '/';
@@ -1370,8 +1457,16 @@ module.exports = class bittrex extends Exchange {
// { success: false, message: "message" }
//
if (body[0] === '{') {
const feedback = this.id + ' ' + body;
let success = this.safeValue (response, 'success');
if (success === undefined) {
const code = this.safeString (response, 'code');
if (code !== undefined) {
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
if (code !== undefined) {
this.throwBroadlyMatchedException (this.exceptions['broad'], code, feedback);
}
}
// throw new ExchangeError (this.id + ' malformed response ' + this.json (response));
return;
}
@@ -1381,7 +1476,6 @@ module.exports = class bittrex extends Exchange {
}
if (!success) {
const message = this.safeString (response, 'message');
const feedback = this.id + ' ' + body;
if (message === 'APIKEY_INVALID') {
if (this.options['hasAlreadyAuthenticatedSuccessfully']) {
throw new DDoSProtection (feedback);
+3
View File
@@ -160,6 +160,7 @@ module.exports = class bitz extends Exchange {
// https://github.com/ccxt/ccxt/issues/3881
// https://support.bit-z.pro/hc/en-us/articles/360007500654-BOX-BOX-Token-
'BOX': 'BOX Token',
'LEO': 'LeoCoin',
'XRB': 'NANO',
'PXC': 'Pixiecoin',
'VTC': 'VoteCoin',
@@ -753,6 +754,7 @@ module.exports = class bitz extends Exchange {
const status = this.parseOrderStatus (this.safeString (order, 'status'));
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -768,6 +770,7 @@ module.exports = class bitz extends Exchange {
'trades': undefined,
'fee': undefined,
'info': order,
'average': undefined,
};
}
+425 -370
View File
@@ -1,99 +1,79 @@
'use strict';
// ---------------------------------------------------------------------------
const bittrex = require ('./bittrex.js');
const { ExchangeError, AuthenticationError, InvalidOrder, InsufficientFunds } = require ('./base/errors');
const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidOrder, AuthenticationError, InsufficientFunds, BadRequest } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class bleutrade extends bittrex {
module.exports = class bleutrade extends Exchange {
describe () {
const timeframes = {
'15m': '15m',
'20m': '20m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'3h': '3h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
};
const result = this.deepExtend (super.describe (), {
return this.deepExtend (super.describe (), {
'id': 'bleutrade',
'name': 'Bleutrade',
'countries': [ 'BR' ], // Brazil
'countries': ['BR'], // Brazil
'rateLimit': 1000,
'version': 'v2',
'certified': false,
'has': {
'CORS': true,
'cancelOrder': false, // todo
'createLimitOrder': false, // todo
'createMarketOrder': false, // todo
'createOrder': false, // todo
'editOrder': false, // todo
'withdraw': false, // todo
'fetchTrades': false,
'fetchTickers': true,
'fetchOrders': true,
'fetchTicker': true,
'fetchOrders': false,
'fetchClosedOrders': true,
'fetchOrderTrades': true,
'fetchOpenOrders': true,
'fetchWithdrawals': true,
'fetchOrderTrades': false,
'fetchLedger': true,
'fetchDepositAddress': true,
},
'timeframes': {
'15m': '15m',
'20m': '20m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'3h': '3h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
},
'timeframes': timeframes,
'hostname': 'bleutrade.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg',
'api': {
'public': 'https://{hostname}/api/v2',
'account': 'https://{hostname}/api/v2',
'market': 'https://{hostname}/api/v2',
'v3Private': 'https://{hostname}/api/v3/private',
'v3Public': 'https://{hostname}/api/v3/public',
},
'www': 'https://bleutrade.com',
'www': ['https://bleutrade.com'],
'doc': [
'https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0',
],
'fees': 'https://bleutrade.com/help/fees_and_deadlines',
'fees': 'https://bleutrade.com/fees/',
},
'api': {
'account': {
'get': [
'balance',
'balances',
'depositaddress',
'deposithistory',
'order',
'orders',
'orderhistory',
'withdrawhistory',
'withdraw',
],
},
'public': {
'get': [
'candles',
'currencies',
'markethistory',
'markets',
'marketsummaries',
'marketsummary',
'orderbook',
'ticker',
],
},
'v3Public': {
'get': [
'assets',
'markets',
'ticker',
'marketsummary',
'marketsummaries',
'orderbook',
'markethistory',
'candles',
'getassets',
'getmarkets',
'getticker',
'getmarketsummary',
'getmarketsummaries',
'getorderbook',
'getmarkethistory',
'getcandles',
],
},
'v3Private': {
'get': [
'post': [
'getbalance',
'getbalances',
'buylimit',
@@ -104,6 +84,7 @@ module.exports = class bleutrade extends bittrex {
'sellstoplimit',
'ordercancel',
'getopenorders',
'getcloseorders',
'getdeposithistory',
'getdepositaddress',
'getmytransactions',
@@ -114,111 +95,110 @@ module.exports = class bleutrade extends bittrex {
],
},
},
'fees': {
'funding': {
'withdraw': {
'ADC': 0.1,
'BTA': 0.1,
'BITB': 0.1,
'BTC': 0.001,
'BCC': 0.001,
'BTCD': 0.001,
'BTG': 0.001,
'BLK': 0.1,
'CDN': 0.1,
'CLAM': 0.01,
'DASH': 0.001,
'DCR': 0.05,
'DGC': 0.1,
'DP': 0.1,
'DPC': 0.1,
'DOGE': 10.0,
'EFL': 0.1,
'ETH': 0.01,
'EXP': 0.1,
'FJC': 0.1,
'BSTY': 0.001,
'GB': 0.1,
'NLG': 0.1,
'HTML': 1.0,
'LTC': 0.001,
'MONA': 0.01,
'MOON': 1.0,
'NMC': 0.015,
'NEOS': 0.1,
'NVC': 0.05,
'OK': 0.1,
'PPC': 0.1,
'POT': 0.1,
'XPM': 0.001,
'QTUM': 0.1,
'RDD': 0.1,
'SLR': 0.1,
'START': 0.1,
'SLG': 0.1,
'TROLL': 0.1,
'UNO': 0.01,
'VRC': 0.1,
'VTC': 0.1,
'XVP': 0.1,
'WDC': 0.001,
'ZET': 0.1,
},
},
},
'commonCurrencies': {
'EPC': 'Epacoin',
},
'exceptions': {
'exact': {
'Insufficient funds!': InsufficientFunds,
'Invalid Order ID': InvalidOrder,
'Invalid apikey or apisecret': AuthenticationError,
'ERR_INSUFICIENT_BALANCE': InsufficientFunds,
'ERR_LOW_VOLUME': BadRequest,
},
'broad': {
'Order is not open': InvalidOrder,
'Invalid Account / Api KEY / Api Secret': AuthenticationError, // also happens when an invalid nonce is used
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'taker': 0.25 / 100,
'maker': 0.25 / 100,
},
},
'options': {
// price precision by quote currency code
'pricePrecisionByCode': {
'USD': 3,
},
'parseOrderStatus': true,
'disableNonce': false,
'symbolSeparator': '_',
},
});
// bittrex inheritance override
result['timeframes'] = timeframes;
// undocumented api calls
// https://bleutrade.com/api/v3/public/tradingview/symbols?symbol=ETH_BTC
// https://bleutrade.com/api/v3/public/tradingview/config
// https://bleutrade.com/api/v3/public/tradingview/time
// https://bleutrade.com/api/v3/private/getcloseorders?market=ETH_BTC
// https://bleutrade.com/config contains the fees
}
async fetchCurrencies (params = {}) {
const response = await this.v3PublicGetGetassets (params);
const items = response['result'];
const result = {};
for (let i = 0; i < items.length; i++) {
// { Asset: 'USDT',
// AssetLong: 'Tether',
// MinConfirmation: 4,
// WithdrawTxFee: 1,
// WithdrawTxFeePercent: 0,
// SystemProtocol: 'ETHERC20',
// IsActive: true,
// InfoMessage: '',
// MaintenanceMode: false,
// MaintenanceMessage: '',
// FormatPrefix: '',
// FormatSufix: '',
// DecimalSeparator: '.',
// ThousandSeparator: ',',
// DecimalPlaces: 8,
// Currency: 'USDT',
// CurrencyLong: 'Tether',
// CoinType: 'ETHERC20' }
const item = items[i];
const id = this.safeString (item, 'Asset');
const code = this.safeCurrencyCode (id);
result[code] = {
'id': id,
'code': code,
'name': this.safeString (item, 'AssetLong'),
'active': this.safeValue (item, 'IsActive') && !this.safeValue (item, 'MaintenanceMode'),
'fee': this.safeFloat (item, 'WithdrawTxFee'),
'precision': this.safeFloat (item, 'DecimalPlaces'),
'info': item,
'limits': this.limits,
};
}
return result;
}
async fetchMarkets (params = {}) {
// https://github.com/ccxt/ccxt/issues/5668
const response = await this.publicGetMarkets (params);
const response = await this.v3PublicGetGetmarkets (params);
const result = [];
const markets = this.safeValue (response, 'result');
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
// { MarketName: 'LTC_USDT',
// MarketAsset: 'LTC',
// BaseAsset: 'USDT',
// MarketAssetLong: 'Litecoin',
// BaseAssetLong: 'Tether',
// IsActive: true,
// MinTradeSize: 0.0001,
// InfoMessage: '',
// MarketCurrency: 'LTC',
// BaseCurrency: 'USDT',
// MarketCurrencyLong: 'Litecoin',
// BaseCurrencyLong: 'Tether' }
const id = this.safeString (market, 'MarketName');
const baseId = this.safeString (market, 'MarketCurrency');
const quoteId = this.safeString (market, 'BaseCurrency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
let pricePrecision = 8;
if (quote in this.options['pricePrecisionByCode']) {
pricePrecision = this.options['pricePrecisionByCode'][quote];
}
const precision = {
'amount': 8,
'price': pricePrecision,
'price': 8,
};
// bittrex uses boolean values, bleutrade uses strings
let active = this.safeValue (market, 'IsActive', false);
if ((active !== 'false') && active) {
active = true;
} else {
active = false;
}
const active = this.safeValue (market, 'IsActive', false);
result.push ({
'id': id,
'symbol': symbol,
@@ -229,6 +209,8 @@ module.exports = class bleutrade extends bittrex {
'active': active,
'info': market,
'precision': precision,
'maker': this.fees['trading']['maker'],
'taker': this.fees['trading']['taker'],
'limits': {
'amount': {
'min': this.safeFloat (market, 'MinTradeSize'),
@@ -244,51 +226,6 @@ module.exports = class bleutrade extends bittrex {
return result;
}
parseOrderStatus (status) {
const statuses = {
'OK': 'closed',
'OPEN': 'open',
'CANCELED': 'canceled',
};
return this.safeString (statuses, status, status);
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
// Possible params
// orderstatus (ALL, OK, OPEN, CANCELED)
// ordertype (ALL, BUY, SELL)
// depth (optional, default is 500, max is 20000)
await this.loadMarkets ();
let market = undefined;
let marketId = 'ALL';
if (symbol !== undefined) {
market = this.market (symbol);
marketId = market['id'];
}
const request = {
'market': marketId,
'orderstatus': 'ALL',
};
const response = await this.accountGetOrders (this.extend (request, params));
return this.parseOrders (response['result'], market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
const response = await this.fetchOrders (symbol, since, limit, params);
return this.filterBy (response, 'status', 'closed');
}
getOrderIdField () {
return 'orderid';
}
parseSymbol (id) {
let [ base, quote ] = id.split (this.options['symbolSeparator']);
base = this.safeCurrencyCode (base);
quote = this.safeCurrencyCode (quote);
return base + '/' + quote;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
@@ -298,43 +235,104 @@ module.exports = class bleutrade extends bittrex {
if (limit !== undefined) {
request['depth'] = limit; // 50
}
const response = await this.publicGetOrderbook (this.extend (request, params));
const response = await this.v3PublicGetGetorderbook (this.extend (request, params));
const orderbook = this.safeValue (response, 'result');
if (!orderbook) {
throw new ExchangeError (this.id + ' publicGetOrderbook() returneded no result ' + this.json (response));
throw new ExchangeError (this.id + ' no orderbook data in ' + this.json (response));
}
return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity');
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
// Currently we can't set the makerOrTaker field, but if the user knows the order side then it can be
// determined (if the side of the trade is different to the side of the order, then the trade is maker).
// Similarly, the correct 'side' for the trade is that of the order.
// The trade fee can be set by the user, it is always 0.25% and is taken in the quote currency.
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'orderid': id,
'market': market['id'],
};
const response = await this.accountGetOrderhistory (this.extend (request, params));
return this.parseTrades (response['result'], undefined, since, limit, {
'order': id,
});
const response = await this.v3PublicGetGetmarketsummary (this.extend (request, params));
const ticker = response['result'][0];
return this.parseTicker (ticker, market);
}
async fetchTransactionsByType (type, code = undefined, since = undefined, limit = undefined, params = {}) {
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const method = (type === 'deposit') ? 'accountGetDeposithistory' : 'accountGetWithdrawhistory';
const response = await this[method] (params);
const result = this.parseTransactions (response['result']);
return this.filterByCurrencySinceLimit (result, code, since, limit);
const response = await this.v3PublicGetGetmarketsummaries (params);
const result = this.safeValue (response, 'result');
const tickers = [];
for (let i = 0; i < result.length; i++) {
const ticker = this.parseTicker (result[i]);
tickers.push (ticker);
}
return this.filterByArray (tickers, 'symbol', symbols);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchTransactionsByType ('deposit', code, since, limit, params);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchTransactionsByType ('withdrawal', code, since, limit, params);
parseTicker (ticker, market = undefined) {
// { TimeStamp: '2020-01-14 14:32:28',
// MarketName: 'LTC_USDT',
// MarketAsset: 'LTC',
// BaseAsset: 'USDT',
// MarketAssetName: 'Litecoin',
// BaseAssetName: 'Tether',
// PrevDay: 49.2867503,
// High: 56.78622664,
// Low: 49.27384025,
// Last: 53.94,
// Average: 51.37509368,
// Volume: 1.51282404,
// BaseVolume: 77.72147677,
// Bid: 53.62070218,
// Ask: 53.94,
// IsActive: 'true',
// InfoMessage: '',
// MarketCurrency: 'Litecoin',
// BaseCurrency: 'Tether' }
const timestamp = this.parse8601 (this.safeString (ticker, 'TimeStamp'));
let symbol = undefined;
const marketId = this.safeString (ticker, 'MarketName');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
} else {
symbol = this.parseSymbol (marketId);
}
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
const previous = this.safeFloat (ticker, 'PrevDay');
const last = this.safeFloat (ticker, 'Last');
let change = undefined;
let percentage = undefined;
if (last !== undefined) {
if (previous !== undefined) {
change = last - previous;
if (previous > 0) {
percentage = (change / previous) * 100;
}
}
}
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'High'),
'low': this.safeFloat (ticker, 'Low'),
'bid': this.safeFloat (ticker, 'Bid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'Ask'),
'askVolume': undefined,
'vwap': undefined,
'open': previous,
'close': last,
'last': last,
'previousClose': undefined,
'change': change,
'percentage': percentage,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'Volume'),
'quoteVolume': this.safeFloat (ticker, 'BaseVolume'),
'info': ticker,
};
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) {
@@ -357,49 +355,133 @@ module.exports = class bleutrade extends bittrex {
'market': market['id'],
'count': limit,
};
const response = await this.publicGetCandles (this.extend (request, params));
if ('result' in response) {
if (response['result']) {
return this.parseOHLCVs (response['result'], market, timeframe, since, limit);
}
}
const response = await this.v3PublicGetGetcandles (this.extend (request, params));
return this.parseOHLCVs (response['result'], market, timeframe, since, limit);
}
parseTrade (trade, market = undefined) {
const timestamp = this.parse8601 (trade['TimeStamp'] + '+00:00');
let side = undefined;
if (trade['OrderType'] === 'BUY') {
side = 'buy';
} else if (trade['OrderType'] === 'SELL') {
side = 'sell';
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
if (type !== 'limit') {
// todo: STOP-LIMIT and AMI order types are supported
throw new InvalidOrder (this.id + ' allows limit orders only');
}
const id = this.safeString2 (trade, 'TradeID', 'ID');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
let cost = undefined;
const price = this.safeFloat (trade, 'Price');
const amount = this.safeFloat (trade, 'Quantity');
if (amount !== undefined) {
if (price !== undefined) {
cost = price * amount;
}
await this.loadMarkets ();
const request = {
'rate': this.priceToPrecision (symbol, price),
'quantity': this.amountToPrecision (symbol, amount),
'tradeType': (side === 'buy') ? '1' : '0',
'market': this.marketId (symbol),
};
let response = undefined;
if (side === 'buy') {
response = await this.v3PrivatePostBuylimit (this.extend (request, params));
} else {
response = await this.v3PrivatePostSelllimit (this.extend (request, params));
}
// { success: true,
// message: "",
// result: "161105236" },
return {
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': 'limit',
'side': side,
'order': undefined,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
'info': response,
'id': this.safeString (response, 'result'),
};
}
async cancelOrder (id, symbol = undefined, params = {}) {
const request = {
'orderid': id,
};
const response = await this.v3PrivatePostOrdercancel (this.extend (request, params));
// { success: true, message: '', result: '' }
return response;
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
const response = await this.v3PrivatePostGetopenorders (this.extend (request, params));
const items = this.safeValue (response, 'result', []);
return this.parseOrders (items, market, since, limit);
}
parseSymbol (id) {
let [ base, quote ] = id.split (this.options['symbolSeparator']);
base = this.safeCurrencyCode (base);
quote = this.safeCurrencyCode (quote);
return base + '/' + quote;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.v3PrivatePostGetbalances (params);
const result = { 'info': response };
const items = response['result'];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const currencyId = this.safeString (item, 'Asset');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (item, 'Available');
account['total'] = this.safeFloat (item, 'Balance');
result[code] = account;
}
return this.parseBalance (result);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
const response = await this.v3PrivatePostGetcloseorders (this.extend (request, params));
const orders = this.safeValue (response, 'result', []);
return this.parseOrders (orders, market, since, limit);
}
async fetchTransactionsWithMethod (method, code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const response = await this[method] (params);
const transactions = this.safeValue (response, 'result', []);
return this.parseTransactions (transactions, code, since, limit);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchTransactionsWithMethod ('v3PrivatePostGetdeposithistory', code, since, limit, params);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchTransactionsWithMethod ('v3PrivatePostGetwithdrawhistory', code, since, limit, params);
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'asset': currency['id'],
};
const response = await this.v3PrivatePostGetdepositaddress (this.extend (request, params));
// { success: true,
// message: '',
// result:
// { Asset: 'ETH',
// AssetName: 'Ethereum',
// DepositAddress: '0x748c5c8jhksjdfhd507d3aa9',
// Currency: 'ETH',
// CurrencyName: 'Ethereum' } }
const item = response['result'];
const address = this.safeString (item, 'DepositAddress');
return {
'currency': code,
'address': this.checkAddress (address),
// 'tag': tag,
'info': item,
};
}
@@ -477,7 +559,7 @@ module.exports = class bleutrade extends bittrex {
'currency': code,
};
} else if (part.indexOf ('order id') === 0) {
referenceId = part.replace ('order id', '');
referenceId = part.replace ('order id ', '');
}
//
// does not belong to Ledger, related to parseTransaction
@@ -520,64 +602,44 @@ module.exports = class bleutrade extends bittrex {
}
async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) {
//
// if (code === undefined) {
// throw new ExchangeError (this.id + ' fetchClosedOrders requires a `symbol` argument');
// }
//
await this.loadMarkets ();
const request = {};
//
// if (code !== undefined) {
// const currency = this.market (code);
// request['asset'] = currency['id'];
// }
//
const response = await this.v3PrivateGetGetmytransactions (this.extend (request, params));
return this.parseLedger (response['result'], code, since, limit);
// only seems to return 100 items and there is no documented way to change page size or offset
const request = {
};
const response = await this.v3PrivatePostGetmytransactions (this.extend (request, params));
const items = response['result'];
return this.parseLedger (items, code, since, limit);
}
parseOrder (order, market = undefined) {
//
// fetchOrders
// fetchClosedOrders
//
// {
// OrderId: '107220258',
// Exchange: 'LTC_BTC',
// Type: 'SELL',
// Quantity: '2.13040000',
// QuantityRemaining: '0.00000000',
// Price: '0.01332672',
// Status: 'OK',
// Created: '2018-06-30 04:55:50',
// QuantityBaseTraded: '0.02839125',
// Comments: ''
// }
// { OrderID: 89742658,
// Exchange: 'DOGE_BTC',
// Type: 'BUY',
// Quantity: 10000,
// QuantityRemaining: 0,
// QuantityBaseTraded: 0,
// Price: 6.6e-7,
// Status: 'OK',
// Created: '2018-02-16 08:55:36',
// Comments: '' }
//
let side = this.safeString2 (order, 'OrderType', 'Type');
const isBuyOrder = (side === 'LIMIT_BUY') || (side === 'BUY');
const isSellOrder = (side === 'LIMIT_SELL') || (side === 'SELL');
if (isBuyOrder) {
side = 'buy';
}
if (isSellOrder) {
side = 'sell';
}
// We parse different fields in a very specific order.
// Order might well be closed and then canceled.
let status = undefined;
if (('Opened' in order) && order['Opened']) {
status = 'open';
}
if (('Closed' in order) && order['Closed']) {
status = 'closed';
}
if (('CancelInitiated' in order) && order['CancelInitiated']) {
status = 'canceled';
}
if (('Status' in order) && this.options['parseOrderStatus']) {
status = this.parseOrderStatus (this.safeString (order, 'Status'));
}
// fetchOpenOrders
//
// { OrderID: 161105302,
// Exchange: 'ETH_BTC',
// Type: 'SELL',
// Quantity: 0.4,
// QuantityRemaining: 0.4,
// QuantityBaseTraded: 0,
// Price: 0.04,
// Status: 'OPEN',
// Created: '2020-01-22 09:21:27',
// Comments: { String: '', Valid: true }
const side = this.safeString (order, 'Type').toLowerCase ();
const status = this.parseOrderStatus (this.safeString (order, 'Status'));
let symbol = undefined;
const marketId = this.safeString (order, 'Exchange');
if (marketId === undefined) {
@@ -593,41 +655,9 @@ module.exports = class bleutrade extends bittrex {
}
}
let timestamp = undefined;
if ('Opened' in order) {
timestamp = this.parse8601 (order['Opened'] + '+00:00');
}
if ('Created' in order) {
timestamp = this.parse8601 (order['Created'] + '+00:00');
}
let lastTradeTimestamp = undefined;
if (('TimeStamp' in order) && (order['TimeStamp'] !== undefined)) {
lastTradeTimestamp = this.parse8601 (order['TimeStamp'] + '+00:00');
}
if (('Closed' in order) && (order['Closed'] !== undefined)) {
lastTradeTimestamp = this.parse8601 (order['Closed'] + '+00:00');
}
if (timestamp === undefined) {
timestamp = lastTradeTimestamp;
}
let fee = undefined;
let commission = undefined;
if ('Commission' in order) {
commission = 'Commission';
} else if ('CommissionPaid' in order) {
commission = 'CommissionPaid';
}
if (commission) {
fee = {
'cost': this.safeFloat (order, commission),
};
if (market !== undefined) {
fee['currency'] = market['quote'];
} else if (symbol !== undefined) {
const currencyIds = symbol.split ('/');
const quoteCurrencyId = currencyIds[1];
fee['currency'] = this.safeCurrencyCode (quoteCurrencyId);
}
}
let price = this.safeFloat (order, 'Price');
let cost = undefined;
const amount = this.safeFloat (order, 'Quantity');
@@ -647,13 +677,14 @@ module.exports = class bleutrade extends bittrex {
}
}
const average = this.safeFloat (order, 'PricePerUnit');
const id = this.safeString2 (order, 'OrderUuid', 'OrderId');
const id = this.safeString (order, 'OrderID');
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': 'limit',
'side': side,
@@ -664,54 +695,56 @@ module.exports = class bleutrade extends bittrex {
'filled': filled,
'remaining': remaining,
'status': status,
'fee': fee,
'fee': undefined,
'trades': undefined,
};
}
parseOrderStatus (status) {
const statuses = {
'OK': 'closed',
'OPEN': 'open',
'CANCELED': 'canceled',
};
return this.safeString (statuses, status, status);
}
parseTransaction (transaction, currency = undefined) {
//
// deposit:
//
// {
// Id: '96974373',
// Coin: 'DOGE',
// Amount: '12.05752192',
// TimeStamp: '2017-09-29 08:10:09',
// Label: 'DQqSjjhzCm3ozT4vAevMUHgv4vsi9LBkoE',
// }
// { ID: 118698752,
// Timestamp: '2020-01-21 11:16:09',
// Asset: 'ETH',
// Amount: 1,
// TransactionID: '',
// Status: 'CONFIRMED',
// Label: '0x748c5c8228d0c596f4d07f338blah',
// Symbol: 'ETH' }
//
// withdrawal:
//
// {
// Id: '98009125',
// Coin: 'DOGE',
// Amount: '-483858.64312050',
// TimeStamp: '2017-11-22 22:29:05',
// Label: '483848.64312050;DJVJZ58tJC8UeUv9Tqcdtn6uhWobouxFLT;10.00000000',
// TransactionId: '8563105276cf798385fee7e5a563c620fea639ab132b089ea880d4d1f4309432',
// }
// { ID: 689281,
// Timestamp: '2019-07-05 13:14:43',
// Asset: 'BTC',
// Amount: -0.108959,
// TransactionID: 'da48d6901fslfjsdjflsdjfls852b87e362cad1',
// Status: 'CONFIRMED',
// Label: '0.1089590;35wztHPMgrebFvvblah;0.00100000',
// Symbol: 'BTC' }
//
// {
// "Id": "95820181",
// "Coin": "BTC",
// "Amount": "-0.71300000",
// "TimeStamp": "2017-07-19 17:14:24",
// "Label": "0.71200000;PER9VM2txt4BTdfyWgvv3GziECRdVEPN63;0.00100000",
// "TransactionId": "CANCELED"
// }
//
const id = this.safeString (transaction, 'Id');
const id = this.safeString (transaction, 'ID');
let amount = this.safeFloat (transaction, 'Amount');
let type = 'deposit';
if (amount < 0) {
amount = Math.abs (amount);
type = 'withdrawal';
}
const currencyId = this.safeString (transaction, 'Coin');
const currencyId = this.safeString (transaction, 'Asset');
const code = this.safeCurrencyCode (currencyId, currency);
const label = this.safeString (transaction, 'Label');
const timestamp = this.parse8601 (this.safeString (transaction, 'TimeStamp'));
let txid = this.safeString (transaction, 'TransactionId');
const timestamp = this.parse8601 (this.safeString (transaction, 'Timestamp'));
let txid = this.safeString (transaction, 'TransactionID');
let address = undefined;
let feeCost = undefined;
const labelParts = label.split (';');
@@ -751,31 +784,53 @@ module.exports = class bleutrade extends bittrex {
};
}
nonce () {
return this.milliseconds ();
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.implodeParams (this.urls['api'][api], {
'hostname': this.hostname,
}) + '/';
if (api === 'v3Private' || api === 'account') {
if (api === 'v3Private') {
this.checkRequiredCredentials ();
if (api === 'account') {
url += api + '/';
}
if (((api === 'account') && (path !== 'withdraw')) || (path === 'openorders')) {
url += method.toLowerCase ();
}
const request = {
'apikey': this.apiKey,
'nonce': this.nonce (),
};
request['nonce'] = this.nonce ();
url += path + '?' + this.urlencode (this.extend (request, params));
const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512');
headers = { 'apisign': signature };
} else {
url += api + '/' + method.toLowerCase () + path;
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
url += path + '?' + this.urlencode (params);
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
}
// examples...
// {"success":false,"message":"Erro: Order is not open.","result":""} <-- 'error' is spelt wrong
// {"success":false,"message":"Error: Very low volume.","result":"ERR_LOW_VOLUME"}
// {"success":false,"message":"Error: Insuficient Balance","result":"ERR_INSUFICIENT_BALANCE"}
//
if (body[0] === '{') {
const success = this.safeValue (response, 'success');
if (success === undefined) {
throw new ExchangeError (this.id + ': malformed response: ' + this.json (response));
}
if (!success) {
const feedback = this.id + ' ' + body;
const errorCode = this.safeString (response, 'result');
this.throwBroadlyMatchedException (this.exceptions['broad'], errorCode, feedback);
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
const errorMessage = this.safeString (response, 'message');
this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback);
this.throwExactlyMatchedException (this.exceptions['exact'], errorMessage, feedback);
throw new ExchangeError (feedback);
}
}
}
};
+3 -1
View File
@@ -452,6 +452,7 @@ module.exports = class braziliex extends Exchange {
const status = (filledPercentage === 1.0) ? 'closed' : 'open';
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -467,6 +468,7 @@ module.exports = class braziliex extends Exchange {
'trades': undefined,
'fee': fee,
'info': info,
'average': undefined,
};
}
@@ -590,7 +592,7 @@ module.exports = class braziliex extends Exchange {
headers = {
'Content-type': 'application/x-www-form-urlencoded',
'Key': this.apiKey,
'Sign': this.decode (signature),
'Sign': signature,
};
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
+5
View File
@@ -151,6 +151,8 @@ module.exports = class btcalpha extends Exchange {
},
},
'info': market,
'baseId': undefined,
'quoteId': undefined,
});
}
return result;
@@ -315,6 +317,7 @@ module.exports = class btcalpha extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'status': status,
@@ -329,6 +332,8 @@ module.exports = class btcalpha extends Exchange {
'trades': trades,
'fee': undefined,
'info': order,
'lastTradeTimestamp': undefined,
'average': undefined,
};
}
+12 -1
View File
@@ -242,7 +242,16 @@ module.exports = class btcbox extends Exchange {
parseOrder (order, market = undefined) {
//
// {"id":11,"datetime":"2014-10-21 10:47:20","type":"sell","price":42000,"amount_original":1.2,"amount_outstanding":1.2,"status":"closed","trades":[]}
// {
// "id":11,
// "datetime":"2014-10-21 10:47:20",
// "type":"sell",
// "price":42000,
// "amount_original":1.2,
// "amount_outstanding":1.2,
// "status":"closed",
// "trades":[]
// }
//
const id = this.safeString (order, 'id');
const datetimeString = this.safeString (order, 'datetime');
@@ -281,6 +290,7 @@ module.exports = class btcbox extends Exchange {
const side = this.safeString (order, 'type');
return {
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -296,6 +306,7 @@ module.exports = class btcbox extends Exchange {
'trades': trades,
'fee': undefined,
'info': order,
'average': undefined,
};
}
-368
View File
@@ -1,368 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
// ---------------------------------------------------------------------------
module.exports = class btcchina extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'btcchina',
'name': 'BTCChina',
'countries': [ 'CN' ],
'rateLimit': 1500,
'version': 'v1',
'has': {
'CORS': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766368-465b3286-5ed6-11e7-9a11-0f6467e1d82b.jpg',
'api': {
'plus': 'https://plus-api.btcchina.com/market',
'public': 'https://data.btcchina.com/data',
'private': 'https://api.btcchina.com/api_trade_v1.php',
},
'www': 'https://www.btcchina.com',
'doc': 'https://www.btcchina.com/apidocs',
},
'api': {
'plus': {
'get': [
'orderbook',
'ticker',
'trade',
],
},
'public': {
'get': [
'historydata',
'orderbook',
'ticker',
'trades',
],
},
'private': {
'post': [
'BuyIcebergOrder',
'BuyOrder',
'BuyOrder2',
'BuyStopOrder',
'CancelIcebergOrder',
'CancelOrder',
'CancelStopOrder',
'GetAccountInfo',
'getArchivedOrder',
'getArchivedOrders',
'GetDeposits',
'GetIcebergOrder',
'GetIcebergOrders',
'GetMarketDepth',
'GetMarketDepth2',
'GetOrder',
'GetOrders',
'GetStopOrder',
'GetStopOrders',
'GetTransactions',
'GetWithdrawal',
'GetWithdrawals',
'RequestWithdrawal',
'SellIcebergOrder',
'SellOrder',
'SellOrder2',
'SellStopOrder',
],
},
},
'markets': {
'BTC/CNY': { 'id': 'btccny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'api': 'public', 'plus': false },
'LTC/CNY': { 'id': 'ltccny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'api': 'public', 'plus': false },
'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'api': 'public', 'plus': false },
'BCH/CNY': { 'id': 'bcccny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'api': 'plus', 'plus': true },
'ETH/CNY': { 'id': 'ethcny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'api': 'plus', 'plus': true },
},
});
}
async fetchMarkets (params = {}) {
const request = {
'market': 'all',
};
const markets = await this.publicGetTicker (this.extend (request, params));
const result = [];
const keys = Object.keys (markets);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const market = markets[key];
const parts = key.split ('_');
const id = parts[1];
const baseId = id.slice (0, 3);
const quoteId = id.slice (3, 6);
let base = baseId.toUpperCase ();
let quote = quoteId.toUpperCase ();
base = this.safeCurrencyCode (base);
quote = this.safeCurrencyCode (quote);
const symbol = base + '/' + quote;
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'info': market,
});
}
return result;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostGetAccountInfo (params);
const balances = this.safeValue (response, 'result');
const result = { 'info': balances };
const codes = Object.keys (this.currencies);
for (let i = 0; i < codes.length; i++) {
const code = codes[i];
const currency = this.currency (code);
const account = this.account ();
const currencyId = currency['id'];
if (currencyId in balances['balance']) {
account['total'] = parseFloat (balances['balance'][currencyId]['amount']);
}
if (currencyId in balances['frozen']) {
account['used'] = parseFloat (balances['frozen'][currencyId]['amount']);
}
result[code] = account;
}
return this.parseBalance (result);
}
createMarketRequest (market) {
const request = {};
const field = (market['plus']) ? 'symbol' : 'market';
request[field] = market['id'];
return request;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const method = market['api'] + 'GetOrderbook';
const request = this.createMarketRequest (market);
const response = await this[method] (this.extend (request, params));
const timestamp = this.safeTimestamp (response, 'date');
return this.parseOrderBook (response, timestamp);
}
parseTicker (ticker, market) {
const timestamp = this.safeTimestamp (ticker, 'date');
const last = this.safeFloat (ticker, 'last');
return {
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': this.safeFloat (ticker, 'buy'),
'ask': this.safeFloat (ticker, 'sell'),
'vwap': this.safeFloat (ticker, 'vwap'),
'open': this.safeFloat (ticker, 'open'),
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'vol'),
'quoteVolume': undefined,
'info': ticker,
};
}
parseTickerPlus (ticker, market) {
const timestamp = this.safeInteger (ticker, 'Timestamp');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, 'High'),
'low': this.safeFloat (ticker, 'Low'),
'bid': this.safeFloat (ticker, 'BidPrice'),
'ask': this.safeFloat (ticker, 'AskPrice'),
'vwap': undefined,
'open': this.safeFloat (ticker, 'Open'),
'last': this.safeFloat (ticker, 'Last'),
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': this.safeFloat (ticker, 'Volume24H'),
'quoteVolume': undefined,
'info': ticker,
};
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const method = market['api'] + 'GetTicker';
const request = this.createMarketRequest (market);
const response = await this[method] (this.extend (request, params));
const ticker = this.safeValue (response, 'ticker');
if (market['plus']) {
return this.parseTickerPlus (ticker, market);
}
return this.parseTicker (ticker, market);
}
parseTrade (trade, market) {
const timestamp = this.safeTimestamp (trade, 'date');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'amount');
let cost = undefined;
if (amount !== undefined) {
if (price !== undefined) {
cost = amount * price;
}
}
const id = this.safeString (trade, 'tid');
return {
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': market['symbol'],
'type': undefined,
'side': undefined,
'price': price,
'amount': amount,
'cost': cost,
};
}
parseTradePlus (trade, market) {
const timestamp = this.parse8601 (this.safeString (trade, 'timestamp'));
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'size');
let cost = undefined;
if (amount !== undefined) {
if (price !== undefined) {
cost = amount * price;
}
}
const side = this.safeStringLower (trade, 'side');
return {
'id': undefined,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': market['symbol'],
'type': undefined,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
};
}
parseTradesPlus (trades, market = undefined) {
const result = [];
for (let i = 0; i < trades.length; i++) {
result.push (this.parseTradePlus (trades[i], market));
}
return result;
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
let method = market['api'] + 'GetTrade';
const request = this.createMarketRequest (market);
if (market['plus']) {
const now = this.milliseconds ();
request['start_time'] = now - 86400000;
request['end_time'] = now;
} else {
method += 's'; // trades vs trade
}
const response = await this[method] (this.extend (request, params));
if (market['plus']) {
return this.parseTradesPlus (response['trades'], market);
}
return this.parseTrades (response, market, since, limit);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const method = 'privatePost' + this.capitalize (side) + 'Order2';
const request = {};
const id = market['id'].toUpperCase ();
if (type === 'market') {
request['params'] = [ undefined, amount, id ];
} else {
request['params'] = [ price, amount, id ];
}
const response = await this[method] (this.extend (request, params));
const orderId = this.safeString (response, 'id');
return {
'info': response,
'id': orderId,
};
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const market = params['market']; // TODO fixme
const request = {
'params': [ id, market ],
};
return await this.privatePostCancelOrder (this.extend (request, params));
}
nonce () {
return this.microseconds ();
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'][api] + '/' + path;
if (api === 'private') {
this.checkRequiredCredentials ();
let p = [];
if ('params' in params) {
p = params['params'];
}
const nonce = this.nonce ();
const request = {
'method': path,
'id': nonce,
'params': p,
};
p = p.join (',');
body = this.json (request);
const query = [
'tonce=' + nonce,
'accesskey=' + this.apiKey,
'requestmethod=' + method.toLowerCase (),
'id=' + nonce,
'method=' + path,
'params=' + p,
].join ('&');
const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1');
const auth = this.encode (this.apiKey + ':' + signature);
headers = {
'Authorization': 'Basic ' + this.stringToBase64 (auth),
'Json-Rpc-Tonce': nonce,
};
} else {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
};
+7 -3
View File
@@ -4,6 +4,7 @@
const Exchange = require ('./base/Exchange');
const { ExchangeError, OrderNotFound, ArgumentsRequired, InvalidOrder, DDoSProtection } = require ('./base/errors');
const { ROUND } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -415,7 +416,7 @@ module.exports = class btcmarkets extends Exchange {
}
parseTrade (trade, market = undefined) {
const timestamp = this.safeTimestamp (trade, 'timestamp');
const timestamp = this.safeTimestamp (trade, 'date');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
@@ -467,8 +468,8 @@ module.exports = class btcmarkets extends Exchange {
});
request['currency'] = market['quote'];
request['instrument'] = market['base'];
request['price'] = parseInt (price * multiplier);
request['volume'] = parseInt (amount * multiplier);
request['price'] = parseInt (this.decimalToPrecision (price * multiplier, ROUND, 0));
request['volume'] = parseInt (this.decimalToPrecision (amount * multiplier, ROUND, 0));
request['orderSide'] = orderSide;
request['ordertype'] = this.capitalize (type);
request['clientRequestId'] = this.nonce ().toString ();
@@ -564,6 +565,7 @@ module.exports = class btcmarkets extends Exchange {
'currency': feeCurrencyCode,
'cost': feeCost,
},
'takerOrMaker': undefined,
};
}
@@ -611,9 +613,11 @@ module.exports = class btcmarkets extends Exchange {
lastTradeTimestamp = trades[numTrades - 1]['timestamp'];
}
const id = this.safeString (order, 'id');
const clientOrderId = this.safeString (order, 'clientRequestId');
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
+5 -1
View File
@@ -15,7 +15,7 @@ module.exports = class btctradeua extends Exchange {
'countries': [ 'UA' ], // Ukraine,
'rateLimit': 3000,
'has': {
'CORS': true,
'CORS': false,
'createMarketOrder': false,
'fetchOpenOrders': true,
},
@@ -320,6 +320,7 @@ module.exports = class btctradeua extends Exchange {
}
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'timestamp': timestamp, // until they fix their timestamp
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -333,6 +334,9 @@ module.exports = class btctradeua extends Exchange {
'remaining': this.safeFloat (order, 'amnt_trade'),
'trades': undefined,
'info': order,
'cost': undefined,
'average': undefined,
'fee': undefined,
};
}
+2
View File
@@ -563,6 +563,7 @@ module.exports = class buda extends Exchange {
};
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -578,6 +579,7 @@ module.exports = class buda extends Exchange {
'trades': undefined,
'fee': fee,
'info': order,
'average': undefined,
};
}
+11 -13
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { BadSymbol, ExchangeError, AuthenticationError, ArgumentsRequired, ExchangeNotAvailable } = require ('./base/errors');
const { RateLimitExceeded, BadSymbol, OrderNotFound, ExchangeError, AuthenticationError, ArgumentsRequired, ExchangeNotAvailable } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -77,18 +77,10 @@ module.exports = class bw extends Exchange {
},
'fees': {
'trading': {
'tierBased': true,
'tierBased': false,
'percentage': true,
'taker': 0.2 / 100,
'maker': 0.2 / 100,
'tiers': {
'taker': [
[ 0, 0.2 / 100 ],
],
'maker': [
[ 0, 0.2 / 100 ],
],
},
},
'funding': {
},
@@ -97,7 +89,9 @@ module.exports = class bw extends Exchange {
'exact': {
'999': AuthenticationError,
'1000': ExchangeNotAvailable, // {"datas":null,"resMsg":{"message":"getKlines error:data not exitsts\uff0cplease wait ,dataType=4002_KLINE_1M","method":null,"code":"1000"}}
'2012': OrderNotFound, // {"datas":null,"resMsg":{"message":"entrust not exists or on dealing with system","method":null,"code":"2012"}}
'5017': BadSymbol, // {"datas":null,"resMsg":{"message":"market not exist","method":null,"code":"5017"}}
'10001': RateLimitExceeded, // {"resMsg":{"code":"10001","message":"API frequency limit"}}
},
},
'api': {
@@ -314,7 +308,7 @@ module.exports = class bw extends Exchange {
},
'withdraw': {
'min': undefined,
'max': parseFloat (this.safeInteger (currency, 'onceDrawLimit')),
'max': this.safeFloat (currency, 'onceDrawLimit'),
},
},
};
@@ -343,8 +337,10 @@ module.exports = class bw extends Exchange {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
}
if ((symbol === undefined) && (market !== undefined)) {
if (market !== undefined) {
symbol = market['symbol'];
} else {
symbol = marketId;
}
const timestamp = this.milliseconds ();
const close = parseFloat (this.safeValue (ticker, 1));
@@ -614,7 +610,7 @@ module.exports = class bw extends Exchange {
const result = { 'info': response };
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeInteger (balance, 'currencyTypeId');
const currencyId = this.safeString (balance, 'currencyTypeId');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'amount');
@@ -670,6 +666,7 @@ module.exports = class bw extends Exchange {
'status': 'open',
'fee': undefined,
'trades': undefined,
'clientOrderId': undefined,
};
}
@@ -739,6 +736,7 @@ module.exports = class bw extends Exchange {
return {
'info': order,
'id': this.safeString (order, 'entrustId'),
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
+1925
View File
File diff suppressed because it is too large. Load diff
+57 -38
View File
@@ -47,10 +47,10 @@ module.exports = class bytetrade extends Exchange {
'1M': '1M',
},
'urls': {
'test': 'https://api-v2-test.bytetrade.com',
'test': 'https://api-v2-test.byte-trade.com',
'logo': 'https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg',
'api': 'https://api-v2.bytetrade.com',
'www': 'https://www.bytetrade.com',
'api': 'https://api-v2.byte-trade.com',
'www': 'https://www.byte-trade.com',
'doc': 'https://github.com/Bytetrade/bytetrade-official-api-docs/wiki',
},
'api': {
@@ -92,6 +92,7 @@ module.exports = class bytetrade extends Exchange {
},
},
'commonCurrencies': {
'44': 'ByteHub',
'48': 'Blocktonic',
},
'exceptions': {
@@ -116,7 +117,7 @@ module.exports = class bytetrade extends Exchange {
code = this.safeString (currency, 'name');
}
const name = this.safeString (currency, 'fullname');
// in bytetrade.com DEX, request https://api-v2.bytetrade.com/currencies will return currencies,
// in byte-trade.com DEX, request https://api-v2.byte-trade.com/currencies will return currencies,
// the api doc is https://github.com/Bytetrade/bytetrade-official-api-docs/wiki/rest-api#get-currencies-get-currencys-supported-in-bytetradecom
// we can see the coin name is none-unique in the result, the coin which code is 18 is the CyberMiles ERC20, and the coin which code is 35 is the CyberMiles main chain, but their name is same.
// that is because bytetrade is a DEX, supports people create coin with the same name, but the id(code) of coin is unique, so we should use the id or name and id as the identity of coin.
@@ -216,6 +217,9 @@ module.exports = class bytetrade extends Exchange {
const id = this.safeString (market, 'symbol');
let base = this.safeString (market, 'baseName');
let quote = this.safeString (market, 'quoteName');
const normalBase = base.split ('@')[0];
const normalQuote = quote.split ('@')[0];
const normalSymbol = normalBase + '/' + normalQuote;
const baseId = this.safeString (market, 'base');
const quoteId = this.safeString (market, 'quote');
if (baseId in this.commonCurrencies) {
@@ -230,9 +234,6 @@ module.exports = class bytetrade extends Exchange {
const price = this.safeValue (limits, 'price', {});
const precision = this.safeValue (market, 'precision', {});
const active = this.safeString (market, 'active');
const normalBase = base.split ('@')[0];
const normalQuote = quote.split ('@')[0];
const normalSymbol = normalBase + '/' + normalQuote;
const entry = {
'id': id,
'symbol': symbol,
@@ -540,6 +541,7 @@ module.exports = class bytetrade extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': datetime,
'lastTradeTimestamp': lastTradeTimestamp,
@@ -554,6 +556,7 @@ module.exports = class bytetrade extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
@@ -575,23 +578,25 @@ module.exports = class bytetrade extends Exchange {
typeNum = 1;
} else {
typeNum = 2;
price = 0;
}
const normalSymbol = market['normalSymbol'];
const baseId = market['baseId'];
const baseCurrency = this.currency (market['base']);
const amountTruncated = this.amountToPrecision (symbol, amount);
const amountChain = this.toWei (amountTruncated, 'ether', baseCurrency['precision']['amount']);
const amountChain = this.toWei (amountTruncated, baseCurrency['precision']['amount']);
const quoteId = market['quoteId'];
const quoteCurrency = this.currency (market['quote']);
const priceRounded = this.priceToPrecision (symbol, price);
const priceChain = this.toWei (priceRounded, 'ether', quoteCurrency['precision']['amount']);
const priceChain = this.toWei (priceRounded, quoteCurrency['precision']['amount']);
const now = this.milliseconds ();
const expiration = this.milliseconds ();
let datetime = this.iso8601 (now);
datetime = datetime.split ('.')[0];
let expirationDatetime = this.iso8601 (expiration);
expirationDatetime = expirationDatetime.split ('.')[0];
const chainName = 'Sagittarius';
const defaultDappId = 'Sagittarius';
const dappId = this.safeString (params, 'dappId', defaultDappId);
const defaultFee = this.safeString (this.options, 'fee', '300000000000000');
const fee = this.safeString (params, 'fee', defaultFee);
const eightBytes = this.integerPow ('2', '64');
@@ -622,8 +627,8 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (parseInt (baseId), 4),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
const txByteStringArray = [
@@ -652,8 +657,8 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (parseInt (baseId), 4),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
const txbytestring = this.binaryConcatArray (txByteStringArray);
@@ -696,7 +701,7 @@ module.exports = class bytetrade extends Exchange {
],
],
'validate_type': 0,
'dapp': 'Sagittarius',
'dapp': dappId,
'signatures': [
mySignature,
],
@@ -725,6 +730,8 @@ module.exports = class bytetrade extends Exchange {
'cost': undefined,
'trades': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
};
}
@@ -825,7 +832,8 @@ module.exports = class bytetrade extends Exchange {
datetime = datetime.split ('.')[0];
let expirationDatetime = this.iso8601 (expiration);
expirationDatetime = expirationDatetime.split ('.')[0];
const chainName = 'Sagittarius';
const defaultDappId = 'Sagittarius';
const dappId = this.safeString (params, 'dappId', defaultDappId);
const byteStringArray = [
this.numberToBE (1, 32),
this.numberToLE (Math.floor (now / 1000), 4),
@@ -844,8 +852,8 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (parseInt (baseId), 4),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
const bytestring = this.binaryConcatArray (byteStringArray);
@@ -871,7 +879,7 @@ module.exports = class bytetrade extends Exchange {
],
],
'validate_type': 0,
'dapp': 'Sagittarius',
'dapp': dappId,
'signatures': [
mySignature,
],
@@ -900,10 +908,12 @@ module.exports = class bytetrade extends Exchange {
'cost': undefined,
'trades': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
};
}
async transfer (code, amount, address, params = {}) {
async transfer (code, amount, address, message = '', params = {}) {
this.checkRequiredDependencies ();
if (this.apiKey === undefined) {
throw new ArgumentsRequired ('transfer requires this.apiKey');
@@ -911,7 +921,7 @@ module.exports = class bytetrade extends Exchange {
await this.loadMarkets ();
const currency = this.currency (code);
const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING);
const amountChain = this.toWei (amountTruncate, 'ether', currency['precision']['amount']);
const amountChain = this.toWei (amountTruncate, currency['precision']['amount']);
const assetType = parseInt (currency['id']);
const now = this.milliseconds ();
const expiration = now;
@@ -920,7 +930,8 @@ module.exports = class bytetrade extends Exchange {
let expirationDatetime = this.iso8601 (expiration);
expirationDatetime = expirationDatetime.split ('.')[0];
const feeAmount = '300000000000000';
const chainName = 'Sagittarius';
const defaultDappId = 'Sagittarius';
const dappId = this.safeString (params, 'dappId', defaultDappId);
const eightBytes = this.integerPow ('2', '64');
const byteStringArray = [
this.numberToBE (1, 32),
@@ -928,7 +939,7 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (1, 1),
this.numberToLE (Math.floor (expiration / 1000), 4),
this.numberToLE (1, 1),
this.numberToLE (0, 1),
this.numberToLE (28, 1),
this.numberToLE (0, 8),
this.numberToLE (feeAmount, 8), // string for 32 bit php
this.numberToLE (this.apiKey.length, 1),
@@ -938,10 +949,13 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (assetType, 4),
this.numberToLE (this.integerDivide (amountChain, eightBytes), 8),
this.numberToLE (this.integerModulo (amountChain, eightBytes), 8),
this.numberToLE (1, 1),
this.numberToLE (message.length, 1),
this.stringToBinary (this.encode (message)),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
const bytestring = this.binaryConcatArray (byteStringArray);
@@ -955,18 +969,19 @@ module.exports = class bytetrade extends Exchange {
'to': address,
'asset_type': parseInt (currency['id']),
'amount': amountChain.toString (),
'message': message,
};
const fatty = {
'timestamp': datetime,
'expiration': expirationDatetime,
'operations': [
[
0,
28,
operation,
],
],
'validate_type': 0,
'dapp': 'Sagittarius',
'dapp': dappId,
'signatures': [
mySignature,
],
@@ -999,6 +1014,9 @@ module.exports = class bytetrade extends Exchange {
'remaining': undefined,
'cost': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
'trades': undefined,
};
}
@@ -1178,12 +1196,13 @@ module.exports = class bytetrade extends Exchange {
datetime = datetime.split ('.')[0];
let expirationDatetime = this.iso8601 (expiration);
expirationDatetime = expirationDatetime.split ('.')[0];
const chainName = 'Sagittarius';
const defaultDappId = 'Sagittarius';
const dappId = this.safeString (params, 'dappId', defaultDappId);
const feeAmount = '300000000000000';
const currency = this.currency (code);
const coinId = currency['id'];
const amountTruncate = this.decimalToPrecision (amount, TRUNCATE, currency['info']['transferPrecision'], DECIMAL_PLACES, NO_PADDING);
const amountChain = this.toWei (amountTruncate, 'ether', currency['info']['externalPrecision']);
const amountChain = this.toWei (amountTruncate, currency['info']['externalPrecision']);
const eightBytes = this.integerPow ('2', '64');
let assetFee = 0;
let byteStringArray = [];
@@ -1210,8 +1229,8 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (this.integerModulo (assetFee, eightBytes), 8),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
} else {
@@ -1240,8 +1259,8 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (this.integerModulo (amountChain, eightBytes), 8),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
this.numberToLE (chainName.length, 1),
this.stringToBinary (this.encode (chainName)),
this.numberToLE (dappId.length, 1),
this.stringToBinary (this.encode (dappId)),
this.numberToLE (0, 1),
];
}
@@ -1273,7 +1292,7 @@ module.exports = class bytetrade extends Exchange {
],
],
'validate_type': 0,
'dapp': 'Sagittarius',
'dapp': dappId,
'signatures': [
mySignature,
],
@@ -1281,7 +1300,7 @@ module.exports = class bytetrade extends Exchange {
request = {
'chainType': chainId,
'trObj': this.json (fatty),
'chainContractAddresss': chainContractAddress,
'chainContractAddress': chainContractAddress,
};
} else {
operation = {
@@ -1310,7 +1329,7 @@ module.exports = class bytetrade extends Exchange {
],
],
'validate_type': 0,
'dapp': 'Sagittarius',
'dapp': dappId,
'signatures': [
mySignature,
],
@@ -1320,14 +1339,14 @@ module.exports = class bytetrade extends Exchange {
'chainType': chainId,
'toExternalAddress': 'noneed',
'trObj': this.json (fatty),
'chainContractAddresss': chainContractAddress,
'chainContractAddress': chainContractAddress,
};
} else {
request = {
'chainType': chainId,
'toExternalAddress': address,
'trObj': this.json (fatty),
'chainContractAddresss': chainContractAddress,
'chainContractAddress': chainContractAddress,
};
}
}
+12 -5
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, NullResponse, InvalidOrder, NotSupported, InsufficientFunds, InvalidNonce, OrderNotFound, RateLimitExceeded } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, AuthenticationError, NullResponse, InvalidOrder, NotSupported, InsufficientFunds, InvalidNonce, OrderNotFound, RateLimitExceeded } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -15,7 +15,7 @@ module.exports = class cex extends Exchange {
'countries': [ 'GB', 'EU', 'CY', 'RU' ],
'rateLimit': 1500,
'has': {
'CORS': true,
'CORS': false,
'fetchCurrencies': true,
'fetchTickers': true,
'fetchOHLCV': true,
@@ -126,6 +126,8 @@ module.exports = class cex extends Exchange {
'Invalid Order': InvalidOrder,
'Order not found': OrderNotFound,
'Rate limit exceeded': RateLimitExceeded,
'Invalid API key': AuthenticationError,
'There was an error while placing your order': InvalidOrder,
},
},
'options': {
@@ -352,6 +354,7 @@ module.exports = class cex extends Exchange {
'max': undefined,
},
},
'active': undefined,
});
}
return result;
@@ -803,11 +806,14 @@ module.exports = class cex extends Exchange {
'currency': market['quote'],
},
'info': item,
'type': undefined,
'takerOrMaker': undefined,
});
}
}
return {
'id': orderId,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -823,6 +829,7 @@ module.exports = class cex extends Exchange {
'trades': trades,
'fee': fee,
'info': order,
'average': undefined,
};
}
@@ -1152,12 +1159,12 @@ module.exports = class cex extends Exchange {
if (Array.isArray (response)) {
return response; // public endpoints may return []-arrays
}
if (body === 'true') {
return;
}
if (response === undefined) {
throw new NullResponse (this.id + ' returned ' + this.json (response));
}
if (response === true || response === 'true') {
return;
}
if ('e' in response) {
if ('ok' in response) {
if (response['ok'] === 'ok') {
-861
View File
@@ -1,861 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidAddress, InsufficientFunds, InvalidNonce, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class cobinhood extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'cobinhood',
'name': 'COBINHOOD',
'countries': [ 'TW' ],
'rateLimit': 1000 / 10,
'version': 'v1',
'has': {
'fetchCurrencies': true,
'fetchTickers': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchOrderTrades': true,
'fetchOrder': true,
'fetchDepositAddress': true,
'createDepositAddress': true,
'fetchDeposits': true,
'fetchWithdrawals': true,
'withdraw': true,
'fetchMyTrades': true,
'editOrder': true,
},
'requiredCredentials': {
'apiKey': true,
'secret': false,
},
'timeframes': {
// the first two don't seem to work at all
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'3h': '3h',
'6h': '6h',
'12h': '12h',
'1d': '1D',
'1w': '7D',
'2w': '14D',
'1M': '1M',
},
'urls': {
'referral': 'https://cobinhood.com?referrerId=a9d57842-99bb-4d7c-b668-0479a15a458b',
'logo': 'https://user-images.githubusercontent.com/1294454/35755576-dee02e5c-0878-11e8-989f-1595d80ba47f.jpg',
'api': 'https://api.cobinhood.com',
'www': 'https://cobinhood.com',
'doc': 'https://cobinhood.github.io/api-public',
},
'api': {
'system': {
'get': [
'info',
'time',
'messages',
'messages/{message_id}',
],
},
'admin': {
'get': [
'system/messages',
'system/messages/{message_id}',
],
'post': [
'system/messages',
],
'patch': [
'system/messages/{message_id}',
],
'delete': [
'system/messages/{message_id}',
],
},
'public': {
'get': [
'market/fundingbook/precisions/{currency_id}',
'market/fundingbooks/{currency_id}',
'market/tickers',
'market/currencies',
'market/quote_currencies',
'market/trading_pairs',
'market/orderbook/precisions/{trading_pair_id}',
'market/orderbooks/{trading_pair_id}',
'market/stats',
'market/tickers', // fetchTickers
'market/tickers/{trading_pair_id}',
'market/trades/{trading_pair_id}',
'market/trades_history/{trading_pair_id}',
'market/trading_pairs',
'chart/candles/{trading_pair_id}',
'system/time',
],
},
'private': {
'get': [
'funding/auto_offerings',
'funding/auto_offerings/{currency_id}',
'funding/funding_history',
'funding/fundings',
'funding/loans',
'funding/loans/{loan_id}',
'trading/orders/{order_id}',
'trading/orders/{order_id}/trades',
'trading/orders',
'trading/order_history',
'trading/positions',
'trading/positions/{trading_pair_id}',
'trading/positions/{trading_pair_id}/claimable_size',
'trading/trades',
'trading/trades/{trade_id}',
'trading/volume',
'wallet/balances',
'wallet/ledger',
'wallet/limits/withdrawal',
'wallet/generic_deposits',
'wallet/generic_deposits/{generic_deposit_id}',
'wallet/generic_withdrawals',
'wallet/generic_withdrawals/{generic_withdrawal_id}',
// older endpoints
'wallet/deposit_addresses',
'wallet/deposit_addresses/iota',
'wallet/withdrawal_addresses',
'wallet/withdrawal_frozen',
'wallet/withdrawals/{withdrawal_id}',
'wallet/withdrawals',
'wallet/deposits/{deposit_id}',
'wallet/deposits',
],
'patch': [
'trading/positions/{trading_pair_id}',
],
'post': [
'funding/auto_offerings',
'funding/fundings',
'trading/check_order',
'trading/orders',
// older endpoints
'wallet/deposit_addresses',
'wallet/transfer',
'wallet/withdrawal_addresses',
'wallet/withdrawals',
'wallet/withdrawals/fee',
],
'put': [
'funding/fundings/{funding_id}',
'trading/orders/{order_id}',
],
'delete': [
'funding/auto_offerings/{currency_id}',
'funding/fundings/{funding_id}',
'funding/loans/{loan_id}',
'trading/orders/{order_id}',
'trading/positions/{trading_pair_id}',
'wallet/generic_withdrawals/{generic_withdrawal_id}',
'wallet/withdrawal_addresses/{wallet_id}',
],
},
},
'fees': {
'trading': {
'maker': 0.0,
'taker': 0.0,
},
},
'precision': {
'amount': 8,
'price': 8,
},
'exceptions': {
'insufficient_balance': InsufficientFunds,
'invalid_order_size': InvalidOrder,
'invalid_nonce': InvalidNonce,
'unauthorized_scope': PermissionDenied,
'invalid_address': InvalidAddress,
'parameter_error': OrderNotFound,
},
'commonCurrencies': {
'SMT': 'SocialMedia.Market',
'MTN': 'Motion Token',
},
});
}
async fetchCurrencies (params = {}) {
const response = await this.publicGetMarketCurrencies (params);
const currencies = response['result']['currencies'];
const result = {};
for (let i = 0; i < currencies.length; i++) {
const currency = currencies[i];
const id = this.safeString (currency, 'currency');
const name = this.safeString (currency, 'name');
const code = this.safeCurrencyCode (id);
const minUnit = this.safeFloat (currency, 'min_unit');
result[code] = {
'id': id,
'code': code,
'name': name,
'active': true,
'fiat': false,
'precision': this.precisionFromString (currency['min_unit']),
'limits': {
'amount': {
'min': minUnit,
'max': undefined,
},
'price': {
'min': minUnit,
'max': undefined,
},
'deposit': {
'min': minUnit,
'max': undefined,
},
'withdraw': {
'min': minUnit,
'max': undefined,
},
},
'funding': {
'withdraw': {
'fee': this.safeFloat (currency, 'withdrawal_fee'),
},
'deposit': {
'fee': this.safeFloat (currency, 'deposit_fee'),
},
},
'info': currency,
};
}
return result;
}
async fetchMarkets (params = {}) {
const response = await this.publicGetMarketTradingPairs (params);
const markets = this.safeValue (response['result'], 'trading_pairs');
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = this.safeString (market, 'id');
const [ baseId, quoteId ] = id.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'amount': 8,
'price': this.precisionFromString (market['quote_increment']),
};
const active = this.safeValue (market, 'is_active', true);
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'precision': precision,
'limits': {
'amount': {
'min': this.safeFloat (market, 'base_min_size'),
'max': this.safeFloat (market, 'base_max_size'),
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
parseTicker (ticker, market = undefined) {
let symbol = undefined;
if (market === undefined) {
const marketId = this.safeString (ticker, 'trading_pair_id');
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
} else {
const [ baseId, quoteId ] = marketId.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (ticker, 'timestamp');
const last = this.safeFloat (ticker, 'last_trade_price');
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (ticker, '24h_high'),
'low': this.safeFloat (ticker, '24h_low'),
'bid': this.safeFloat (ticker, 'highest_bid'),
'bidVolume': undefined,
'ask': this.safeFloat (ticker, 'lowest_ask'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': last,
'last': last,
'previousClose': undefined,
'change': this.safeFloat (ticker, 'percentChanged24hr'),
'percentage': undefined,
'average': undefined,
'baseVolume': this.safeFloat (ticker, '24h_volume'),
'quoteVolume': this.safeFloat (ticker, 'quote_volume'),
'info': ticker,
};
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'trading_pair_id': market['id'],
};
const response = await this.publicGetMarketTickersTradingPairId (this.extend (request, params));
const ticker = this.safeValue (response['result'], 'ticker');
return this.parseTicker (ticker, market);
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.publicGetMarketTickers (params);
const tickers = this.safeValue (response['result'], 'tickers');
const result = [];
for (let i = 0; i < tickers.length; i++) {
result.push (this.parseTicker (tickers[i]));
}
return this.indexBy (result, 'symbol');
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'trading_pair_id': this.marketId (symbol),
};
if (limit !== undefined) {
request['limit'] = limit; // 100
}
const response = await this.publicGetMarketOrderbooksTradingPairId (this.extend (request, params));
return this.parseOrderBook (response['result']['orderbook'], undefined, 'bids', 'asks', 0, 2);
}
parseTrade (trade, market = undefined) {
let symbol = undefined;
if (market) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (trade, 'timestamp');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'size');
let cost = undefined;
if (price !== undefined) {
if (amount !== undefined) {
cost = price * amount;
}
}
// you can't determine your side from maker/taker side and vice versa
// you can't determine if your order/trade was a maker or a taker based
// on just the side of your order/trade
// https://github.com/ccxt/ccxt/issues/4300
// let side = (trade['maker_side'] === 'bid') ? 'sell' : 'buy';
const side = undefined;
const id = this.safeString (trade, 'id');
return {
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'id': id,
'order': undefined,
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
};
}
async fetchTrades (symbol, since = undefined, limit = 50, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'trading_pair_id': market['id'],
'limit': limit, // default 20, but that seems too little
};
const response = await this.publicGetMarketTradesTradingPairId (this.extend (request, params));
const trades = this.safeValue (response['result'], 'trades');
return this.parseTrades (trades, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) {
return [
// they say that timestamps are Unix Timestamps in seconds, but in fact those are milliseconds
ohlcv['timestamp'],
parseFloat (ohlcv['open']),
parseFloat (ohlcv['high']),
parseFloat (ohlcv['low']),
parseFloat (ohlcv['close']),
parseFloat (ohlcv['volume']),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
//
// they say in their docs that end_time defaults to current server time
// but if you don't specify it, their range limits does not allow you to query anything
//
// they also say that start_time defaults to 0,
// but most calls fail if you do not specify any of end_time
//
// to make things worse, their docs say it should be a Unix Timestamp
// but with seconds it fails, so we set milliseconds (somehow it works that way)
//
const endTime = this.milliseconds ();
const request = {
'trading_pair_id': market['id'],
'timeframe': this.timeframes[timeframe],
'end_time': endTime,
};
if (since !== undefined) {
request['start_time'] = since;
}
const response = await this.publicGetChartCandlesTradingPairId (this.extend (request, params));
const ohlcv = this.safeValue (response['result'], 'candles');
return this.parseOHLCVs (ohlcv, market, timeframe, since, limit);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetWalletBalances (params);
const result = { 'info': response };
const balances = this.safeValue (response['result'], 'balances');
for (let i = 0; i < balances.length; i++) {
const balance = balances[i];
const currencyId = this.safeString (balance, 'currency');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['used'] = this.safeFloat (balance, 'on_order');
account['total'] = this.safeFloat (balance, 'total');
result[code] = account;
}
return this.parseBalance (result);
}
parseOrderStatus (status) {
const statuses = {
'filled': 'closed',
'rejected': 'closed',
'partially_filled': 'open',
'pending_cancellation': 'open',
'pending_modification': 'open',
'open': 'open',
'new': 'open',
'queued': 'open',
'cancelled': 'canceled',
'triggered': 'triggered',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// {
// 'completed_at': None,
// 'eq_price': '0',
// 'filled': '0',
// 'id': '88426800-beae-4407-b4a1-f65cef693542',
// 'price': '0.00000507',
// 'side': 'bid',
// 'size': '3503.6489',
// 'source': 'exchange',
// 'state': 'open',
// 'timestamp': 1535258403597,
// 'trading_pair_id': 'ACT-BTC',
// 'type': 'limit',
// }
//
let symbol = undefined;
if (market === undefined) {
const marketId = this.safeString2 (order, 'trading_pair', 'trading_pair_id');
market = this.safeValue (this.markets_by_id, marketId);
}
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (order, 'timestamp');
const price = this.safeFloat (order, 'price');
const average = this.safeFloat (order, 'eq_price');
const amount = this.safeFloat (order, 'size');
const filled = this.safeFloat (order, 'filled');
let remaining = undefined;
let cost = undefined;
if (filled !== undefined && average !== undefined) {
cost = average * filled;
} else if (average !== undefined) {
cost = average * amount;
}
if (amount !== undefined) {
if (filled !== undefined) {
remaining = amount - filled;
}
}
const status = this.parseOrderStatus (this.safeString (order, 'state'));
let side = this.safeString (order, 'side');
if (side === 'bid') {
side = 'buy';
} else if (side === 'ask') {
side = 'sell';
}
return {
'id': this.safeString (order, 'id'),
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
'status': status,
'symbol': symbol,
'type': this.safeString (order, 'type'), // market, limit, stop, stop_limit, trailing_stop, fill_or_kill
'side': side,
'price': price,
'cost': cost,
'average': average,
'amount': amount,
'filled': filled,
'remaining': remaining,
'trades': undefined,
'fee': undefined,
'info': order,
};
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
side = (side === 'sell') ? 'ask' : 'bid';
const request = {
'trading_pair_id': market['id'],
'type': type, // market, limit, stop, stop_limit
'side': side,
'size': this.amountToPrecision (symbol, amount),
};
if (type !== 'market') {
request['price'] = this.priceToPrecision (symbol, price);
}
const response = await this.privatePostTradingOrders (this.extend (request, params));
const order = this.parseOrder (response['result']['order'], market);
const id = order['id'];
this.orders[id] = order;
return order;
}
async editOrder (id, symbol, type, side, amount, price, params = {}) {
await this.loadMarkets ();
const request = {
'order_id': id,
'price': this.priceToPrecision (symbol, price),
'size': this.amountToPrecision (symbol, amount),
};
const response = await this.privatePutTradingOrdersOrderId (this.extend (request, params));
return this.parseOrder (this.extend (response, {
'id': id,
}));
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'order_id': id,
};
const response = await this.privateDeleteTradingOrdersOrderId (this.extend (request, params));
return this.parseOrder (this.extend (response, {
'id': id,
}));
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'order_id': id.toString (),
};
const response = await this.privateGetTradingOrdersOrderId (this.extend (request, params));
return this.parseOrder (response['result']['order']);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const result = await this.privateGetTradingOrders (params);
const orders = this.parseOrders (result['result']['orders'], undefined, since, limit);
if (symbol !== undefined) {
return this.filterBySymbolSinceLimit (orders, symbol, since, limit);
}
return this.filterBySinceLimit (orders, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['trading_pair_id'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 50, max 100
}
const response = await this.privateGetTradingOrderHistory (this.extend (request, params));
const orders = this.parseOrders (response['result']['orders'], market, since, limit);
if (symbol !== undefined) {
return this.filterBySymbolSinceLimit (orders, symbol, since, limit);
}
return this.filterBySinceLimit (orders, since, limit);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'order_id': id,
};
const response = await this.privateGetTradingOrdersOrderIdTrades (this.extend (request, params));
const market = (symbol === undefined) ? undefined : this.market (symbol);
return this.parseTrades (response['result']['trades'], market);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {};
if (symbol !== undefined) {
request['trading_pair_id'] = market['id'];
}
const response = await this.privateGetTradingTrades (this.extend (request, params));
return this.parseTrades (response['result']['trades'], market, since, limit);
}
async createDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
// 'ledger_type' is required, see: https://cobinhood.github.io/api-public/#create-new-deposit-address
const ledgerType = this.safeString (params, 'ledger_type', 'exchange');
const request = {
'currency': currency['id'],
'ledger_type': ledgerType,
};
const response = await this.privatePostWalletDepositAddresses (this.extend (request, params));
const address = this.safeString (response['result']['deposit_address'], 'address');
const tag = this.safeString (response['result']['deposit_address'], 'memo');
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': response,
};
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currency': currency['id'],
};
const response = await this.privateGetWalletDepositAddresses (this.extend (request, params));
//
// { success: true,
// result: { deposit_addresses: [ { address: "abcdefg",
// blockchain_id: "eosio",
// created_at: 1536768050235,
// currency: "EOS",
// memo: "12345678",
// type: "exchange" } ] } }
//
const addresses = this.safeValue (response['result'], 'deposit_addresses', []);
let address = undefined;
let tag = undefined;
if (addresses.length > 0) {
address = this.safeString (addresses[0], 'address');
tag = this.safeString2 (addresses[0], 'memo', 'tag');
}
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': response,
};
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currency': currency['id'],
'amount': amount,
'address': address,
};
if (tag !== undefined) {
request['memo'] = tag;
}
const response = await this.privatePostWalletWithdrawals (this.extend (request, params));
return {
'id': undefined,
'info': response,
};
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (code === undefined) {
throw new ExchangeError (this.id + ' fetchDeposits() requires a currency code argument');
}
const currency = this.currency (code);
const request = {
'currency': currency['id'],
};
const response = await this.privateGetWalletDeposits (this.extend (request, params));
return this.parseTransactions (response['result']['deposits'], currency);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (code === undefined) {
throw new ExchangeError (this.id + ' fetchWithdrawals() requires a currency code argument');
}
const currency = this.currency (code);
const request = {
'currency': currency['id'],
};
const response = await this.privateGetWalletWithdrawals (this.extend (request, params));
return this.parseTransactions (response['result']['withdrawals'], currency);
}
parseTransactionStatus (status) {
const statuses = {
'tx_pending_two_factor_auth': 'pending',
'tx_pending_email_auth': 'pending',
'tx_pending_approval': 'pending',
'tx_approved': 'pending',
'tx_processing': 'pending',
'tx_pending': 'pending',
'tx_sent': 'pending',
'tx_cancelled': 'canceled',
'tx_timeout': 'failed',
'tx_invalid': 'failed',
'tx_rejected': 'failed',
'tx_confirmed': 'ok',
};
return this.safeString (statuses, status, status);
}
parseTransaction (transaction, currency = undefined) {
const timestamp = this.safeInteger (transaction, 'created_at');
const currencyId = this.safeString (transaction, 'currency');
const code = this.safeCurrencyCode (currencyId, currency);
let id = undefined;
const withdrawalId = this.safeString (transaction, 'withdrawal_id');
const depositId = this.safeString (transaction, 'deposit_id');
let type = undefined;
let address = undefined;
if (withdrawalId !== undefined) {
type = 'withdrawal';
id = withdrawalId;
address = this.safeString (transaction, 'to_address');
} else if (depositId !== undefined) {
type = 'deposit';
id = depositId;
address = this.safeString (transaction, 'from_address');
}
const additionalInfo = this.safeValue (transaction, 'additional_info', {});
const tag = this.safeString (additionalInfo, 'memo');
return {
'info': transaction,
'id': id,
'txid': this.safeString (transaction, 'txhash'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'address': address,
'tag': tag, // refix it properly
'type': type,
'amount': this.safeFloat (transaction, 'amount'),
'currency': code,
'status': this.parseTransactionStatus (transaction['status']),
'updated': undefined,
'fee': {
'cost': this.safeFloat (transaction, 'fee'),
'rate': undefined,
},
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
let query = this.omit (params, this.extractParams (path));
headers = {};
if (api === 'private') {
this.checkRequiredCredentials ();
// headers['device_id'] = this.apiKey;
headers['nonce'] = this.nonce ().toString ();
headers['Authorization'] = this.apiKey;
}
if (method === 'GET') {
query = this.urlencode (query);
if (query.length) {
url += '?' + query;
}
} else {
headers['Content-type'] = 'application/json; charset=UTF-8';
body = this.json (query);
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (code < 400 || code >= 600) {
return;
}
if (body[0] !== '{') {
throw new ExchangeError (this.id + ' ' + body);
}
const feedback = this.id + ' ' + this.json (response);
const errorCode = this.safeValue (response['error'], 'error_code');
if (method === 'DELETE' || method === 'GET') {
if (errorCode === 'parameter_error') {
if (url.indexOf ('trading/orders/') >= 0) {
// Cobinhood returns vague "parameter_error" on fetchOrder() and cancelOrder() calls
// for invalid order IDs as well as orders that are not "open"
throw new InvalidOrder (feedback);
}
}
}
this.throwExactlyMatchedException (this.exceptions, errorCode, feedback);
throw new ExchangeError (feedback);
}
nonce () {
return this.milliseconds ();
}
};
+1
View File
@@ -11,6 +11,7 @@ module.exports = class coinbaseprime extends coinbasepro {
return this.deepExtend (super.describe (), {
'id': 'coinbaseprime',
'name': 'Coinbase Prime',
'pro': true,
'urls': {
'test': {
'public': 'https://api-public.sandbox.prime.coinbase.com',
+4
View File
@@ -15,6 +15,7 @@ module.exports = class coinbasepro extends Exchange {
'countries': [ 'US' ],
'rateLimit': 1000,
'userAgent': this.userAgents['chrome'],
'pro': true,
'has': {
'cancelAllOrders': true,
'CORS': true,
@@ -581,6 +582,7 @@ module.exports = class coinbasepro extends Exchange {
const side = this.safeString (order, 'side');
return {
'id': id,
'clientOrderId': undefined,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -595,6 +597,8 @@ module.exports = class coinbasepro extends Exchange {
'filled': filled,
'remaining': remaining,
'fee': fee,
'average': undefined,
'trades': undefined,
};
}
+3
View File
@@ -192,6 +192,7 @@ module.exports = class coincheck extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -206,6 +207,8 @@ module.exports = class coincheck extends Exchange {
'cost': cost,
'fee': undefined,
'info': order,
'average': undefined,
'trades': undefined,
};
}
+2
View File
@@ -352,6 +352,7 @@ module.exports = class coinegg extends Exchange {
const id = this.safeString (order, 'id');
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -367,6 +368,7 @@ module.exports = class coinegg extends Exchange {
'trades': undefined,
'fee': undefined,
'info': info,
'average': undefined,
};
}
+2 -1
View File
@@ -464,6 +464,7 @@ module.exports = class coinex extends Exchange {
const side = this.safeString (order, 'type');
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -724,7 +725,7 @@ module.exports = class coinex extends Exchange {
const code = this.safeCurrencyCode (currencyId, currency);
const timestamp = this.safeTimestamp (transaction, 'create_time');
const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit';
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'), type);
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const amount = this.safeFloat (transaction, 'amount');
let feeCost = this.safeFloat (transaction, 'tx_fee');
if (type === 'deposit') {
+20
View File
@@ -275,6 +275,23 @@ module.exports = class coinfalcon extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "id":"8bdd79f4-8414-40a2-90c3-e9f4d6d1eef4"
// "market":"IOT-BTC"
// "price":"0.0000003"
// "size":"4.0"
// "size_filled":"3.0"
// "fee":"0.0075"
// "fee_currency_code":"iot"
// "funds":"0.0"
// "status":"canceled"
// "order_type":"buy"
// "post_only":false
// "operation_type":"market_order"
// "created_at":"2018-01-12T21:14:06.747828Z"
// }
//
if (market === undefined) {
const marketId = this.safeString (order, 'market');
if (marketId in this.markets_by_id) {
@@ -308,6 +325,7 @@ module.exports = class coinfalcon extends Exchange {
const side = this.safeString (order, 'order_type');
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'status': status,
@@ -322,6 +340,8 @@ module.exports = class coinfalcon extends Exchange {
'trades': undefined,
'fee': undefined,
'info': order,
'lastTradeTimestamp': undefined,
'average': undefined,
};
}
+3 -1
View File
@@ -59,7 +59,6 @@ module.exports = class coinfloor extends Exchange {
'markets': {
'BTC/GBP': { 'id': 'XBT/GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'XBT', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }},
'BTC/EUR': { 'id': 'XBT/EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR', 'precision': { 'price': 0, 'amount': 4 }},
'ETH/GBP': { 'id': 'ETH/GBP', 'symbol': 'ETH/GBP', 'base': 'ETH', 'quote': 'GBP', 'baseId': 'ETH', 'quoteId': 'GBP', 'precision': { 'price': 0, 'amount': 4 }},
},
'exceptions': {
'exact': {
@@ -446,6 +445,7 @@ module.exports = class coinfloor extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -459,6 +459,8 @@ module.exports = class coinfloor extends Exchange {
'remaining': undefined,
'cost': cost,
'fee': undefined,
'average': undefined,
'trades': undefined,
};
}
+6
View File
@@ -126,10 +126,13 @@ module.exports = class coinmarketcap extends Exchange {
'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network)
'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin)
'Maggie': 'Maggie',
'Monarch': 'Monarch', // conflict with MyToken (MT)
'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025
'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all
'NetCoin': 'NetCoin',
'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai)
'Plair': 'Plair', // conflict with PLA (PLANET)
'PlayChip': 'PlayChip', // conflict with PLA (PLANET)
'Polcoin': 'Polcoin',
'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token)
'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network)
@@ -167,6 +170,9 @@ module.exports = class coinmarketcap extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'info': market,
'active': undefined,
'precision': this.precision,
'limits': this.limits,
});
}
}
+272 -15
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, RateLimitExceeded, InsufficientFunds } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -16,8 +16,14 @@ module.exports = class coinmate extends Exchange {
'rateLimit': 1000,
'has': {
'CORS': true,
'fetchBalance': true,
'fetchOrders': true,
'fetchOrder': true,
'fetchMyTrades': true,
'fetchTransactions': true,
'fetchOpenOrders': true,
'createOrder': true,
'cancelOrder': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27811229-c1efb510-606c-11e7-9a36-84ba2ce412d8.jpg',
@@ -66,6 +72,7 @@ module.exports = class coinmate extends Exchange {
'openOrders',
'order',
'orderHistory',
'orderById',
'pusherAuth',
'redeemVoucher',
'replaceByBuyLimit',
@@ -92,8 +99,74 @@ module.exports = class coinmate extends Exchange {
},
'fees': {
'trading': {
'maker': 0.05 / 100,
'taker': 0.15 / 100,
'tierBased': true,
'percentage': true,
'maker': 0.12 / 100,
'taker': 0.25 / 100,
'tiers': {
'taker': [
[0, 0.25 / 100],
[10000, 0.23 / 100],
[100000, 0.21 / 100],
[250000, 0.20 / 100],
[500000, 0.15 / 100],
[1000000, 0.13 / 100],
[3000000, 0.10 / 100],
[15000000, 0.05 / 100],
],
'maker': [
[0, 0.12 / 100],
[10000, 0.11 / 100],
[1000000, 0.10 / 100],
[250000, 0.08 / 100],
[500000, 0.05 / 100],
[1000000, 0.03 / 100],
[3000000, 0.02 / 100],
[15000000, 0],
],
},
},
'promotional': {
'trading': {
'maker': 0.05 / 100,
'taker': 0.15 / 100,
'tiers': {
'taker': [
[0, 0.15 / 100],
[10000, 0.14 / 100],
[100000, 0.13 / 100],
[250000, 0.12 / 100],
[500000, 0.11 / 100],
[1000000, 0.1 / 100],
[3000000, 0.08 / 100],
[15000000, 0.05 / 100],
],
'maker': [
[0, 0.05 / 100],
[10000, 0.04 / 100],
[1000000, 0.03 / 100],
[250000, 0.02 / 100],
[500000, 0],
[1000000, 0],
[3000000, 0],
[15000000, 0],
],
},
},
},
},
'options': {
'promotionalMarkets': ['ETH/EUR', 'ETH/CZK', 'ETH/BTC', 'XRP/EUR', 'XRP/CZK', 'XRP/BTC', 'DASH/EUR', 'DASH/CZK', 'DASH/BTC', 'BCH/EUR', 'BCH/CZK', 'BCH/BTC'],
},
'exceptions': {
'exact': {
'No order with given ID': OrderNotFound,
},
'broad': {
'Not enough account balance available': InsufficientFunds,
'Incorrect order ID': InvalidOrder,
'Minimum Order Size ': InvalidOrder,
'TOO MANY REQUESTS': RateLimitExceeded,
},
},
});
@@ -130,6 +203,12 @@ module.exports = class coinmate extends Exchange {
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const promotionalMarkets = this.safeValue (this.options, 'promotionalMarkets', []);
let fees = this.safeValue (this.fees, 'trading');
if (this.inArray (symbol, promotionalMarkets)) {
const promotionalFees = this.safeValue (this.fees, 'promotional', {});
fees = this.safeValue (promotionalFees, 'trading', fees);
}
result.push ({
'id': id,
'symbol': symbol,
@@ -138,6 +217,8 @@ module.exports = class coinmate extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'active': undefined,
'maker': fees['maker'],
'taker': fees['taker'],
'info': market,
'precision': {
'price': this.safeInteger (market, 'priceDecimals'),
@@ -326,6 +407,10 @@ module.exports = class coinmate extends Exchange {
const request = {
'limit': limit,
};
if (symbol !== undefined) {
const market = this.market (symbol);
request['currencyPair'] = market['id'];
}
if (since !== undefined) {
request['timestampFrom'] = since;
}
@@ -449,6 +534,144 @@ module.exports = class coinmate extends Exchange {
return this.parseTrades (data, market, since, limit);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
const response = await this.privatePostOpenOrders (this.extend ({}, params));
const extension = { 'status': 'open' };
return this.parseOrders (response['data'], undefined, since, limit, extension);
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'currencyPair': market['id'],
};
// offset param that appears in other parts of the API doesn't appear to be supported here
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privatePostOrderHistory (this.extend (request, params));
return this.parseOrders (response['data'], market, since, limit);
}
parseOrderStatus (status) {
const statuses = {
'FILLED': 'closed',
'CANCELLED': 'canceled',
'OPEN': 'open',
};
return this.safeString (statuses, status, status);
}
parseOrderType (type) {
const types = {
'LIMIT': 'limit',
'MARKET': 'market',
};
return this.safeString (types, type, type);
}
parseOrder (order, market = undefined) {
//
// limit sell
//
// {
// id: 781246605,
// timestamp: 1584480015133,
// trailingUpdatedTimestamp: null,
// type: 'SELL',
// currencyPair: 'ETH_BTC',
// price: 0.0345,
// amount: 0.01,
// stopPrice: null,
// originalStopPrice: null,
// marketPriceAtLastUpdate: null,
// marketPriceAtOrderCreation: null,
// orderTradeType: 'LIMIT',
// hidden: false,
// trailing: false,
// clientOrderId: null
// }
//
// limit buy
//
// {
// id: 67527001,
// timestamp: 1517931722613,
// trailingUpdatedTimestamp: null,
// type: 'BUY',
// price: 5897.24,
// remainingAmount: 0.002367,
// originalAmount: 0.1,
// stopPrice: null,
// originalStopPrice: null,
// marketPriceAtLastUpdate: null,
// marketPriceAtOrderCreation: null,
// status: 'CANCELLED',
// orderTradeType: 'LIMIT',
// hidden: false,
// avgPrice: null,
// trailing: false,
// }
//
const id = this.safeString (order, 'id');
const timestamp = this.safeInteger (order, 'timestamp');
const side = this.safeStringLower (order, 'type');
const price = this.safeFloat (order, 'price');
const amount = this.safeFloat2 (order, 'originalAmount', 'amount');
const remaining = this.safeFloat (order, 'remainingAmount', amount);
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const type = this.parseOrderType (this.safeString (order, 'orderTradeType'));
let filled = undefined;
let cost = undefined;
if ((amount !== undefined) && (remaining !== undefined)) {
filled = amount - remaining;
if (price !== undefined) {
cost = filled * price;
}
}
const average = this.safeFloat (order, 'avgPrice');
let symbol = undefined;
const marketId = this.safeString (order, 'currencyPair');
if (marketId !== undefined) {
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
} else {
const [ baseId, quoteId ] = marketId.split ('_');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
}
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
const clientOrderId = this.safeString (order, 'clientOrderId');
return {
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'average': average,
'filled': filled,
'remaining': remaining,
'status': status,
'trades': undefined,
'info': order,
'fee': undefined,
};
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
let method = 'privatePost' + this.capitalize (side);
@@ -457,25 +680,45 @@ module.exports = class coinmate extends Exchange {
};
if (type === 'market') {
if (side === 'buy') {
request['total'] = amount; // amount in fiat
request['total'] = this.amountToPrecision (symbol, amount); // amount in fiat
} else {
request['amount'] = amount; // amount in fiat
request['amount'] = this.amountToPrecision (symbol, amount); // amount in fiat
}
method += 'Instant';
} else {
request['amount'] = amount; // amount in crypto
request['price'] = price;
request['amount'] = this.amountToPrecision (symbol, amount); // amount in crypto
request['price'] = this.priceToPrecision (symbol, price);
method += this.capitalize (type);
}
const response = await this[method] (this.extend (request, params));
const id = this.safeString (response, 'data');
return {
'info': response,
'id': response['data'].toString (),
'id': id,
};
}
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'orderId': id,
};
let market = undefined;
if (symbol) {
market = this.market (symbol);
}
const response = await this.privatePostOrderById (this.extend (request, params));
const data = this.safeValue (response, 'data');
return this.parseOrder (data, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
return await this.privatePostCancelOrder ({ 'orderId': id });
// {"error":false,"errorMessage":null,"data":{"success":true,"remainingAmount":0.01}}
const request = { 'orderId': id };
const response = await this.privatePostCancelOrderWithInfo (this.extend (request, params));
return {
'info': response,
};
}
nonce () {
@@ -506,13 +749,27 @@ module.exports = class coinmate extends Exchange {
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const response = await this.fetch2 (path, api, method, params, headers, body);
if ('error' in response) {
if (response['error']) {
throw new ExchangeError (this.id + ' ' + this.json (response));
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response !== undefined) {
if ('error' in response) {
// {"error":true,"errorMessage":"Minimum Order Size 0.01 ETH","data":null}
if (response['error']) {
const message = this.safeString (response, 'errorMessage');
const feedback = this.id + ' ' + message;
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
throw new ExchangeError (this.id + ' ' + this.json (response));
}
}
}
return response;
if (code > 400) {
if (body) {
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException (this.exceptions['exact'], body, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback);
throw new ExchangeError (feedback); // unknown message
}
throw new ExchangeError (this.id + ' ' + body);
}
}
};
+16
View File
@@ -305,6 +305,8 @@ module.exports = class coinone extends Exchange {
'remaining': amount,
'status': 'open',
'fee': undefined,
'clientOrderId': undefined,
'trades': undefined,
};
this.orders[id] = order;
return order;
@@ -356,6 +358,17 @@ module.exports = class coinone extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "index": "0",
// "orderId": "68665943-1eb5-4e4b-9d76-845fc54f5489",
// "timestamp": "1449037367",
// "price": "444000.0",
// "qty": "0.3456",
// "type": "ask",
// "feeRate": "-0.0015"
// }
//
const info = this.safeValue (order, 'info');
const id = this.safeStringUpper (info, 'orderId');
const timestamp = this.safeTimestamp (info, 'timestamp');
@@ -398,6 +411,7 @@ module.exports = class coinone extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -411,6 +425,8 @@ module.exports = class coinone extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'average': undefined,
'trades': undefined,
};
}
+1
View File
@@ -833,6 +833,7 @@ module.exports = class coss extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
+2
View File
@@ -120,6 +120,7 @@ module.exports = class crex24 extends Exchange {
},
'commonCurrencies': {
'YOYO': 'YOYOW',
'BULL': 'BuySell',
'BCC': 'BCH',
},
// exchange-specific options
@@ -705,6 +706,7 @@ module.exports = class crex24 extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
+1384 -432
View File
File diff suppressed because it is too large. Load diff
+14 -1
View File
@@ -43,7 +43,7 @@ module.exports = class digifinex extends Exchange {
'doc': [
'https://docs.digifinex.vip',
],
'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328482-Fee-Structure-on-DigiFinex',
'fees': 'https://digifinex.zendesk.com/hc/en-us/articles/360000328422-Fee-Structure-on-DigiFinex',
'referral': 'https://www.digifinex.vip/en-ww/from/DhOzBg/3798****5114',
},
'api': {
@@ -100,6 +100,14 @@ module.exports = class digifinex extends Exchange {
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.002,
'taker': 0.002,
},
},
'exceptions': {
'exact': {
'10001': [ BadRequest, "Wrong request method, please check it's a GET ot POST request" ],
@@ -136,6 +144,9 @@ module.exports = class digifinex extends Exchange {
'defaultType': 'spot',
'types': [ 'spot', 'margin', 'otc' ],
},
'commonCurrencies': {
'BHT': 'Black House Test',
},
});
}
@@ -838,6 +849,7 @@ module.exports = class digifinex extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -852,6 +864,7 @@ module.exports = class digifinex extends Exchange {
'average': average,
'status': status,
'fee': undefined,
'trades': undefined,
};
}
+6 -1
View File
@@ -32,7 +32,7 @@ module.exports = class dsx extends Exchange {
'withdraw': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27990275-1413158a-645a-11e7-931c-94717f7510e3.jpg',
'logo': 'https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg',
'api': {
'public': 'https://dsx.uk/mapi', // market data
'private': 'https://dsx.uk/tapi', // trading
@@ -714,6 +714,9 @@ module.exports = class dsx extends Exchange {
'filled': filled,
'fee': undefined,
// 'trades': this.parseTrades (order['trades'], market),
'clientOrderId': undefined,
'average': undefined,
'trades': undefined,
};
}
@@ -825,6 +828,7 @@ module.exports = class dsx extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -839,6 +843,7 @@ module.exports = class dsx extends Exchange {
'status': status,
'fee': fee,
'trades': trades,
'average': undefined,
};
}
+203 -169
View File
@@ -103,159 +103,185 @@ module.exports = class exmo extends Exchange {
'error': '',
'data': {
'limits': [
{ 'pair': 'BTC/USD', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/EUR', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/RUB', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'BTC/UAH', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '1500000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BTC/PLN', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.001', 'max_p': '90000', 'min_a': '1', 'max_a': '2000000' },
{ 'pair': 'BTC/TRY', 'min_q': '0.001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' },
{ 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.0001', 'max_a': '10' },
{ 'pair': 'ROOBEE/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' },
{ 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' },
{ 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' },
{ 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
{ 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' },
{ 'pair': 'ATMCASH/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
{ 'pair': 'TRX/UAH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'ETH/TRY', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' },
{ 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' },
{ 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' },
{ 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' },
{ 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' },
{ 'pair': 'MKR/BTC', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MKR/DAI', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' },
{ 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'HB/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' },
{ 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GUSD/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GUSD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'LSK/USD', 'min_q': '1', 'max_q': '200000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'LSK/RUB', 'min_q': '1', 'max_q': '200000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' },
{ 'pair': 'NEO/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' },
{ 'pair': 'ADA/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'ZRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'GNT/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'TRX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'TRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'TRX/RUB', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'INK/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'INK/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'INK/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'MNX/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'OMG/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'XLM/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.1', 'max_a': '100' },
{ 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' },
{ 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' },
{ 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'EXM/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'EXM/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'BTC/USD', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/KZT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1000', 'max_p': '12000000', 'min_a': '1000', 'max_a': '100000000' },
{ 'pair': 'ETH/KZT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '4', 'max_p': '40000000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDT/KZT', 'min_q': '1', 'max_q': '500000', 'min_p': '200', 'max_p': '4000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ONT/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '15000000' },
{ 'pair': 'ONT/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONT/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '6000000' },
{ 'pair': 'ONT/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '250000' },
{ 'pair': 'ONG/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '15000000' },
{ 'pair': 'ONG/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONG/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '250000' },
{ 'pair': 'ONG/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '6000000' },
{ 'pair': 'CRON/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'CRON/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'CRON/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'CRON/EXM', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'BTC/RUB', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'BTC/EUR', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/UAH', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '15000000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BTC/PLN', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '20000000', 'min_a': '50', 'max_a': '2000000' },
{ 'pair': 'BTC/TRY', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' },
{ 'pair': 'BTC/USDT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/BTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETH/LTC', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' },
{ 'pair': 'ETH/USD', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/EUR', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '10000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'ETH/RUB', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'ETH/UAH', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' },
{ 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.001', 'max_p': '8000', 'min_a': '1', 'max_a': '2000000' },
{ 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'ETH/USD', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/RUB', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '100000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'XMR/BTC', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/USD', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/EUR', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'BTC/USDT', 'min_q': '0.001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/USDT', 'min_q': '0.01', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'USD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '10000', 'min_p': '1', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'KICK/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'KICK/ETH', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'KICK/USDT', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'ADA/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' },
{ 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' },
{ 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' },
{ 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' },
{ 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '1000000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'XMR/ETH', 'min_q': '0.03', 'max_q': '1000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' },
{ 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' },
{ 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' },
{ 'pair': 'ZAG/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'EXM/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0000001', 'max_a': '100' },
{ 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' },
{ 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' },
{ 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' },
{ 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '1000000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETH/EUR', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/UAH', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' },
{ 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '2000000' },
{ 'pair': 'ETH/TRY', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' },
{ 'pair': 'ETH/USDT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/LTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '100000', 'min_a': '0.05', 'max_a': '100000' },
{ 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' },
{ 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' },
{ 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GNT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GUSD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'GUSD/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'HP/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'HB/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'INK/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'INK/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'INK/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'KICK/RUB', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'KICK/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'KICK/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'KICK/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'LSK/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'LSK/RUB', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' },
{ 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/UAH', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '5', 'max_a': '18000000' },
{ 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'MKR/BTC', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MKR/DAI', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' },
{ 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' },
{ 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'MNX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' },
{ 'pair': 'NEO/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'OMG/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'PTI/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '600000', 'min_a': '10', 'max_a': '600000' },
{ 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
{ 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' },
{ 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'TRX/USD', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'TRX/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' },
{ 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' },
{ 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' },
{ 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' },
{ 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' },
{ 'pair': 'XLM/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '600000', 'min_a': '10', 'max_a': '16000000' },
{ 'pair': 'XMR/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '300000', 'min_a': '5', 'max_a': '16000000' },
{ 'pair': 'XMR/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/ETH', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' },
{ 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' },
{ 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'XTZ/USD', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' },
{ 'pair': 'XTZ/RUB', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'XTZ/BTC', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'XTZ/ETH', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '1000' },
{ 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.00001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZRX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'ZAG/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'USD/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'EXM/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0000001', 'max_a': '1' },
{ 'pair': 'ATMCASH/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
],
'fees': [
{
@@ -308,75 +334,79 @@ module.exports = class exmo extends Exchange {
{ 'prov': 'ZAG', 'dep': '0%', 'wd': '0%' },
{ 'prov': 'BTT', 'dep': '0%', 'wd': '100 BTT' },
{ 'prov': 'VLX', 'dep': '0%', 'wd': '1 VLX' },
{ 'prov': 'CRON', 'dep': '0%', 'wd': '5 CRON' },
{ 'prov': 'ONT', 'dep': '0%', 'wd': '1 ONT' },
{ 'prov': 'ONG', 'dep': '0%', 'wd': '5 ONG' },
],
},
{
'group': 'usd',
'title': 'USD',
'items': [
{ 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Perfect Money', 'dep': '-', 'wd': '0.5%' },
{ 'prov': 'AdvCash', 'dep': '3.45%', 'wd': '2.95%' },
{ 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.99%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 0.5 USD', 'wd': '-' },
{ 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' },
{ 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' },
],
},
{
'group': 'eur',
'title': 'EUR',
'items': [
{ 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' },
{ 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' },
{ 'prov': 'AdvCash', 'dep': '0.45%', 'wd': '-' },
{ 'prov': 'Wire Transfer', 'dep': '6.95 EUR', 'wd': '-' },
{ 'prov': 'SEPA', 'dep': '6.95 EUR', 'wd': '-' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' },
{ 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 0.5 EUR', 'wd': '-' },
{ 'prov': 'Visa', 'dep': '2.95%', 'wd': '-' },
{ 'prov': 'SEPA', 'dep': '10 EUR', 'wd': '50 EUR' },
],
},
{
'group': 'rub',
'title': 'RUB',
'items': [
{ 'prov': 'Yandex Money', 'dep': '2.95%', 'wd': '1.95%' },
{ 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '3.95%', 'wd': '0.95%' },
{ 'prov': 'Qiwi', 'dep': '3.95%', 'wd': '2.95%' },
{ 'prov': 'Visa/MasterCard', 'dep': '3.95%', 'wd': '4.95% + 60 RUB' },
{ 'prov': 'WebMoney', 'dep': '-', 'wd': '3.5%' },
{ 'prov': 'Payeer', 'dep': '2.49%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Qiwi', 'dep': '0.99%', 'wd': '2%' },
{ 'prov': 'Yandex Money', 'dep': '1.95%', 'wd': '-' },
{ 'prov': 'Enfins', 'dep': '0%', 'wd': '1.49%' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '2.49%' },
{ 'prov': 'Visa/MasterCard', 'dep': '1.99%', 'wd': '4.49% + 60 RUB' },
],
},
{
'group': 'pln',
'title': 'PLN',
'items': [
{ 'prov': 'CryptoCapital', 'dep': '-', 'wd': '0.45%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 1 PLN', 'wd': '-' },
],
},
{
'group': 'try',
'title': 'TRY',
'items': [
{ 'prov': 'QR ile yatırma', 'dep': '5.95%', 'wd': '-' },
{ 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa', 'dep': '3.05%', 'wd': '-' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 2 TRY', 'wd': '-' },
],
},
{
'group': 'uah',
'title': 'UAH',
'items': [
{ 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'AdvCash', 'dep': '-', 'wd': '2.95%' },
{ 'prov': 'Visa/MasterCard', 'dep': '2.6%', 'wd': '3.95% + 20 UAH' },
{ 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' },
{ 'prov': 'Enfins', 'dep': '0%', 'wd': '1.95%' },
{ 'prov': 'Visa/MasterCard', 'dep': '1% + 5 UAH', 'wd': '2.99%' },
],
},
{
'group': 'kzt',
'title': 'KZT',
'items': [
{ 'prov': 'Visa/MasterCard RoyalPay', 'dep': '-', 'wd': '2.99% + 450 KZT' },
{ 'prov': 'Visa/MasterCard', 'dep': '3.5%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '1.49%' },
],
},
],
@@ -390,6 +420,7 @@ module.exports = class exmo extends Exchange {
'40015': ExchangeError, // API function do not exist
'40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"}
'40017': AuthenticationError, // Wrong API Key
'40034': RateLimitExceeded, // {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"}
'50052': InsufficientFunds,
'50054': InsufficientFunds,
'50304': OrderNotFound, // "Order was not found '123456789'" (fetching order trades for an order that does not have trades yet)
@@ -869,6 +900,8 @@ module.exports = class exmo extends Exchange {
'filled': 0.0,
'fee': undefined,
'trades': undefined,
'clientOrderId': undefined,
'average': undefined,
};
this.orders[id] = order;
return order;
@@ -1136,6 +1169,7 @@ module.exports = class exmo extends Exchange {
};
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': lastTradeTimestamp,
+16
View File
@@ -272,6 +272,20 @@ module.exports = class exx extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "fees": 0,
// "total_amount": 1,
// "trade_amount": 0,
// "price": 30,
// "currency": “eth_hsr",
// "id": "13878",
// "trade_money": 0,
// "type": "buy",
// "trade_date": 1509728897772,
// "status": 0
// }
//
const symbol = market['symbol'];
const timestamp = parseInt (order['trade_date']);
const price = this.safeFloat (order, 'price');
@@ -296,6 +310,7 @@ module.exports = class exx extends Exchange {
}
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -311,6 +326,7 @@ module.exports = class exx extends Exchange {
'trades': undefined,
'fee': fee,
'info': order,
'average': undefined,
};
}
+17
View File
@@ -514,6 +514,22 @@ module.exports = class fcoin extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "id": "string",
// "symbol": "string",
// "type": "limit",
// "side": "buy",
// "price": "string",
// "amount": "string",
// "state": "submitted",
// "executed_value": "string",
// "fill_fees": "string",
// "filled_amount": "string",
// "created_at": 0,
// "source": "web"
// }
//
const id = this.safeString (order, 'id');
const side = this.safeString (order, 'side');
const status = this.parseOrderStatus (this.safeString (order, 'state'));
@@ -562,6 +578,7 @@ module.exports = class fcoin extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
+4 -1
View File
@@ -16,7 +16,7 @@ module.exports = class flowbtc extends Exchange {
'version': 'v1',
'rateLimit': 1000,
'has': {
'CORS': true,
'CORS': false,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/28162465-cd815d4c-67cf-11e7-8e57-438bea0523a2.jpg',
@@ -108,6 +108,7 @@ module.exports = class flowbtc extends Exchange {
},
},
'info': market,
'active': undefined,
};
}
return result;
@@ -197,6 +198,8 @@ module.exports = class flowbtc extends Exchange {
'price': price,
'amount': amount,
'cost': cost,
'takerOrMaker': undefined,
'fee': undefined,
};
}
+95 -17
View File
@@ -16,10 +16,14 @@ module.exports = class ftx extends Exchange {
'countries': [ 'HK' ],
'rateLimit': 100,
'certified': true,
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg',
'www': 'https://ftx.com',
'api': 'https://ftx.com',
'api': {
'public': 'https://ftx.com',
'private': 'https://ftx.com',
},
'doc': 'https://github.com/ftexchange/ftx',
'fees': 'https://ftexchange.zendesk.com/hc/en-us/articles/360024479432-Fees',
'referral': 'https://ftx.com/#a=1623029',
@@ -159,6 +163,19 @@ module.exports = class ftx extends Exchange {
},
},
'precisionMode': TICK_SIZE,
'options': {
// support for canceling conditional orders
// https://github.com/ccxt/ccxt/issues/6669
'cancelOrder': {
'method': 'privateDeleteOrdersOrderId', // privateDeleteConditionalOrdersOrderId
},
'fetchOpenOrders': {
'method': 'privateGetOrders', // privateGetConditionalOrders
},
'fetchOrders': {
'method': 'privateGetOrdersHistory', // privateGetConditionalOrdersHistory
},
},
});
}
@@ -334,14 +351,16 @@ module.exports = class ftx extends Exchange {
} else {
const base = this.safeCurrencyCode (this.safeString (ticker, 'baseCurrency'));
const quote = this.safeCurrencyCode (this.safeString (ticker, 'quoteCurrency'));
symbol = base + '/' + quote;
if ((base !== undefined) && (quote !== undefined)) {
symbol = base + '/' + quote;
}
}
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
const last = this.safeFloat (ticker, 'last');
const timestamp = this.milliseconds ();
const timestamp = this.safeTimestamp (ticker, 'time', this.milliseconds ());
return {
'symbol': symbol,
'timestamp': timestamp,
@@ -349,9 +368,9 @@ module.exports = class ftx extends Exchange {
'high': this.safeFloat (ticker, 'high'),
'low': this.safeFloat (ticker, 'low'),
'bid': this.safeFloat (ticker, 'bid'),
'bidVolume': undefined,
'bidVolume': this.safeFloat (ticker, 'bidSize'),
'ask': this.safeFloat (ticker, 'ask'),
'askVolume': undefined,
'askVolume': this.safeFloat (ticker, 'askSize'),
'vwap': undefined,
'open': undefined,
'close': last,
@@ -502,11 +521,16 @@ module.exports = class ftx extends Exchange {
'market_name': market['id'],
'resolution': this.timeframes[timeframe],
};
if (limit !== undefined) {
// max 1501 candles, including the current candle when since is not specified
limit = (limit === undefined) ? 1501 : limit;
if (since === undefined) {
request['end_time'] = this.seconds ();
request['limit'] = limit;
}
if (since !== undefined) {
request['start_time'] = request['end_time'] - limit * this.parseTimeframe (timeframe);
} else {
request['start_time'] = parseInt (since / 1000);
request['limit'] = limit;
request['end_time'] = this.sum (request['start_time'], limit * this.parseTimeframe (timeframe));
}
const response = await this.publicGetMarketsMarketNameCandles (this.extend (request, params));
//
@@ -756,7 +780,7 @@ module.exports = class ftx extends Exchange {
parseOrder (order, market = undefined) {
//
// fetchOrder, fetchOrders, fetchOpenOrders, createOrder ("limit", "market")
// limit orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder
//
// {
// "createdAt": "2019-03-05T09:56:55.728933+00:00",
@@ -776,6 +800,27 @@ module.exports = class ftx extends Exchange {
// "clientId": null,
// }
//
// market orders - fetchOrder, fetchOrders, fetchOpenOrders, createOrder
//
// {
// "avgFillPrice": 2666.0,
// "clientId": None,
// "createdAt": "2020-02-12T00: 53: 49.009726+00: 00",
// "filledSize": 0.0007,
// "future": None,
// "id": 3109208514,
// "ioc": True,
// "market": "BNBBULL/USD",
// "postOnly": False,
// "price": None,
// "reduceOnly": False,
// "remainingSize": 0.0,
// "side": "buy",
// "size": 0.0007,
// "status": "closed",
// "type": "market"
// }
//
// createOrder (conditional, "stop", "trailingStop", or "takeProfit")
//
// {
@@ -812,15 +857,18 @@ module.exports = class ftx extends Exchange {
const side = this.safeString (order, 'side');
const type = this.safeString (order, 'type');
const amount = this.safeFloat (order, 'size');
const average = this.safeFloat (order, 'avgFillPrice');
const price = this.safeFloat2 (order, 'price', 'triggerPrice', average);
let cost = undefined;
if (filled !== undefined && amount !== undefined) {
cost = filled * amount;
if (filled !== undefined && price !== undefined) {
cost = filled * price;
}
const price = this.safeFloat2 (order, 'price', 'triggerPrice');
const lastTradeTimestamp = this.parse8601 (this.safeString (order, 'triggeredAt'));
const clientOrderId = this.safeString (order, 'clientId');
return {
'info': order,
'id': id,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -830,7 +878,7 @@ module.exports = class ftx extends Exchange {
'price': price,
'amount': amount,
'cost': cost,
'average': undefined,
'average': average,
'filled': filled,
'remaining': remaining,
'status': status,
@@ -933,7 +981,17 @@ module.exports = class ftx extends Exchange {
const request = {
'order_id': parseInt (id),
};
const response = await this.privateDeleteOrdersOrderId (this.extend (request, params));
// support for canceling conditional orders
// https://github.com/ccxt/ccxt/issues/6669
const options = this.safeValue (this.options, 'cancelOrder', {});
const defaultMethod = this.safeString (options, 'method', 'privateDeleteOrdersOrderId');
let method = this.safeString (params, 'method', defaultMethod);
const type = this.safeValue (params, 'type');
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
method = 'privateDeleteConditionalOrdersOrderId';
}
const query = this.omit (params, [ 'method', 'type' ]);
const response = await this[method] (this.extend (request, query));
//
// {
// "success": true,
@@ -1008,7 +1066,17 @@ module.exports = class ftx extends Exchange {
market = this.market (symbol);
request['market'] = market['id'];
}
const response = await this.privateGetOrders (this.extend (request, params));
// support for canceling conditional orders
// https://github.com/ccxt/ccxt/issues/6669
const options = this.safeValue (this.options, 'fetchOpenOrders', {});
const defaultMethod = this.safeString (options, 'method', 'privateGetOrders');
let method = this.safeString (params, 'method', defaultMethod);
const type = this.safeValue (params, 'type');
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
method = 'privateGetConditionalOrders';
}
const query = this.omit (params, [ 'method', 'type' ]);
const response = await this[method] (this.extend (request, query));
//
// {
// "success": true,
@@ -1052,7 +1120,17 @@ module.exports = class ftx extends Exchange {
if (since !== undefined) {
request['start_time'] = parseInt (since / 1000);
}
const response = await this.privateGetOrdersHistory (this.extend (request, params));
// support for canceling conditional orders
// https://github.com/ccxt/ccxt/issues/6669
const options = this.safeValue (this.options, 'fetchOrders', {});
const defaultMethod = this.safeString (options, 'method', 'privateGetOrdersHistory');
let method = this.safeString (params, 'method', defaultMethod);
const type = this.safeValue (params, 'type');
if ((type === 'stop') || (type === 'trailingStop') || (type === 'takeProfit')) {
method = 'privateGetConditionalOrdersHistory';
}
const query = this.omit (params, [ 'method', 'type' ]);
const response = await this[method] (this.extend (request, query));
//
// {
// "success": true,
@@ -1309,7 +1387,7 @@ module.exports = class ftx extends Exchange {
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let request = '/api/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
let url = this.urls['api'] + request;
let url = this.urls['api'][api] + request;
if (method !== 'POST') {
if (Object.keys (query).length) {
const suffix = '?' + this.urlencode (query);
+42 -2
View File
@@ -15,6 +15,7 @@ module.exports = class gateio extends Exchange {
'countries': [ 'CN' ],
'version': '2',
'rateLimit': 1000,
'pro': true,
'has': {
'CORS': false,
'createMarketOrder': false,
@@ -68,6 +69,7 @@ module.exports = class gateio extends Exchange {
'pairs',
'marketinfo',
'marketlist',
'coininfo',
'tickers',
'ticker/{id}',
'orderBook/{id}',
@@ -89,6 +91,7 @@ module.exports = class gateio extends Exchange {
'getOrder',
'openOrders',
'tradeHistory',
'feelist',
'withdraw',
],
},
@@ -152,6 +155,10 @@ module.exports = class gateio extends Exchange {
},
},
},
'commonCurrencies': {
'BTCBEAR': 'BEAR',
'BTCBULL': 'BULL',
},
});
}
@@ -373,6 +380,18 @@ module.exports = class gateio extends Exchange {
}
parseTrade (trade, market = undefined) {
// {
// "tradeID": 3175762,
// "date": "2017-08-25 07:24:28",
// "type": "sell",
// "rate": 29011,
// "amount": 0.0019,
// "total": 55.1209,
// "fee": "0",
// "fee_coin": "btc",
// "gt_fee":"0",
// "point_fee":"0.1213",
// },
let timestamp = this.safeTimestamp2 (trade, 'timestamp', 'time_unix');
timestamp = this.safeTimestamp (trade, 'time', timestamp);
const id = this.safeString2 (trade, 'tradeID', 'id');
@@ -381,6 +400,7 @@ module.exports = class gateio extends Exchange {
const price = this.safeFloat2 (trade, 'rate', 'price');
const amount = this.safeFloat (trade, 'amount');
const type = this.safeString (trade, 'type');
const takerOrMaker = this.safeString (trade, 'role');
let cost = undefined;
if (price !== undefined) {
if (amount !== undefined) {
@@ -391,6 +411,25 @@ module.exports = class gateio extends Exchange {
if (market !== undefined) {
symbol = market['symbol'];
}
let fee = undefined;
let feeCurrency = this.safeCurrencyCode (this.safeString (trade, 'fee_coin'));
let feeCost = this.safeFloat (trade, 'point_fee');
if ((feeCost === undefined) || (feeCost === 0)) {
feeCost = this.safeFloat (trade, 'gt_fee');
if ((feeCost === undefined) || (feeCost === 0)) {
feeCost = this.safeFloat (trade, 'fee');
} else {
feeCurrency = this.safeCurrencyCode ('GT');
}
} else {
feeCurrency = this.safeCurrencyCode ('POINT');
}
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': feeCurrency,
};
}
return {
'id': id,
'info': trade,
@@ -400,11 +439,11 @@ module.exports = class gateio extends Exchange {
'order': orderId,
'type': undefined,
'side': type,
'takerOrMaker': undefined,
'takerOrMaker': takerOrMaker,
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
'fee': fee,
};
}
@@ -496,6 +535,7 @@ module.exports = class gateio extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': lastTradeTimestamp,
+9 -1
View File
@@ -45,7 +45,11 @@ module.exports = class gemini extends Exchange {
'https://docs.gemini.com/rest-api',
'https://docs.sandbox.gemini.com',
],
'test': 'https://api.sandbox.gemini.com',
'test': {
'public': 'https://api.sandbox.gemini.com',
'private': 'https://api.sandbox.gemini.com',
'web': 'https://docs.sandbox.gemini.com',
},
'fees': [
'https://gemini.com/api-fee-schedule',
'https://gemini.com/trading-fees',
@@ -290,6 +294,7 @@ module.exports = class gemini extends Exchange {
'max': undefined,
},
},
'active': undefined,
});
}
return result;
@@ -451,8 +456,10 @@ module.exports = class gemini extends Exchange {
}
const id = this.safeString (order, 'order_id');
const side = this.safeStringLower (order, 'side');
const clientOrderId = this.safeString (order, 'client_order_id');
return {
'id': id,
'clientOrderId': clientOrderId,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -468,6 +475,7 @@ module.exports = class gemini extends Exchange {
'filled': filled,
'remaining': remaining,
'fee': fee,
'trades': undefined,
};
}
+827 -335
View File
File diff suppressed because it is too large. Load diff
+3 -1489
View File
File diff suppressed because it is too large. Load diff
+1230
View File
File diff suppressed because it is too large. Load diff
+270 -103
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { AuthenticationError, ExchangeError, ExchangeNotAvailable, InvalidOrder, OrderNotFound, InsufficientFunds, ArgumentsRequired, BadSymbol, BadRequest, RequestTimeout } = require ('./base/errors');
const { AuthenticationError, ExchangeError, PermissionDenied, ExchangeNotAvailable, InvalidOrder, OrderNotFound, InsufficientFunds, ArgumentsRequired, BadSymbol, BadRequest, RequestTimeout } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -18,11 +18,12 @@ module.exports = class huobipro extends Exchange {
'version': 'v1',
'accounts': undefined,
'accountsById': undefined,
'hostname': 'api.huobi.pro',
'hostname': 'api.huobi.pro', // api.testnet.huobi.pro
'pro': true,
'has': {
'CORS': false,
'fetchTickers': true,
'fetchDepositAddress': false,
'fetchDepositAddress': true,
'fetchOHLCV': true,
'fetchOrder': true,
'fetchOrders': true,
@@ -48,12 +49,18 @@ module.exports = class huobipro extends Exchange {
'1y': '1year',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766569-15aa7b9a-5edd-11e7-9e7f-44791f4ee49c.jpg',
'test': {
'market': 'https://api.testnet.huobi.pro',
'public': 'https://api.testnet.huobi.pro',
'private': 'https://api.testnet.huobi.pro',
},
'logo': 'https://user-images.githubusercontent.com/1294454/76137448-22748a80-604e-11ea-8069-6e389271911d.jpg',
'api': {
'market': 'https://{hostname}',
'public': 'https://{hostname}',
'private': 'https://{hostname}',
'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles',
'v2Public': 'https://{hostname}',
'v2Private': 'https://{hostname}',
},
'www': 'https://www.huobi.pro',
'referral': 'https://www.huobi.co/en-us/topic/invited/?invite_code=rwrd3',
@@ -61,9 +68,19 @@ module.exports = class huobipro extends Exchange {
'fees': 'https://www.huobi.pro/about/fee/',
},
'api': {
'zendesk': {
'v2Public': {
'get': [
'360000400491-Trade-Limits',
'reference/currencies',
],
},
'v2Private': {
'get': [
'account/withdraw/quota',
'account/deposit/address',
'reference/transact-fee-rate',
],
'post': [
'sub-user/management',
],
},
'market': {
@@ -87,10 +104,6 @@ module.exports = class huobipro extends Exchange {
],
},
'private': {
// todo add v2 endpoints
// 'GET /v2/account/withdraw/quota
// 'GET /v2/reference/currencies
// 'GET /v2/account/deposit/address
'get': [
'account/accounts', // 查询当前用户的所有账户(即account-id)
'account/accounts/{id}/balance', // 查询指定账户的余额
@@ -115,8 +128,6 @@ module.exports = class huobipro extends Exchange {
'stable-coin/exchange_rate',
'stable-coin/quote',
],
// todo add v2 endpoints
// POST /v2/sub-user/management
'post': [
'futures/transfer',
'order/batch-orders',
@@ -152,6 +163,7 @@ module.exports = class huobipro extends Exchange {
'exceptions': {
'exact': {
// err-code
'api-not-support-temp-addr': PermissionDenied, // {"status":"error","err-code":"api-not-support-temp-addr","err-msg":"API withdrawal does not support temporary addresses","data":null}
'timeout': RequestTimeout, // {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"}
'gateway-internal-error': ExchangeNotAvailable, // {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null}
'account-frozen-balance-insufficient-error': InsufficientFunds, // {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null}
@@ -319,6 +331,21 @@ module.exports = class huobipro extends Exchange {
}
parseTicker (ticker, market = undefined) {
//
// {
// "amount": 26228.672978342216,
// "open": 9078.95,
// "close": 9146.86,
// "high": 9155.41,
// "id": 209988544334,
// "count": 265846,
// "low": 8988.0,
// "version": 209988544334,
// "ask": [ 9146.87, 0.156134 ],
// "vol": 2.3822168242201668E8,
// "bid": [ 9146.86, 0.080758 ],
// }
//
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
@@ -390,13 +417,35 @@ module.exports = class huobipro extends Exchange {
'type': 'step0',
};
const response = await this.marketGetDepth (this.extend (request, params));
//
// {
// "status": "ok",
// "ch": "market.btcusdt.depth.step0",
// "ts": 1583474832790,
// "tick": {
// "bids": [
// [ 9100.290000000000000000, 0.200000000000000000 ],
// [ 9099.820000000000000000, 0.200000000000000000 ],
// [ 9099.610000000000000000, 0.205000000000000000 ],
// ],
// "asks": [
// [ 9100.640000000000000000, 0.005904000000000000 ],
// [ 9101.010000000000000000, 0.287311000000000000 ],
// [ 9101.030000000000000000, 0.012121000000000000 ],
// ],
// "ts":1583474832008,
// "version":104999698780
// }
// }
//
if ('tick' in response) {
if (!response['tick']) {
throw new ExchangeError (this.id + ' fetchOrderBook() returned empty response: ' + this.json (response));
}
const orderbook = this.safeValue (response, 'tick');
const result = this.parseOrderBook (orderbook, orderbook['ts']);
result['nonce'] = orderbook['version'];
const tick = this.safeValue (response, 'tick');
const timestamp = this.safeInteger (tick, 'ts', this.safeInteger (response, 'ts'));
const result = this.parseOrderBook (tick, timestamp);
result['nonce'] = this.safeInteger (tick, 'version');
return result;
}
throw new ExchangeError (this.id + ' fetchOrderBook() returned unrecognized response: ' + this.json (response));
@@ -409,7 +458,31 @@ module.exports = class huobipro extends Exchange {
'symbol': market['id'],
};
const response = await this.marketGetDetailMerged (this.extend (request, params));
return this.parseTicker (response['tick'], market);
//
// {
// "status": "ok",
// "ch": "market.btcusdt.detail.merged",
// "ts": 1583494336669,
// "tick": {
// "amount": 26228.672978342216,
// "open": 9078.95,
// "close": 9146.86,
// "high": 9155.41,
// "id": 209988544334,
// "count": 265846,
// "low": 8988.0,
// "version": 209988544334,
// "ask": [ 9146.87, 0.156134 ],
// "vol": 2.3822168242201668E8,
// "bid": [ 9146.86, 0.080758 ],
// }
// }
//
const ticker = this.parseTicker (response['tick'], market);
const timestamp = this.safeValue (response, 'ts');
ticker['timestamp'] = timestamp;
ticker['datetime'] = this.iso8601 (timestamp);
return ticker;
}
async fetchTickers (symbols = undefined, params = {}) {
@@ -430,10 +503,24 @@ module.exports = class huobipro extends Exchange {
result[symbol] = ticker;
}
}
return result;
return this.filterByArray (result, 'symbol', symbols);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// "amount": 0.010411000000000000,
// "trade-id": 102090736910,
// "ts": 1583497692182,
// "id": 10500517034273194594947,
// "price": 9096.050000000000000000,
// "direction": "sell"
// }
//
// fetchMyTrades (private)
//
let symbol = undefined;
if (market === undefined) {
const marketId = this.safeString (trade, 'symbol');
@@ -481,7 +568,8 @@ module.exports = class huobipro extends Exchange {
'currency': feeCurrency,
};
}
const id = this.safeString (trade, 'id');
const tradeId = this.safeString2 (trade, 'trade-id', 'tradeId');
const id = this.safeString (trade, 'id', tradeId);
return {
'id': id,
'info': trade,
@@ -528,6 +616,30 @@ module.exports = class huobipro extends Exchange {
request['size'] = limit;
}
const response = await this.marketGetHistoryTrade (this.extend (request, params));
//
// {
// "status": "ok",
// "ch": "market.btcusdt.trade.detail",
// "ts": 1583497692365,
// "data": [
// {
// "id": 105005170342,
// "ts": 1583497692182,
// "data": [
// {
// "amount": 0.010411000000000000,
// "trade-id": 102090736910,
// "ts": 1583497692182,
// "id": 10500517034273194594947,
// "price": 9096.050000000000000000,
// "direction": "sell"
// }
// ]
// },
// // ...
// ]
// }
//
const data = this.safeValue (response, 'data');
let result = [];
for (let i = 0; i < data.length; i++) {
@@ -729,6 +841,9 @@ module.exports = class huobipro extends Exchange {
}
async fetchOpenOrdersV1 (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrdersV1 requires a symbol argument');
}
return await this.fetchOrdersByStates ('pre-submitted,submitted,partial-filled', symbol, since, limit, params);
}
@@ -892,6 +1007,7 @@ module.exports = class huobipro extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -906,6 +1022,7 @@ module.exports = class huobipro extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
@@ -960,6 +1077,8 @@ module.exports = class huobipro extends Exchange {
'cost': undefined,
'trades': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
};
}
@@ -999,94 +1118,50 @@ module.exports = class huobipro extends Exchange {
};
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
await this.loadMarkets ();
parseDepositAddress (depositAddress, currency = undefined) {
//
// {
// currency: "eth",
// address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b",
// addressTag: "",
// chain: "eth"
// }
//
const address = this.safeString (depositAddress, 'address');
const tag = this.safeString (depositAddress, 'addressTag');
const currencyId = this.safeString (depositAddress, 'currency');
const code = this.safeCurrencyCode (currencyId);
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': depositAddress,
};
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'address': address, // only supports existing addresses in your withdraw address list
'amount': amount,
'currency': currency['id'].toLowerCase (),
'currency': currency['id'],
};
if (tag !== undefined) {
request['addr-tag'] = tag; // only for XRP?
}
const response = await this.privatePostDwWithdrawApiCreate (this.extend (request, params));
const id = this.safeString (response, 'data');
return {
'info': response,
'id': id,
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = '/';
if (api === 'market') {
url += api;
} else if ((api === 'public') || (api === 'private')) {
url += this.version;
}
url += '/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
if (api === 'private') {
this.checkRequiredCredentials ();
const timestamp = this.ymdhms (this.milliseconds (), 'T');
let request = {
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'AccessKeyId': this.apiKey,
'Timestamp': timestamp,
};
if (method !== 'POST') {
request = this.extend (request, query);
}
request = this.keysort (request);
let auth = this.urlencode (request);
// unfortunately, PHP demands double quotes for the escaped newline symbol
// eslint-disable-next-line quotes
const payload = [ method, this.hostname, url, auth ].join ("\n");
const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64');
auth += '&' + this.urlencode ({ 'Signature': signature });
url += '?' + auth;
if (method === 'POST') {
body = this.json (query);
headers = {
'Content-Type': 'application/json',
};
} else {
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
}
} else {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
}
url = this.implodeParams (this.urls['api'][api], {
'hostname': this.hostname,
}) + url;
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
}
if ('status' in response) {
//
// {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null}
//
const status = this.safeString (response, 'status');
if (status === 'error') {
const code = this.safeString (response, 'err-code');
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
const message = this.safeString (response, 'err-msg');
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
throw new ExchangeError (feedback);
}
}
const response = await this.v2PrivateGetAccountDepositAddress (this.extend (request, params));
//
// {
// code: 200,
// data: [
// {
// currency: "eth",
// address: "0xf7292eb9ba7bc50358e27f0e025a4d225a64127b",
// addressTag: "",
// chain: "eth"
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseDepositAddress (this.safeValue (data, 0, {}), currency);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
@@ -1230,4 +1305,96 @@ module.exports = class huobipro extends Exchange {
};
return this.safeString (statuses, status, status);
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
await this.loadMarkets ();
this.checkAddress (address);
const currency = this.currency (code);
const request = {
'address': address, // only supports existing addresses in your withdraw address list
'amount': amount,
'currency': currency['id'].toLowerCase (),
};
if (tag !== undefined) {
request['addr-tag'] = tag; // only for XRP?
}
const response = await this.privatePostDwWithdrawApiCreate (this.extend (request, params));
const id = this.safeString (response, 'data');
return {
'info': response,
'id': id,
};
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = '/';
if (api === 'market') {
url += api;
} else if ((api === 'public') || (api === 'private')) {
url += this.version;
} else if ((api === 'v2Public') || (api === 'v2Private')) {
url += 'v2';
}
url += '/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
if (api === 'private' || api === 'v2Private') {
this.checkRequiredCredentials ();
const timestamp = this.ymdhms (this.milliseconds (), 'T');
let request = {
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'AccessKeyId': this.apiKey,
'Timestamp': timestamp,
};
if (method !== 'POST') {
request = this.extend (request, query);
}
request = this.keysort (request);
let auth = this.urlencode (request);
// unfortunately, PHP demands double quotes for the escaped newline symbol
// eslint-disable-next-line quotes
const payload = [ method, this.hostname, url, auth ].join ("\n");
const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256', 'base64');
auth += '&' + this.urlencode ({ 'Signature': signature });
url += '?' + auth;
if (method === 'POST') {
body = this.json (query);
headers = {
'Content-Type': 'application/json',
};
} else {
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
}
} else {
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
}
}
url = this.implodeParams (this.urls['api'][api], {
'hostname': this.hostname,
}) + url;
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
}
if ('status' in response) {
//
// {"status":"error","err-code":"order-limitorder-amount-min-error","err-msg":"limit order amount error, min: `0.001`","data":null}
//
const status = this.safeString (response, 'status');
if (status === 'error') {
const code = this.safeString (response, 'err-code');
const feedback = this.id + ' ' + body;
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
const message = this.safeString (response, 'err-msg');
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
throw new ExchangeError (feedback);
}
}
}
};
+4 -3
View File
@@ -13,12 +13,13 @@ module.exports = class huobiru extends huobipro {
'name': 'Huobi Russia',
'countries': [ 'RU' ],
'hostname': 'www.huobi.com.ru',
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg',
'api': {
'market': 'https://www.huobi.com.ru/api',
'public': 'https://www.huobi.com.ru/api',
'private': 'https://www.huobi.com.ru/api',
'market': 'https://{hostname}/api',
'public': 'https://{hostname}/api',
'private': 'https://{hostname}/api',
'zendesk': 'https://huobiglobal.zendesk.com/hc/en-us/articles',
},
'www': 'https://www.huobi.com.ru/ru-ru',
+5
View File
@@ -116,6 +116,7 @@ module.exports = class ice3x extends Exchange {
},
},
'info': currency,
'fee': undefined,
};
}
return result;
@@ -148,6 +149,8 @@ module.exports = class ice3x extends Exchange {
'quoteId': quoteId,
'active': undefined,
'info': market,
'precision': this.precision,
'limits': this.limits,
});
}
return result;
@@ -330,6 +333,7 @@ module.exports = class ice3x extends Exchange {
}
return {
'id': this.safeString (order, 'order_id'),
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -345,6 +349,7 @@ module.exports = class ice3x extends Exchange {
'trades': undefined,
'fee': fee,
'info': order,
'average': undefined,
};
}
+18 -7
View File
@@ -97,8 +97,11 @@ module.exports = class idex extends Exchange {
'secret': false,
},
'commonCurrencies': {
'ONE': 'Menlo One',
'FT': 'Fabric Token',
'MT': 'Monarch',
'ONE': 'Menlo One',
'PLA': 'PlayChip',
'WAX': 'WAXP',
},
});
}
@@ -382,13 +385,13 @@ module.exports = class idex extends Exchange {
if (side === 'buy') {
tokenBuy = market['baseId'];
tokenSell = market['quoteId'];
amountBuy = this.toWei (amount, 'ether', market['precision']['amount']);
amountSell = this.toWei (quoteAmount, 'ether', 18);
amountBuy = this.toWei (amount, market['precision']['amount']);
amountSell = this.toWei (quoteAmount, 18);
} else {
tokenBuy = market['quoteId'];
tokenSell = market['baseId'];
amountBuy = this.toWei (quoteAmount, 'ether', 18);
amountSell = this.toWei (amount, 'ether', market['precision']['amount']);
amountBuy = this.toWei (quoteAmount, 18);
amountSell = this.toWei (amount, market['precision']['amount']);
}
const nonce = await this.getNonce ();
const orderToHash = {
@@ -733,8 +736,11 @@ module.exports = class idex extends Exchange {
amount = this.safeFloat (order, 'amount');
}
const filled = this.safeFloat (order, 'filled');
const cost = this.safeFloat (order, 'total');
const price = this.safeFloat (order, 'price');
let cost = this.safeFloat (order, 'total');
if ((cost !== undefined) && (filled !== undefined) && !cost) {
cost = filled * price;
}
if ('market' in order) {
const marketId = order['market'];
symbol = this.markets_by_id[marketId]['symbol'];
@@ -754,6 +760,7 @@ module.exports = class idex extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -765,6 +772,10 @@ module.exports = class idex extends Exchange {
'remaining': remaining,
'cost': cost,
'status': status,
'lastTradeTimestamp': undefined,
'average': undefined,
'trades': undefined,
'fee': undefined,
};
}
@@ -988,7 +999,7 @@ module.exports = class idex extends Exchange {
const currency = this.currency (code);
const tokenAddress = currency['id'];
const nonce = await this.getNonce ();
amount = this.toWei (amount, 'ether', currency['precision']);
amount = this.toWei (amount, currency['precision']);
const requestToHash = {
'contractAddress': await this.getContractAddress (),
'token': tokenAddress,
+23
View File
@@ -71,6 +71,9 @@ module.exports = class independentreserve extends Exchange {
'tierBased': false,
},
},
'commonCurrencies': {
'PLA': 'PlayChip',
},
});
}
@@ -94,6 +97,9 @@ module.exports = class independentreserve extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'info': id,
'active': undefined,
'precision': this.precision,
'limits': this.limits,
});
}
}
@@ -171,6 +177,21 @@ module.exports = class independentreserve extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "OrderGuid": "c7347e4c-b865-4c94-8f74-d934d4b0b177",
// "CreatedTimestampUtc": "2014-09-23T12:39:34.3817763Z",
// "Type": "MarketBid",
// "VolumeOrdered": 5.0,
// "VolumeFilled": 5.0,
// "Price": null,
// "AvgPrice": 100.0,
// "ReservedAmount": 0.0,
// "Status": "Filled",
// "PrimaryCurrencyCode": "Xbt",
// "SecondaryCurrencyCode": "Usd"
// }
//
let symbol = undefined;
const baseId = this.safeString (order, 'PrimaryCurrencyCode');
const quoteId = this.safeString (order, 'PrimaryCurrencyCode');
@@ -227,6 +248,7 @@ module.exports = class independentreserve extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -241,6 +263,7 @@ module.exports = class independentreserve extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
+12
View File
@@ -280,6 +280,16 @@ module.exports = class indodax extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "order_id": "12345",
// "submit_time": "1392228122",
// "price": "8000000",
// "type": "sell",
// "order_ltc": "100000000",
// "remain_ltc": "100000000"
// }
//
let side = undefined;
if ('type' in order) {
side = order['type'];
@@ -331,6 +341,7 @@ module.exports = class indodax extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -345,6 +356,7 @@ module.exports = class indodax extends Exchange {
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
}
+45 -11
View File
@@ -61,12 +61,12 @@ module.exports = class itbit extends Exchange {
},
},
'markets': {
'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD' },
'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD' },
'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' },
'ETH/USD': { 'id': 'ETHUSD', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD' },
'ETH/EUR': { 'id': 'ETHEUR', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR' },
'ETH/SGD': { 'id': 'ETHSGD', 'symbol': 'ETH/SGD', 'base': 'ETH', 'quote': 'SGD' },
'BTC/USD': { 'id': 'XBTUSD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'XBT', 'quoteId': 'USD' },
'BTC/SGD': { 'id': 'XBTSGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'XBT', 'quoteId': 'SGD' },
'BTC/EUR': { 'id': 'XBTEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'XBT', 'quoteId': 'EUR' },
'ETH/USD': { 'id': 'ETHUSD', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'ETH', 'quoteId': 'USD' },
'ETH/EUR': { 'id': 'ETHEUR', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'ETH', 'quoteId': 'EUR' },
'ETH/SGD': { 'id': 'ETHSGD', 'symbol': 'ETH/SGD', 'base': 'ETH', 'quote': 'SGD', 'baseId': 'ETH', 'quoteId': 'SGD' },
},
'fees': {
'trading': {
@@ -213,6 +213,7 @@ module.exports = class itbit extends Exchange {
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
};
if (feeCost !== undefined) {
if (rebatesApplied !== undefined) {
@@ -472,19 +473,51 @@ module.exports = class itbit extends Exchange {
}
parseOrder (order, market = undefined) {
const side = order['side'];
const type = order['type'];
//
// {
// "id": "13d6af57-8b0b-41e5-af30-becf0bcc574d",
// "walletId": "7e037345-1288-4c39-12fe-d0f99a475a98",
// "side": "buy",
// "instrument": "XBTUSD",
// "type": "limit",
// "currency": "XBT",
// "amount": "2.50000000",
// "displayAmount": "2.50000000",
// "price": "650.00000000",
// "volumeWeightedAveragePrice": "0.00000000",
// "amountFilled": "0.00000000",
// "createdTime": "2014-02-11T17:05:15Z",
// "status": "submitted",
// "funds": null,
// "metadata": {},
// "clientOrderIdentifier": null,
// "postOnly": "False"
// }
//
const side = this.safeString (order, 'side');
const type = this.safeString (order, 'type');
const symbol = this.markets_by_id[order['instrument']]['symbol'];
const timestamp = this.parse8601 (order['createdTime']);
const amount = this.safeFloat (order, 'amount');
const filled = this.safeFloat (order, 'amountFilled');
const remaining = amount - filled;
let remaining = undefined;
let cost = undefined;
const fee = undefined;
const price = this.safeFloat (order, 'price');
const average = this.safeFloat (order, 'volumeWeightedAveragePrice');
const cost = filled * average;
if (filled !== undefined) {
if (amount !== undefined) {
remaining = amount - filled;
}
if (average !== undefined) {
cost = filled * average;
}
}
const clientOrderId = this.safeString (order, 'clientOrderIdentifier');
const id = this.safeString (order, 'id');
return {
'id': order['id'],
'id': id,
'clientOrderId': clientOrderId,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -501,6 +534,7 @@ module.exports = class itbit extends Exchange {
'remaining': remaining,
'fee': fee,
// 'trades': this.parseTrades (order['trades'], market),
'trades': undefined,
};
}
+18
View File
@@ -399,6 +399,20 @@ module.exports = class kkex extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "status": 2,
// "source": "NORMAL",
// "amount": "10.852019",
// "create_date": 1523938461036,
// "avg_price": "0.00096104",
// "order_id": "100",
// "price": "0.00096105",
// "type": "buy",
// "symbol": "READBTC",
// "deal_amount": "10.852019"
// }
//
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
@@ -427,6 +441,7 @@ module.exports = class kkex extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -442,6 +457,7 @@ module.exports = class kkex extends Exchange {
'remaining': remaining,
'fee': undefined,
'info': order,
'trades': undefined,
};
}
@@ -490,6 +506,8 @@ module.exports = class kkex extends Exchange {
'remaining': undefined,
'trades': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
};
}
+17 -6
View File
@@ -17,6 +17,7 @@ module.exports = class kraken extends Exchange {
'version': '0',
'rateLimit': 3000,
'certified': true,
'pro': true,
'has': {
'createDepositAddress': true,
'fetchDepositAddress': true,
@@ -49,7 +50,7 @@ module.exports = class kraken extends Exchange {
'2w': 21600,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766599-22709304-5ede-11e7-9de1-9f33732e1509.jpg',
'logo': 'https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg',
'api': {
'public': 'https://api.kraken.com',
'private': 'https://api.kraken.com',
@@ -205,6 +206,7 @@ module.exports = class kraken extends Exchange {
'delistedMarketsById': {},
// cannot withdraw/deposit these
'inactiveCurrencies': [ 'CAD', 'USD', 'JPY', 'GBP' ],
'fetchMinOrderAmounts': true,
},
'exceptions': {
'EQuery:Invalid asset pair': BadSymbol, // {"error":["EQuery:Invalid asset pair"]}
@@ -260,7 +262,11 @@ module.exports = class kraken extends Exchange {
async fetchMarkets (params = {}) {
const response = await this.publicGetAssetPairs (params);
const limits = await this.fetchMinOrderAmounts ();
const fetchMinOrderAmounts = this.safeValue (this.options, 'fetchMinOrderAmounts', false);
let limits = {};
if (fetchMinOrderAmounts) {
limits = await this.fetchMinOrderAmounts ();
}
const keys = Object.keys (response['result']);
let result = [];
for (let i = 0; i < keys.length; i++) {
@@ -723,14 +729,14 @@ module.exports = class kraken extends Exchange {
symbol = market['symbol'];
}
if (Array.isArray (trade)) {
timestamp = parseInt (trade[2] * 1000);
timestamp = this.safeTimestamp (trade, 2);
side = (trade[3] === 's') ? 'sell' : 'buy';
type = (trade[4] === 'l') ? 'limit' : 'market';
price = parseFloat (trade[0]);
amount = parseFloat (trade[1]);
price = this.safeFloat (trade, 0);
amount = this.safeFloat (trade, 1);
const tradeLength = trade.length;
if (tradeLength > 6) {
id = trade[6]; // artificially added as per #1794
id = this.safeString (trade, 6); // artificially added as per #1794
}
} else if ('ordertxid' in trade) {
order = trade['ordertxid'];
@@ -854,8 +860,10 @@ module.exports = class kraken extends Exchange {
id = (length > 1) ? id : id[0];
}
}
const clientOrderId = this.safeString (params, 'userref');
return {
'id': id,
'clientOrderId': clientOrderId,
'info': response,
'timestamp': undefined,
'datetime': undefined,
@@ -977,8 +985,10 @@ module.exports = class kraken extends Exchange {
}
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const id = this.safeString (order, 'id');
const clientOrderId = this.safeString (order, 'userref');
return {
'id': id,
'clientOrderId': clientOrderId,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -995,6 +1005,7 @@ module.exports = class kraken extends Exchange {
'remaining': remaining,
'fee': fee,
// 'trades': this.parseTrades (order['trades'], market),
'trades': undefined,
};
}
+40 -3
View File
@@ -16,8 +16,10 @@ module.exports = class kucoin extends Exchange {
'rateLimit': 334,
'version': 'v2',
'certified': false,
'pro': true,
'comment': 'Platform 2.0',
'has': {
'CORS': false,
'fetchTime': true,
'fetchMarkets': true,
'fetchCurrencies': true,
@@ -70,6 +72,10 @@ module.exports = class kucoin extends Exchange {
'symbols',
'market/allTickers',
'market/orderbook/level{level}',
'market/orderbook/level2',
'market/orderbook/level2_20',
'market/orderbook/level2_100',
'market/orderbook/level3',
'market/histories',
'market/candles',
'market/stats',
@@ -101,6 +107,7 @@ module.exports = class kucoin extends Exchange {
'post': [
'accounts',
'accounts/inner-transfer',
'accounts/sub-transfer',
'deposit-addresses',
'withdrawals',
'orders',
@@ -182,6 +189,8 @@ module.exports = class kucoin extends Exchange {
'commonCurrencies': {
'HOT': 'HOTNOW',
'EDGE': 'DADI', // https://github.com/ccxt/ccxt/issues/5756
'WAX': 'WAXP',
'TRY': 'Trias',
},
'options': {
'version': 'v1',
@@ -190,6 +199,23 @@ module.exports = class kucoin extends Exchange {
'fetchBalance': {
'type': 'trade', // or 'main'
},
// endpoint versions
'versions': {
'public': {
'GET': {
'market/orderbook/level{level}': 'v1',
'market/orderbook/level2': 'v2',
'market/orderbook/level2_20': 'v1',
'market/orderbook/level2_100': 'v1',
},
},
'private': {
'POST': {
'accounts/inner-transfer': 'v2',
'accounts/sub-transfer': 'v2',
},
},
},
},
});
}
@@ -239,8 +265,7 @@ module.exports = class kucoin extends Exchange {
for (let i = 0; i < data.length; i++) {
const market = data[i];
const id = this.safeString (market, 'symbol');
const baseId = this.safeString (market, 'baseCurrency');
const quoteId = this.safeString (market, 'quoteCurrency');
const [ baseId, quoteId ] = id.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
@@ -308,6 +333,9 @@ module.exports = class kucoin extends Exchange {
'code': code,
'precision': precision,
'info': entry,
'active': undefined,
'fee': undefined,
'limits': this.limits,
};
}
return result;
@@ -873,8 +901,10 @@ module.exports = class kucoin extends Exchange {
}
}
}
const clientOrderId = this.safeString (order, 'clientOid');
return {
'id': orderId,
'clientOrderId': clientOrderId,
'symbol': symbol,
'type': type,
'side': side,
@@ -888,6 +918,9 @@ module.exports = class kucoin extends Exchange {
'fee': fee,
'status': status,
'info': order,
'lastTradeTimestamp': undefined,
'average': undefined,
'trades': undefined,
};
}
@@ -1612,7 +1645,11 @@ module.exports = class kucoin extends Exchange {
// the v2 URL is https://openapi-v2.kucoin.com/api/v1/endpoint
// † ↑
//
const version = this.safeString (params, 'version', this.options['version']);
const versions = this.safeValue (this.options, 'versions', {});
const apiVersions = this.safeValue (versions, api);
const methodVersions = this.safeValue (apiVersions, method, {});
const defaultVersion = this.safeString (methodVersions, path, this.options['version']);
const version = this.safeString (params, 'version', defaultVersion);
params = this.omit (params, 'version');
let endpoint = '/api/' + version + '/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
+2
View File
@@ -103,6 +103,8 @@ module.exports = class kuna extends acx {
'max': undefined,
},
},
'active': undefined,
'info': undefined,
});
break;
}
+3
View File
@@ -78,6 +78,9 @@ module.exports = class lakebtc extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'info': market,
'active': undefined,
'precision': this.precision,
'limits': this.limits,
});
}
return result;
+4
View File
@@ -92,6 +92,7 @@ module.exports = class latoken extends Exchange {
},
},
'commonCurrencies': {
'MT': 'Monarch',
'TSL': 'Treasure SL',
},
'options': {
@@ -647,8 +648,10 @@ module.exports = class latoken extends Exchange {
if ((timeFilled !== undefined) && (timeFilled > 0)) {
lastTradeTimestamp = timeFilled;
}
const clientOrderId = this.safeString (order, 'cliOrdId');
return {
'id': id,
'clientOrderId': clientOrderId,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -664,6 +667,7 @@ module.exports = class latoken extends Exchange {
'average': undefined,
'remaining': remaining,
'fee': undefined,
'trades': undefined,
};
}
+15
View File
@@ -409,6 +409,19 @@ module.exports = class lbank extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "symbol":"eth_btc",
// "amount":10.000000,
// "create_time":1484289832081,
// "price":5000.000000,
// "avg_price":5277.301200,
// "type":"sell",
// "order_id":"ab704110-af0d-48fd-a083-c218f19a4a55",
// "deal_amount":10.000000,
// "status":2
// }
//
let symbol = undefined;
const responseMarket = this.safeValue (this.marketsById, order['symbol']);
if (responseMarket !== undefined) {
@@ -439,6 +452,7 @@ module.exports = class lbank extends Exchange {
}
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -454,6 +468,7 @@ module.exports = class lbank extends Exchange {
'trades': undefined,
'fee': undefined,
'info': this.safeValue (order, 'info', order),
'average': undefined,
};
}
+1
View File
@@ -658,6 +658,7 @@ module.exports = class liquid extends Exchange {
const side = this.safeString (order, 'side');
return {
'id': orderId,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
+6
View File
@@ -104,6 +104,7 @@ module.exports = class livecoin extends Exchange {
'RUR': 'RUB',
'SCT': 'SpaceCoin',
'TPI': 'ThaneCoin',
'WAX': 'WAXP',
'wETT': 'WETT',
'XBT': 'Bricktox',
},
@@ -254,6 +255,9 @@ module.exports = class livecoin extends Exchange {
'max': Math.pow (10, precision),
},
},
'id': undefined,
'code': undefined,
'name': undefined,
};
const currencies = [
{ 'id': 'USD', 'code': 'USD', 'name': 'US Dollar' },
@@ -602,6 +606,7 @@ module.exports = class livecoin extends Exchange {
return {
'info': order,
'id': order['id'],
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
@@ -620,6 +625,7 @@ module.exports = class livecoin extends Exchange {
'currency': feeCurrency,
'rate': feeRate,
},
'average': undefined,
};
}
+22
View File
@@ -103,6 +103,9 @@ module.exports = class luno extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'info': market,
'active': undefined,
'precision': this.precision,
'limits': this.limits,
});
}
return result;
@@ -145,6 +148,23 @@ module.exports = class luno extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "base": "string",
// "completed_timestamp": "string",
// "counter": "string",
// "creation_timestamp": "string",
// "expiration_timestamp": "string",
// "fee_base": "string",
// "fee_counter": "string",
// "limit_price": "string",
// "limit_volume": "string",
// "order_id": "string",
// "pair": "string",
// "state": "PENDING",
// "type": "BID"
// }
//
const timestamp = this.safeInteger (order, 'creation_timestamp');
const status = (order['state'] === 'PENDING') ? 'open' : 'closed';
const side = (order['type'] === 'ASK') ? 'sell' : 'buy';
@@ -183,6 +203,7 @@ module.exports = class luno extends Exchange {
const id = this.safeString (order, 'order_id');
return {
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': undefined,
@@ -198,6 +219,7 @@ module.exports = class luno extends Exchange {
'trades': undefined,
'fee': fee,
'info': order,
'average': undefined,
};
}
+21
View File
@@ -255,6 +255,8 @@ module.exports = class lykke extends Exchange {
'max': undefined,
},
},
'baseId': undefined,
'quoteId': undefined,
});
}
return result;
@@ -316,6 +318,23 @@ module.exports = class lykke extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "Id": "string",
// "Status": "Unknown",
// "AssetPairId": "string",
// "Volume": 0,
// "Price": 0,
// "RemainingVolume": 0,
// "LastMatchTime": "2020-03-26T20:58:50.710Z",
// "CreatedAt": "2020-03-26T20:58:50.710Z",
// "Type": "Unknown",
// "LowerLimitPrice": 0,
// "LowerPrice": 0,
// "UpperLimitPrice": 0,
// "UpperPrice": 0
// }
//
const status = this.parseOrderStatus (this.safeString (order, 'Status'));
let symbol = undefined;
if (market === undefined) {
@@ -341,6 +360,7 @@ module.exports = class lykke extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
@@ -355,6 +375,7 @@ module.exports = class lykke extends Exchange {
'remaining': remaining,
'status': status,
'fee': undefined,
'trades': undefined,
};
}
+1
View File
@@ -352,6 +352,7 @@ module.exports = class mercado extends Exchange {
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
+21
View File
@@ -94,6 +94,9 @@ module.exports = class oceanex extends Exchange {
'taker': 0.1 / 100,
},
},
'commonCurrencies': {
'PLA': 'Plair',
},
'exceptions': {
'codes': {
'-1': BadRequest,
@@ -553,6 +556,23 @@ module.exports = class oceanex extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "created_at": "2019-01-18T00:38:18Z",
// "trades_count": 0,
// "remaining_volume": "0.2",
// "price": "1001.0",
// "created_on": "1547771898",
// "side": "buy",
// "volume": "0.2",
// "state": "wait",
// "ord_type": "limit",
// "avg_price": "0.0",
// "executed_volume": "0.0",
// "id": 473797,
// "market": "veteth"
// }
//
const status = this.parseOrderStatus (this.safeValue (order, 'state'));
const marketId = this.safeValue2 (order, 'market', 'market_id');
let symbol = undefined;
@@ -576,6 +596,7 @@ module.exports = class oceanex extends Exchange {
return {
'info': order,
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
+35
View File
@@ -0,0 +1,35 @@
'use strict';
// ---------------------------------------------------------------------------
const okex = require ('./okex.js');
// ---------------------------------------------------------------------------
module.exports = class okcoin extends okex {
describe () {
return this.deepExtend (super.describe (), {
'id': 'okcoin',
'name': 'OKCoin',
'countries': [ 'CN', 'US' ],
'hostname': 'okcoin.com',
'pro': true,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766791-89ffb502-5ee5-11e7-8a5b-c5950b68ac65.jpg',
'www': 'https://www.okcoin.com',
'doc': 'https://www.okcoin.com/docs/en/',
'fees': 'https://www.okcoin.com/coin-fees',
'referral': 'https://www.okcoin.com/account/register?flag=activity&channelId=600001513',
},
'fees': {
'trading': {
'taker': 0.002,
'maker': 0.001,
},
},
'options': {
'fetchMarkets': [ 'spot' ],
},
});
}
};
-38
View File
@@ -1,38 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const okcoinusd = require ('./okcoinusd.js');
// ---------------------------------------------------------------------------
module.exports = class okcoincny extends okcoinusd {
describe () {
return this.deepExtend (super.describe (), {
'id': 'okcoincny',
'name': 'OKCoin CNY',
'countries': [ 'CN' ],
'has': {
'CORS': false,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766792-8be9157a-5ee5-11e7-926c-6d69b8d3378d.jpg',
'api': {
'web': 'https://www.okcoin.cn',
'public': 'https://www.okcoin.cn/api',
'private': 'https://www.okcoin.cn/api',
},
'www': 'https://www.okcoin.cn',
'doc': 'https://www.okcoin.cn/rest_getStarted.html',
'referral': 'https://www.okcoin.cn',
},
'markets': {
'BTC/CNY': { 'id': 'btc_cny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false },
'LTC/CNY': { 'id': 'ltc_cny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false },
'ETH/CNY': { 'id': 'eth_cny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false },
'ETC/CNY': { 'id': 'etc_cny', 'symbol': 'ETC/CNY', 'base': 'ETC', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false },
'BCH/CNY': { 'id': 'bcc_cny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY', 'type': 'spot', 'spot': true, 'future': false },
},
});
}
};
-1116
View File
File diff suppressed because it is too large. Load diff
Loaded 100 of 503 files, more files were not shown because too many files have changed in this diff. Show more