Adding Missing Exchanges and Markets
This commit is contained in:
1 parent
f724d4f260
commit
d26d54466b
1926 files changed
+241463
-163648
No files matched your search
-8
@@ -1,8 +0,0 @@
|
||||
const { MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME,
|
||||
ORDER_MARGIN_ENABLED, createRecord, getRecord, getExpandedRecord, createRecordFromObject
|
||||
} = require("./orderLifeCicle/index")
|
||||
|
||||
module.exports = {
|
||||
MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME,
|
||||
ORDER_MARGIN_ENABLED, createRecord, getRecord, getExpandedRecord, createRecordFromObject
|
||||
}
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
const MESSAGE_ENTITY = {
|
||||
SimulationExecutor: 'EX',
|
||||
TradingCokpit: 'CO',
|
||||
SimulationEngine: 'EN',
|
||||
TradingAssistant: 'AS'
|
||||
}
|
||||
const MESSAGE_TYPE = {
|
||||
HeartBeat: 'HBT',
|
||||
OrderAuthorizationRequest: 'REQ',
|
||||
OrderAuthorizationResponse: 'RES',
|
||||
Order: 'ORD',
|
||||
OrderUpdate: 'UPD'
|
||||
}
|
||||
const ORDER_CREATOR = { SimulationEngine: 'S', HumanTrader: 'H' }
|
||||
const ORDER_TYPE = { Market: 'M', Limit: 'L', Stop: 'S', }
|
||||
const ORDER_OWNER = { Node: 'N', Team: 'T', User: 'U', }
|
||||
const ORDER_DIRECTION = { Sell: 'Sell', Buy: 'Buy' }
|
||||
const ORDER_STATUS = {
|
||||
Signaled: 'SIG', // The order has been created and is ready to be authorized
|
||||
ManualAuthorized: 'MAU', // Authorized by the user manually
|
||||
ManualNotAuthorized: 'MNA', // Rejected by the user manually
|
||||
AutoAuthorized: 'AAU', // Authorize all as been selected by the user
|
||||
AutoNotAuthorized: 'ANA', // Reject all as been selected by the user
|
||||
Executing: 'EXE', // The order is efectively being executed on the exchange
|
||||
Cancelled: 'CAN', // The order has been canceled
|
||||
Filled: 'FIL', // The order bas been completed on the exchange
|
||||
PartiallyFilled: 'PRT', // The order is partially filled and is still on the exchange
|
||||
Discarded: 'DIS', // Order discarded since there is already an order in place
|
||||
Placed: 'PLA', // Order has been just been placed on the exchange
|
||||
Rejected: 'REJ', // Order has been rejected by the exchange due to some error
|
||||
Undeliverable: 'UND' // The order was not able to be delivered to the exchange
|
||||
}
|
||||
const ORDER_EXIT_OUTCOME = { StopLoss: 'SL', TakeProfit: 'TP' }
|
||||
const ORDER_MARGIN_ENABLED = { True: 1, False: 0 }
|
||||
|
||||
function createRecord(messageId, from, to, messageType, messageDateTime, orderId, creator, orderDateTime,
|
||||
owner, exchange, market, marginEnabled, type, rate, stop, takeProfit, direction, size, status, sizeFilled, exitOutcome) {
|
||||
|
||||
return [
|
||||
messageId,
|
||||
from,
|
||||
to,
|
||||
messageType,
|
||||
messageDateTime,
|
||||
[
|
||||
orderId,
|
||||
creator,
|
||||
orderDateTime,
|
||||
owner,
|
||||
exchange,
|
||||
market,
|
||||
marginEnabled,
|
||||
type,
|
||||
rate,
|
||||
stop,
|
||||
takeProfit,
|
||||
direction,
|
||||
size,
|
||||
status,
|
||||
sizeFilled,
|
||||
exitOutcome
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
function createRecordFromObject(object) {
|
||||
|
||||
return createRecord(object.id, object.from, object.to, object.messageType, object.dateTime, object.order.id,
|
||||
object.order.creator, object.order.dateTime, object.order.owner, object.order.exchange, object.order.market,
|
||||
object.order.marginEnabled, object.order.type, object.order.rate, object.order.stop, object.order.takeProfit,
|
||||
object.order.direction, object.order.size, object.order.status, object.order.sizeFilled, object.order.exitOutcome)
|
||||
}
|
||||
|
||||
function getRecord(fileContent) {
|
||||
return {
|
||||
id: fileContent[0],
|
||||
from: fileContent[1],
|
||||
to: fileContent[2],
|
||||
messageType: fileContent[3],
|
||||
dateTime: fileContent[4],
|
||||
order: {
|
||||
id: fileContent[5][0],
|
||||
creator: fileContent[5][1],
|
||||
dateTime: fileContent[5][2],
|
||||
owner: fileContent[5][3],
|
||||
exchange: fileContent[5][4],
|
||||
market: fileContent[5][5],
|
||||
marginEnabled: fileContent[5][6],
|
||||
type: fileContent[5][7],
|
||||
rate: fileContent[5][8],
|
||||
stop: fileContent[5][9],
|
||||
takeProfit: fileContent[5][10],
|
||||
direction: fileContent[5][11],
|
||||
size: fileContent[5][12],
|
||||
status: fileContent[5][13],
|
||||
sizeFilled: fileContent[5][14],
|
||||
exitOutcome: fileContent[5][15]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getExpandedRecord(fileContent) {
|
||||
return {
|
||||
id: fileContent[0],
|
||||
from: getKeyByValue(MESSAGE_ENTITY, fileContent[1]),
|
||||
to: getKeyByValue(MESSAGE_ENTITY, fileContent[2]),
|
||||
messageType: getKeyByValue(MESSAGE_TYPE, fileContent[3]),
|
||||
dateTime: fileContent[4],
|
||||
order: {
|
||||
id: fileContent[5][0],
|
||||
creator: getKeyByValue(ORDER_CREATOR, fileContent[5][1]),
|
||||
dateTime: fileContent[5][2],
|
||||
owner: getKeyByValue(ORDER_OWNER, fileContent[5][3]),
|
||||
exchange: fileContent[5][4],
|
||||
market: fileContent[5][5],
|
||||
marginEnabled: getKeyByValue(ORDER_MARGIN_ENABLED, fileContent[5][6]),
|
||||
type: getKeyByValue(ORDER_TYPE, fileContent[5][7]),
|
||||
rate: fileContent[5][8],
|
||||
stop: fileContent[5][9],
|
||||
takeProfit: fileContent[5][10],
|
||||
direction: getKeyByValue(ORDER_DIRECTION, fileContent[5][11]),
|
||||
size: fileContent[5][12],
|
||||
status: getKeyByValue(ORDER_STATUS, fileContent[5][13]),
|
||||
sizeFilled: fileContent[5][14],
|
||||
exitOutcome: getKeyByValue(ORDER_EXIT_OUTCOME, fileContent[5][15])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyByValue(object, value) {
|
||||
let retrievedValue = Object.keys(object).find(key => object[key] === value)
|
||||
if (retrievedValue !== undefined) {
|
||||
retrievedValue = retrievedValue.replace(/([A-Z])/g, ' $1').trim()
|
||||
}
|
||||
else {
|
||||
throw new Error("The property is not valid: " + value + ". Available values: " + JSON.stringify(Object.values(object)))
|
||||
}
|
||||
return retrievedValue
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER, ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, ORDER_MARGIN_ENABLED,
|
||||
createRecord, getRecord, getExpandedRecord, createRecordFromObject
|
||||
}
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
|
||||
function newOrderRecord() {
|
||||
|
||||
let thisObject = {
|
||||
MESSAGE_ENTITY: MESSAGE_ENTITY,
|
||||
MESSAGE_TYPE: MESSAGE_TYPE,
|
||||
ORDER_CREATOR: ORDER_CREATOR,
|
||||
ORDER_TYPE: ORDER_TYPE,
|
||||
ORDER_OWNER: ORDER_OWNER,
|
||||
ORDER_DIRECTION: ORDER_DIRECTION,
|
||||
ORDER_STATUS: ORDER_STATUS,
|
||||
ORDER_EXIT_OUTCOME: ORDER_EXIT_OUTCOME,
|
||||
createRecord: createRecord,
|
||||
getRecord: getRecord,
|
||||
getExpandedRecord: getExpandedRecord,
|
||||
createRecordFromObject: createRecordFromObject
|
||||
};
|
||||
|
||||
return thisObject
|
||||
|
||||
const MESSAGE_ENTITY = {
|
||||
SimulationExecutor: 'EX',
|
||||
TradingCokpit: 'CO',
|
||||
SimulationEngine: 'EN',
|
||||
TradingAssistant: 'AS'
|
||||
}
|
||||
const MESSAGE_TYPE = {
|
||||
HeartBeat: 'HBT',
|
||||
OrderAuthorizationRequest: 'REQ',
|
||||
OrderAuthorizationResponse: 'RES',
|
||||
Order: 'ORD',
|
||||
OrderUpdate: 'UPD'
|
||||
}
|
||||
const ORDER_CREATOR = { SimulationEngine: 'S', HumanTrader: 'H' }
|
||||
const ORDER_TYPE = { Market: 'M', Limit: 'L', Stop: 'S', }
|
||||
const ORDER_OWNER = { Node: 'N', Team: 'T', User: 'U', }
|
||||
const ORDER_DIRECTION = { Sell: 'Sell', Buy: 'Buy' }
|
||||
const ORDER_STATUS = {
|
||||
Signaled: 'SIG',
|
||||
ManualAuthorized: 'MAU',
|
||||
ManualNotAuthorized: 'MNA',
|
||||
AutoAuthorized: 'AAU',
|
||||
AutoNotAuthorized: 'ANA',
|
||||
Executing: 'EXE',
|
||||
Cancelled: 'CAN',
|
||||
Filled: 'FIL',
|
||||
PartiallyFilled: 'PRT',
|
||||
Discarded: 'DIS',
|
||||
Placed: 'PLA'
|
||||
}
|
||||
const ORDER_EXIT_OUTCOME = { StopLoss: 'SL', TakeProfit: 'TP' }
|
||||
const ORDER_MARGIN_ENABLED = { True: 1, False: 0 }
|
||||
|
||||
function createRecord(messageId, from, to, messageType, messageDateTime, orderId, creator, orderDateTime,
|
||||
owner, exchange, market, marginEnabled, type, rate, stop, takeProfit, direction, size, status, sizeFilled, exitOutcome) {
|
||||
|
||||
return [
|
||||
messageId,
|
||||
from,
|
||||
to,
|
||||
messageType,
|
||||
messageDateTime,
|
||||
[
|
||||
orderId,
|
||||
creator,
|
||||
orderDateTime,
|
||||
owner,
|
||||
exchange,
|
||||
market,
|
||||
marginEnabled,
|
||||
type,
|
||||
rate,
|
||||
stop,
|
||||
takeProfit,
|
||||
direction,
|
||||
size,
|
||||
status,
|
||||
sizeFilled,
|
||||
exitOutcome
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
function createRecordFromObject(object) {
|
||||
|
||||
return createRecord(object.id, object.from, object.to, object.messageType, object.dateTime, object.order.id,
|
||||
object.order.creator, object.order.dateTime, object.order.owner, object.order.exchange, object.order.market,
|
||||
object.order.marginEnabled, object.order.type, object.order.rate, object.order.stop, object.order.takeProfit,
|
||||
object.order.direction, object.order.size, object.order.status, object.order.sizeFilled, object.order.exitOutcome)
|
||||
}
|
||||
|
||||
function getRecord(fileContent) {
|
||||
let parsedFileContent = JSON.parse(fileContent)
|
||||
|
||||
return {
|
||||
id: parsedFileContent[0],
|
||||
from: parsedFileContent[1],
|
||||
to: parsedFileContent[2],
|
||||
messageType: parsedFileContent[3],
|
||||
dateTime: parsedFileContent[4],
|
||||
order: {
|
||||
id: parsedFileContent[5][0],
|
||||
creator: parsedFileContent[5][1],
|
||||
dateTime: parsedFileContent[5][2],
|
||||
owner: parsedFileContent[5][3],
|
||||
exchange: parsedFileContent[5][4],
|
||||
market: parsedFileContent[5][5],
|
||||
marginEnabled: parsedFileContent[5][6],
|
||||
type: parsedFileContent[5][7],
|
||||
rate: parsedFileContent[5][8],
|
||||
stop: parsedFileContent[5][9],
|
||||
takeProfit: parsedFileContent[5][10],
|
||||
direction: parsedFileContent[5][11],
|
||||
size: parsedFileContent[5][12],
|
||||
status: parsedFileContent[5][13],
|
||||
sizeFilled: parsedFileContent[5][14],
|
||||
exitOutcome: parsedFileContent[5][15]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getExpandedRecord(fileContent) {
|
||||
let parsedFileContent = JSON.parse(fileContent)
|
||||
|
||||
return {
|
||||
id: parsedFileContent[0],
|
||||
from: getKeyByValue(MESSAGE_ENTITY, parsedFileContent[1]),
|
||||
to: getKeyByValue(MESSAGE_ENTITY, parsedFileContent[2]),
|
||||
messageType: getKeyByValue(MESSAGE_TYPE, parsedFileContent[3]),
|
||||
dateTime: parsedFileContent[4],
|
||||
order: {
|
||||
id: parsedFileContent[5][0],
|
||||
creator: getKeyByValue(ORDER_CREATOR, parsedFileContent[5][1]),
|
||||
dateTime: parsedFileContent[5][2],
|
||||
owner: getKeyByValue(ORDER_OWNER, parsedFileContent[5][3]),
|
||||
exchange: parsedFileContent[5][4],
|
||||
market: parsedFileContent[5][5],
|
||||
marginEnabled: getKeyByValue(ORDER_MARGIN_ENABLED, parsedFileContent[5][6]),
|
||||
type: getKeyByValue(ORDER_TYPE, parsedFileContent[5][7]),
|
||||
rate: parsedFileContent[5][8],
|
||||
stop: parsedFileContent[5][9],
|
||||
takeProfit: parsedFileContent[5][10],
|
||||
direction: getKeyByValue(ORDER_DIRECTION, parsedFileContent[5][11]),
|
||||
size: parsedFileContent[5][12],
|
||||
status: getKeyByValue(ORDER_STATUS, parsedFileContent[5][13]),
|
||||
sizeFilled: parsedFileContent[5][14],
|
||||
exitOutcome: getKeyByValue(ORDER_EXIT_OUTCOME, parsedFileContent[5][15])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyByValue(object, value) {
|
||||
let retrievedValue = Object.keys(object).find(key => object[key] === value)
|
||||
if (retrievedValue !== undefined) {
|
||||
retrievedValue = retrievedValue.replace(/([A-Z])/g, ' $1').trim()
|
||||
}
|
||||
else {
|
||||
throw new Error("The property is not valid: " + value + ". Available values: " + JSON.stringify(Object.values(object)))
|
||||
}
|
||||
return retrievedValue
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"@superalgos/mqservice@0.0.12",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "@superalgos/mqservice@0.0.12",
|
||||
"_id": "@superalgos/mqservice@0.0.12",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-Dw6VvjuqB1a7w+7Ghqi4pxvEJDUKWMbS1W/xNmSlTJhI3sW/YexmBbF8nmvdyIfxtajO/+Q1ZoqeBWsiPcjmcQ==",
|
||||
"_location": "/@superalgos/mqservice",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "@superalgos/mqservice@0.0.12",
|
||||
"name": "@superalgos/mqservice",
|
||||
"escapedName": "@superalgos%2fmqservice",
|
||||
"scope": "@superalgos",
|
||||
"rawSpec": "0.0.12",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "0.0.12"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/@superalgos/mqservice/-/mqservice-0.0.12.tgz",
|
||||
"_spec": "0.0.12",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Superalgos, ltd."
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Superalgos/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^6.0.2"
|
||||
},
|
||||
"description": "Provide a common framework for objects between different modules.",
|
||||
"homepage": "https://github.com/Superalgos/MQService#readme",
|
||||
"license": "SEE LICENSE IN LICENSE.MD",
|
||||
"main": "index.js",
|
||||
"name": "@superalgos/mqservice",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Superalgos/MQService.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "0.0.12"
|
||||
}
|
||||
Generated
Vendored
-144
@@ -1,144 +0,0 @@
|
||||
var assert = require("chai").assert
|
||||
const {
|
||||
MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER,
|
||||
ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, ORDER_MARGIN_ENABLED,
|
||||
createRecord, getRecord, getExpandedRecord, createRecordFromObject
|
||||
} = require("../../orderLifeCicle/index")
|
||||
|
||||
describe("SimulatorExecutor ", function () {
|
||||
describe("Order Life Cicle Test", function () {
|
||||
it("testCreateRecord", function () {
|
||||
let expected = JSON.stringify([
|
||||
90,
|
||||
"EN",
|
||||
"EX",
|
||||
"ORD",
|
||||
1553850096262,
|
||||
[
|
||||
1,
|
||||
"S",
|
||||
155385234234,
|
||||
"U",
|
||||
"Poloniex",
|
||||
"BTC/USDT",
|
||||
0,
|
||||
"L",
|
||||
6286.707,
|
||||
6381.007,
|
||||
0,
|
||||
"Sell",
|
||||
0,
|
||||
"SIG",
|
||||
0,
|
||||
"SL"
|
||||
]
|
||||
])
|
||||
|
||||
let record = createRecord(90, MESSAGE_ENTITY.SimulationEngine, MESSAGE_ENTITY.SimulationExecutor,
|
||||
MESSAGE_TYPE.Order, 1553850096262, 1, ORDER_CREATOR.SimulationEngine, 155385234234, ORDER_OWNER.User,
|
||||
"Poloniex", "BTC/USDT", 0, ORDER_TYPE.Limit, 6286.707, 6381.007, ORDER_MARGIN_ENABLED.False, ORDER_DIRECTION.Sell, 0,
|
||||
ORDER_STATUS.Signaled, 0, ORDER_EXIT_OUTCOME.StopLoss)
|
||||
|
||||
let result = JSON.stringify(record)
|
||||
assert.equal(result, expected)
|
||||
})
|
||||
it("testGetRecord", function () {
|
||||
let fileContent = '[90, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]'
|
||||
|
||||
let expected = JSON.stringify({
|
||||
"id": 90,
|
||||
"from": "EN",
|
||||
"to": "EX",
|
||||
"messageType": "ORD",
|
||||
"dateTime": 1553850096262,
|
||||
"order": {
|
||||
"id": 1,
|
||||
"creator": "S",
|
||||
"dateTime": 155385234234,
|
||||
"owner": "U",
|
||||
"exchange": "Poloniex",
|
||||
"market": "BTC/USDT",
|
||||
"marginEnabled": 0,
|
||||
"type": "L",
|
||||
"rate": 6286.707,
|
||||
"stop": 6381.007,
|
||||
"takeProfit": 0,
|
||||
"direction": "Sell",
|
||||
"size": 0,
|
||||
"status": "SIG",
|
||||
"sizeFilled": 0,
|
||||
"exitOutcome": "SL"
|
||||
}
|
||||
})
|
||||
|
||||
let record = getRecord(fileContent)
|
||||
let result = JSON.stringify(record)
|
||||
assert.equal(result, expected)
|
||||
})
|
||||
it("testGetExpandedRecord", function () {
|
||||
let fileContent = '[90, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]'
|
||||
|
||||
let expected = JSON.stringify({
|
||||
"id": 90,
|
||||
"from": "Simulation Engine",
|
||||
"to": "Simulation Executor",
|
||||
"messageType": "Order",
|
||||
"dateTime": 1553850096262,
|
||||
"order": {
|
||||
"id": 1,
|
||||
"creator": "Simulation Engine",
|
||||
"dateTime": 155385234234,
|
||||
"owner": "User",
|
||||
"exchange": "Poloniex",
|
||||
"market": "BTC/USDT",
|
||||
"marginEnabled": "False",
|
||||
"type": "Limit",
|
||||
"rate": 6286.707,
|
||||
"stop": 6381.007,
|
||||
"takeProfit": 0,
|
||||
"direction": "Sell",
|
||||
"size": 0,
|
||||
"status": "Signaled",
|
||||
"sizeFilled": 0,
|
||||
"exitOutcome": "Stop Loss"
|
||||
}
|
||||
})
|
||||
|
||||
let expandedRecord = getExpandedRecord(fileContent)
|
||||
let result = JSON.stringify(expandedRecord)
|
||||
assert.equal(result, expected)
|
||||
})
|
||||
it("createRecordFromObject", function () {
|
||||
let expected = JSON.stringify(JSON.parse('[136, "EN", "EX", "ORD", 1553850096262, [1, "S", 155385234234, "U", "Poloniex", "BTC/USDT", 0, "L", 6286.707, 6381.007, 0, "Sell", 0, "SIG", 0, "SL"]]'))
|
||||
|
||||
let newRecord = {}
|
||||
newRecord.id = 136
|
||||
newRecord.from = MESSAGE_ENTITY.SimulationEngine
|
||||
newRecord.to = MESSAGE_ENTITY.SimulationExecutor
|
||||
newRecord.messageType = MESSAGE_TYPE.Order
|
||||
newRecord.dateTime = 1553850096262
|
||||
|
||||
newRecord.order = {}
|
||||
newRecord.order.id = 1
|
||||
newRecord.order.creator = ORDER_CREATOR.SimulationEngine
|
||||
newRecord.order.dateTime = 155385234234
|
||||
newRecord.order.owner = ORDER_OWNER.User
|
||||
newRecord.order.exchange = "Poloniex"
|
||||
newRecord.order.market = "BTC/USDT"
|
||||
newRecord.order.marginEnabled = ORDER_MARGIN_ENABLED.False
|
||||
newRecord.order.type = ORDER_TYPE.Limit
|
||||
newRecord.order.rate = 6286.707
|
||||
newRecord.order.stop = 6381.007
|
||||
newRecord.order.takeProfit = 0
|
||||
newRecord.order.direction = ORDER_DIRECTION.Sell
|
||||
newRecord.order.size = 0
|
||||
newRecord.order.status = ORDER_STATUS.Signaled
|
||||
newRecord.order.sizeFilled = 0
|
||||
newRecord.order.exitOutcome = ORDER_EXIT_OUTCOME.StopLoss
|
||||
|
||||
let recordFromObject = createRecordFromObject(newRecord)
|
||||
let result = JSON.stringify(recordFromObject)
|
||||
assert.equal(result, expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const colors = { enabled: true, visible: true, styles: {}, keys: {} };
|
||||
|
||||
if ('FORCE_COLOR' in process.env) {
|
||||
colors.enabled = process.env.FORCE_COLOR !== '0';
|
||||
}
|
||||
|
||||
const ansi = style => {
|
||||
style.open = `\u001b[${style.codes[0]}m`;
|
||||
style.close = `\u001b[${style.codes[1]}m`;
|
||||
style.regex = new RegExp(`\\u001b\\[${style.codes[1]}m`, 'g');
|
||||
return style;
|
||||
};
|
||||
|
||||
const wrap = (style, str, nl) => {
|
||||
let { open, close, regex } = style;
|
||||
str = open + (str.includes(close) ? str.replace(regex, close + open) : str) + close;
|
||||
// see https://github.com/chalk/chalk/pull/92, thanks to the
|
||||
// chalk contributors for this fix. However, we've confirmed that
|
||||
// this issue is also present in Windows terminals
|
||||
return nl ? str.replace(/\r?\n/g, `${close}$&${open}`) : str;
|
||||
};
|
||||
|
||||
const style = (input, stack) => {
|
||||
if (input === '' || input == null) return '';
|
||||
if (colors.enabled === false) return input;
|
||||
if (colors.visible === false) return '';
|
||||
let str = '' + input;
|
||||
let nl = str.includes('\n');
|
||||
let n = stack.length;
|
||||
while (n-- > 0) str = wrap(colors.styles[stack[n]], str, nl);
|
||||
return str;
|
||||
};
|
||||
|
||||
const define = (name, codes, type) => {
|
||||
colors.styles[name] = ansi({ name, codes });
|
||||
let t = colors.keys[type] || (colors.keys[type] = []);
|
||||
t.push(name);
|
||||
|
||||
Reflect.defineProperty(colors, name, {
|
||||
get() {
|
||||
let color = input => style(input, color.stack);
|
||||
Reflect.setPrototypeOf(color, colors);
|
||||
color.stack = this.stack ? this.stack.concat(name) : [name];
|
||||
return color;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
define('reset', [0, 0], 'modifier');
|
||||
define('bold', [1, 22], 'modifier');
|
||||
define('dim', [2, 22], 'modifier');
|
||||
define('italic', [3, 23], 'modifier');
|
||||
define('underline', [4, 24], 'modifier');
|
||||
define('inverse', [7, 27], 'modifier');
|
||||
define('hidden', [8, 28], 'modifier');
|
||||
define('strikethrough', [9, 29], 'modifier');
|
||||
|
||||
define('black', [30, 39], 'color');
|
||||
define('red', [31, 39], 'color');
|
||||
define('green', [32, 39], 'color');
|
||||
define('yellow', [33, 39], 'color');
|
||||
define('blue', [34, 39], 'color');
|
||||
define('magenta', [35, 39], 'color');
|
||||
define('cyan', [36, 39], 'color');
|
||||
define('white', [37, 39], 'color');
|
||||
define('gray', [90, 39], 'color');
|
||||
define('grey', [90, 39], 'color');
|
||||
|
||||
define('bgBlack', [40, 49], 'bg');
|
||||
define('bgRed', [41, 49], 'bg');
|
||||
define('bgGreen', [42, 49], 'bg');
|
||||
define('bgYellow', [43, 49], 'bg');
|
||||
define('bgBlue', [44, 49], 'bg');
|
||||
define('bgMagenta', [45, 49], 'bg');
|
||||
define('bgCyan', [46, 49], 'bg');
|
||||
define('bgWhite', [47, 49], 'bg');
|
||||
|
||||
define('blackBright', [90, 39], 'bright');
|
||||
define('redBright', [91, 39], 'bright');
|
||||
define('greenBright', [92, 39], 'bright');
|
||||
define('yellowBright', [93, 39], 'bright');
|
||||
define('blueBright', [94, 39], 'bright');
|
||||
define('magentaBright', [95, 39], 'bright');
|
||||
define('cyanBright', [96, 39], 'bright');
|
||||
define('whiteBright', [97, 39], 'bright');
|
||||
|
||||
define('bgBlackBright', [100, 49], 'bgBright');
|
||||
define('bgRedBright', [101, 49], 'bgBright');
|
||||
define('bgGreenBright', [102, 49], 'bgBright');
|
||||
define('bgYellowBright', [103, 49], 'bgBright');
|
||||
define('bgBlueBright', [104, 49], 'bgBright');
|
||||
define('bgMagentaBright', [105, 49], 'bgBright');
|
||||
define('bgCyanBright', [106, 49], 'bgBright');
|
||||
define('bgWhiteBright', [107, 49], 'bgBright');
|
||||
|
||||
/* eslint-disable no-control-regex */
|
||||
const re = colors.ansiRegex = /\u001b\[\d+m/gm;
|
||||
colors.hasColor = colors.hasAnsi = str => {
|
||||
re.lastIndex = 0;
|
||||
return !!str && typeof str === 'string' && re.test(str);
|
||||
};
|
||||
|
||||
colors.unstyle = str => {
|
||||
re.lastIndex = 0;
|
||||
return typeof str === 'string' ? str.replace(re, '') : str;
|
||||
};
|
||||
|
||||
colors.none = colors.clear = colors.noop = str => str; // no-op, for programmatic usage
|
||||
colors.stripColor = colors.unstyle;
|
||||
colors.symbols = require('./symbols');
|
||||
colors.define = define;
|
||||
module.exports = colors;
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"ansi-colors@3.2.3",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "ansi-colors@3.2.3",
|
||||
"_id": "ansi-colors@3.2.3",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
|
||||
"_location": "/ansi-colors",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "ansi-colors@3.2.3",
|
||||
"name": "ansi-colors",
|
||||
"escapedName": "ansi-colors",
|
||||
"rawSpec": "3.2.3",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "3.2.3"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/mocha"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
|
||||
"_spec": "3.2.3",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Brian Woodward",
|
||||
"url": "https://github.com/doowb"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/doowb/ansi-colors/issues"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Brian Woodward",
|
||||
"url": "https://twitter.com/doowb"
|
||||
},
|
||||
{
|
||||
"name": "Jason Schilling",
|
||||
"url": "https://sourecode.de"
|
||||
},
|
||||
{
|
||||
"name": "Jon Schlinkert",
|
||||
"url": "http://twitter.com/jonschlinkert"
|
||||
},
|
||||
{
|
||||
"name": "Jordan",
|
||||
"url": "https://github.com/Silic0nS0ldier"
|
||||
}
|
||||
],
|
||||
"description": "Easily add ANSI colors to your text and symbols in the terminal. A faster drop-in replacement for chalk, kleur and turbocolor (without the dependencies and rendering bugs).",
|
||||
"devDependencies": {
|
||||
"decache": "^4.4.0",
|
||||
"gulp-format-md": "^1.0.0",
|
||||
"justified": "^1.0.1",
|
||||
"mocha": "^5.2.0",
|
||||
"text-table": "^0.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"symbols.js",
|
||||
"types/index.d.ts"
|
||||
],
|
||||
"homepage": "https://github.com/doowb/ansi-colors",
|
||||
"keywords": [
|
||||
"ansi",
|
||||
"bgblack",
|
||||
"bgBlack",
|
||||
"bgblue",
|
||||
"bgBlue",
|
||||
"bgcyan",
|
||||
"bgCyan",
|
||||
"bggreen",
|
||||
"bgGreen",
|
||||
"bgmagenta",
|
||||
"bgMagenta",
|
||||
"bgred",
|
||||
"bgRed",
|
||||
"bgwhite",
|
||||
"bgWhite",
|
||||
"bgyellow",
|
||||
"bgYellow",
|
||||
"black",
|
||||
"blue",
|
||||
"bold",
|
||||
"clorox",
|
||||
"colors",
|
||||
"cyan",
|
||||
"dim",
|
||||
"gray",
|
||||
"green",
|
||||
"grey",
|
||||
"hidden",
|
||||
"inverse",
|
||||
"italic",
|
||||
"kleur",
|
||||
"magenta",
|
||||
"red",
|
||||
"reset",
|
||||
"strikethrough",
|
||||
"underline",
|
||||
"white",
|
||||
"yellow"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"name": "ansi-colors",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/doowb/ansi-colors.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"types": "./types/index.d.ts",
|
||||
"verb": {
|
||||
"toc": false,
|
||||
"layout": "default",
|
||||
"tasks": [
|
||||
"readme"
|
||||
],
|
||||
"data": {
|
||||
"author": {
|
||||
"linkedin": "woodwardbrian",
|
||||
"twitter": "doowb"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"gulp-format-md"
|
||||
],
|
||||
"lint": {
|
||||
"reflinks": true
|
||||
},
|
||||
"related": {
|
||||
"list": [
|
||||
"ansi-wrap",
|
||||
"strip-color"
|
||||
]
|
||||
},
|
||||
"reflinks": [
|
||||
"chalk",
|
||||
"colorette",
|
||||
"colors",
|
||||
"kleur"
|
||||
]
|
||||
},
|
||||
"version": "3.2.3"
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
const windows = {
|
||||
bullet: '•',
|
||||
check: '√',
|
||||
cross: '×',
|
||||
ellipsis: '...',
|
||||
heart: '❤',
|
||||
info: 'i',
|
||||
line: '─',
|
||||
middot: '·',
|
||||
minus: '-',
|
||||
plus: '+',
|
||||
question: '?',
|
||||
questionSmall: '﹖',
|
||||
pointer: '>',
|
||||
pointerSmall: '»',
|
||||
warning: '‼'
|
||||
};
|
||||
|
||||
const other = {
|
||||
ballotCross: '✘',
|
||||
bullet: '•',
|
||||
check: '✔',
|
||||
cross: '✖',
|
||||
ellipsis: '…',
|
||||
heart: '❤',
|
||||
info: 'ℹ',
|
||||
line: '─',
|
||||
middot: '·',
|
||||
minus: '-',
|
||||
plus: '+',
|
||||
question: '?',
|
||||
questionFull: '?',
|
||||
questionSmall: '﹖',
|
||||
pointer: isLinux ? '▸' : '❯',
|
||||
pointerSmall: isLinux ? '‣' : '›',
|
||||
warning: '⚠'
|
||||
};
|
||||
|
||||
module.exports = isWindows ? windows : other;
|
||||
Reflect.defineProperty(module.exports, 'windows', { enumerable: false, value: windows });
|
||||
Reflect.defineProperty(module.exports, 'other', { enumerable: false, value: other });
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = () => {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
|
||||
return new RegExp(pattern, 'g');
|
||||
};
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"ansi-regex@3.0.0",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "ansi-regex@3.0.0",
|
||||
"_id": "ansi-regex@3.0.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"_location": "/ansi-regex",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "ansi-regex@3.0.0",
|
||||
"name": "ansi-regex",
|
||||
"escapedName": "ansi-regex",
|
||||
"rawSpec": "3.0.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "3.0.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/strip-ansi"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"_spec": "3.0.0",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "sindresorhus.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/chalk/ansi-regex/issues"
|
||||
},
|
||||
"description": "Regular expression for matching ANSI escape codes",
|
||||
"devDependencies": {
|
||||
"ava": "*",
|
||||
"xo": "*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"homepage": "https://github.com/chalk/ansi-regex#readme",
|
||||
"keywords": [
|
||||
"ansi",
|
||||
"styles",
|
||||
"color",
|
||||
"colour",
|
||||
"colors",
|
||||
"terminal",
|
||||
"console",
|
||||
"cli",
|
||||
"string",
|
||||
"tty",
|
||||
"escape",
|
||||
"formatting",
|
||||
"rgb",
|
||||
"256",
|
||||
"shell",
|
||||
"xterm",
|
||||
"command-line",
|
||||
"text",
|
||||
"regex",
|
||||
"regexp",
|
||||
"re",
|
||||
"match",
|
||||
"test",
|
||||
"find",
|
||||
"pattern"
|
||||
],
|
||||
"license": "MIT",
|
||||
"name": "ansi-regex",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/chalk/ansi-regex.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && ava",
|
||||
"view-supported": "node fixtures/view-codes.js"
|
||||
},
|
||||
"version": "3.0.0"
|
||||
}
|
||||
-165
@@ -1,165 +0,0 @@
|
||||
'use strict';
|
||||
const colorConvert = require('color-convert');
|
||||
|
||||
const wrapAnsi16 = (fn, offset) => function () {
|
||||
const code = fn.apply(colorConvert, arguments);
|
||||
return `\u001B[${code + offset}m`;
|
||||
};
|
||||
|
||||
const wrapAnsi256 = (fn, offset) => function () {
|
||||
const code = fn.apply(colorConvert, arguments);
|
||||
return `\u001B[${38 + offset};5;${code}m`;
|
||||
};
|
||||
|
||||
const wrapAnsi16m = (fn, offset) => function () {
|
||||
const rgb = fn.apply(colorConvert, arguments);
|
||||
return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
|
||||
};
|
||||
|
||||
function assembleStyles() {
|
||||
const codes = new Map();
|
||||
const styles = {
|
||||
modifier: {
|
||||
reset: [0, 0],
|
||||
// 21 isn't widely supported and 22 does the same thing
|
||||
bold: [1, 22],
|
||||
dim: [2, 22],
|
||||
italic: [3, 23],
|
||||
underline: [4, 24],
|
||||
inverse: [7, 27],
|
||||
hidden: [8, 28],
|
||||
strikethrough: [9, 29]
|
||||
},
|
||||
color: {
|
||||
black: [30, 39],
|
||||
red: [31, 39],
|
||||
green: [32, 39],
|
||||
yellow: [33, 39],
|
||||
blue: [34, 39],
|
||||
magenta: [35, 39],
|
||||
cyan: [36, 39],
|
||||
white: [37, 39],
|
||||
gray: [90, 39],
|
||||
|
||||
// Bright color
|
||||
redBright: [91, 39],
|
||||
greenBright: [92, 39],
|
||||
yellowBright: [93, 39],
|
||||
blueBright: [94, 39],
|
||||
magentaBright: [95, 39],
|
||||
cyanBright: [96, 39],
|
||||
whiteBright: [97, 39]
|
||||
},
|
||||
bgColor: {
|
||||
bgBlack: [40, 49],
|
||||
bgRed: [41, 49],
|
||||
bgGreen: [42, 49],
|
||||
bgYellow: [43, 49],
|
||||
bgBlue: [44, 49],
|
||||
bgMagenta: [45, 49],
|
||||
bgCyan: [46, 49],
|
||||
bgWhite: [47, 49],
|
||||
|
||||
// Bright color
|
||||
bgBlackBright: [100, 49],
|
||||
bgRedBright: [101, 49],
|
||||
bgGreenBright: [102, 49],
|
||||
bgYellowBright: [103, 49],
|
||||
bgBlueBright: [104, 49],
|
||||
bgMagentaBright: [105, 49],
|
||||
bgCyanBright: [106, 49],
|
||||
bgWhiteBright: [107, 49]
|
||||
}
|
||||
};
|
||||
|
||||
// Fix humans
|
||||
styles.color.grey = styles.color.gray;
|
||||
|
||||
for (const groupName of Object.keys(styles)) {
|
||||
const group = styles[groupName];
|
||||
|
||||
for (const styleName of Object.keys(group)) {
|
||||
const style = group[styleName];
|
||||
|
||||
styles[styleName] = {
|
||||
open: `\u001B[${style[0]}m`,
|
||||
close: `\u001B[${style[1]}m`
|
||||
};
|
||||
|
||||
group[styleName] = styles[styleName];
|
||||
|
||||
codes.set(style[0], style[1]);
|
||||
}
|
||||
|
||||
Object.defineProperty(styles, groupName, {
|
||||
value: group,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(styles, 'codes', {
|
||||
value: codes,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
const ansi2ansi = n => n;
|
||||
const rgb2rgb = (r, g, b) => [r, g, b];
|
||||
|
||||
styles.color.close = '\u001B[39m';
|
||||
styles.bgColor.close = '\u001B[49m';
|
||||
|
||||
styles.color.ansi = {
|
||||
ansi: wrapAnsi16(ansi2ansi, 0)
|
||||
};
|
||||
styles.color.ansi256 = {
|
||||
ansi256: wrapAnsi256(ansi2ansi, 0)
|
||||
};
|
||||
styles.color.ansi16m = {
|
||||
rgb: wrapAnsi16m(rgb2rgb, 0)
|
||||
};
|
||||
|
||||
styles.bgColor.ansi = {
|
||||
ansi: wrapAnsi16(ansi2ansi, 10)
|
||||
};
|
||||
styles.bgColor.ansi256 = {
|
||||
ansi256: wrapAnsi256(ansi2ansi, 10)
|
||||
};
|
||||
styles.bgColor.ansi16m = {
|
||||
rgb: wrapAnsi16m(rgb2rgb, 10)
|
||||
};
|
||||
|
||||
for (let key of Object.keys(colorConvert)) {
|
||||
if (typeof colorConvert[key] !== 'object') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const suite = colorConvert[key];
|
||||
|
||||
if (key === 'ansi16') {
|
||||
key = 'ansi';
|
||||
}
|
||||
|
||||
if ('ansi16' in suite) {
|
||||
styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0);
|
||||
styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10);
|
||||
}
|
||||
|
||||
if ('ansi256' in suite) {
|
||||
styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0);
|
||||
styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10);
|
||||
}
|
||||
|
||||
if ('rgb' in suite) {
|
||||
styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0);
|
||||
styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
// Make the export immutable
|
||||
Object.defineProperty(module, 'exports', {
|
||||
enumerable: true,
|
||||
get: assembleStyles
|
||||
});
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"ansi-styles@3.2.1",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "ansi-styles@3.2.1",
|
||||
"_id": "ansi-styles@3.2.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"_location": "/ansi-styles",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "ansi-styles@3.2.1",
|
||||
"name": "ansi-styles",
|
||||
"escapedName": "ansi-styles",
|
||||
"rawSpec": "3.2.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "3.2.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/chalk"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"_spec": "3.2.1",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "sindresorhus.com"
|
||||
},
|
||||
"ava": {
|
||||
"require": "babel-polyfill"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/chalk/ansi-styles/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"description": "ANSI escape codes for styling strings in the terminal",
|
||||
"devDependencies": {
|
||||
"ava": "*",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"svg-term-cli": "^2.1.1",
|
||||
"xo": "*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"homepage": "https://github.com/chalk/ansi-styles#readme",
|
||||
"keywords": [
|
||||
"ansi",
|
||||
"styles",
|
||||
"color",
|
||||
"colour",
|
||||
"colors",
|
||||
"terminal",
|
||||
"console",
|
||||
"cli",
|
||||
"string",
|
||||
"tty",
|
||||
"escape",
|
||||
"formatting",
|
||||
"rgb",
|
||||
"256",
|
||||
"shell",
|
||||
"xterm",
|
||||
"log",
|
||||
"logging",
|
||||
"command-line",
|
||||
"text"
|
||||
],
|
||||
"license": "MIT",
|
||||
"name": "ansi-styles",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/chalk/ansi-styles.git"
|
||||
},
|
||||
"scripts": {
|
||||
"screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor",
|
||||
"test": "xo && ava"
|
||||
},
|
||||
"version": "3.2.1"
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./lib/argparse');
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* class Action
|
||||
*
|
||||
* Base class for all actions
|
||||
* Do not call in your code, use this class only for inherits your own action
|
||||
*
|
||||
* Information about how to convert command line strings to Javascript objects.
|
||||
* Action objects are used by an ArgumentParser to represent the information
|
||||
* needed to parse a single argument from one or more strings from the command
|
||||
* line. The keyword arguments to the Action constructor are also all attributes
|
||||
* of Action instances.
|
||||
*
|
||||
* ##### Allowed keywords:
|
||||
*
|
||||
* - `store`
|
||||
* - `storeConstant`
|
||||
* - `storeTrue`
|
||||
* - `storeFalse`
|
||||
* - `append`
|
||||
* - `appendConstant`
|
||||
* - `count`
|
||||
* - `help`
|
||||
* - `version`
|
||||
*
|
||||
* Information about action options see [[Action.new]]
|
||||
*
|
||||
* See also [original guide](http://docs.python.org/dev/library/argparse.html#action)
|
||||
*
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
// Constants
|
||||
var c = require('./const');
|
||||
|
||||
|
||||
/**
|
||||
* new Action(options)
|
||||
*
|
||||
* Base class for all actions. Used only for inherits
|
||||
*
|
||||
*
|
||||
* ##### Options:
|
||||
*
|
||||
* - `optionStrings` A list of command-line option strings for the action.
|
||||
* - `dest` Attribute to hold the created object(s)
|
||||
* - `nargs` The number of command-line arguments that should be consumed.
|
||||
* By default, one argument will be consumed and a single value will be
|
||||
* produced.
|
||||
* - `constant` Default value for an action with no value.
|
||||
* - `defaultValue` The value to be produced if the option is not specified.
|
||||
* - `type` Cast to 'string'|'int'|'float'|'complex'|function (string). If
|
||||
* None, 'string'.
|
||||
* - `choices` The choices available.
|
||||
* - `required` True if the action must always be specified at the command
|
||||
* line.
|
||||
* - `help` The help describing the argument.
|
||||
* - `metavar` The name to be used for the option's argument with the help
|
||||
* string. If None, the 'dest' value will be used as the name.
|
||||
*
|
||||
* ##### nargs supported values:
|
||||
*
|
||||
* - `N` (an integer) consumes N arguments (and produces a list)
|
||||
* - `?` consumes zero or one arguments
|
||||
* - `*` consumes zero or more arguments (and produces a list)
|
||||
* - `+` consumes one or more arguments (and produces a list)
|
||||
*
|
||||
* Note: that the difference between the default and nargs=1 is that with the
|
||||
* default, a single value will be produced, while with nargs=1, a list
|
||||
* containing a single value will be produced.
|
||||
**/
|
||||
var Action = module.exports = function Action(options) {
|
||||
options = options || {};
|
||||
this.optionStrings = options.optionStrings || [];
|
||||
this.dest = options.dest;
|
||||
this.nargs = typeof options.nargs !== 'undefined' ? options.nargs : null;
|
||||
this.constant = typeof options.constant !== 'undefined' ? options.constant : null;
|
||||
this.defaultValue = options.defaultValue;
|
||||
this.type = typeof options.type !== 'undefined' ? options.type : null;
|
||||
this.choices = typeof options.choices !== 'undefined' ? options.choices : null;
|
||||
this.required = typeof options.required !== 'undefined' ? options.required : false;
|
||||
this.help = typeof options.help !== 'undefined' ? options.help : null;
|
||||
this.metavar = typeof options.metavar !== 'undefined' ? options.metavar : null;
|
||||
|
||||
if (!(this.optionStrings instanceof Array)) {
|
||||
throw new Error('optionStrings should be an array');
|
||||
}
|
||||
if (typeof this.required !== 'undefined' && typeof this.required !== 'boolean') {
|
||||
throw new Error('required should be a boolean');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Action#getName -> String
|
||||
*
|
||||
* Tells action name
|
||||
**/
|
||||
Action.prototype.getName = function () {
|
||||
if (this.optionStrings.length > 0) {
|
||||
return this.optionStrings.join('/');
|
||||
} else if (this.metavar !== null && this.metavar !== c.SUPPRESS) {
|
||||
return this.metavar;
|
||||
} else if (typeof this.dest !== 'undefined' && this.dest !== c.SUPPRESS) {
|
||||
return this.dest;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Action#isOptional -> Boolean
|
||||
*
|
||||
* Return true if optional
|
||||
**/
|
||||
Action.prototype.isOptional = function () {
|
||||
return !this.isPositional();
|
||||
};
|
||||
|
||||
/**
|
||||
* Action#isPositional -> Boolean
|
||||
*
|
||||
* Return true if positional
|
||||
**/
|
||||
Action.prototype.isPositional = function () {
|
||||
return (this.optionStrings.length === 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Action#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Should be implemented in inherited classes
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* ActionCount.prototype.call = function (parser, namespace, values, optionString) {
|
||||
* namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
|
||||
* };
|
||||
*
|
||||
**/
|
||||
Action.prototype.call = function () {
|
||||
throw new Error('.call() not defined');// Not Implemented error
|
||||
};
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionAppend
|
||||
*
|
||||
* This action stores a list, and appends each argument value to the list.
|
||||
* This is useful to allow an option to be specified multiple times.
|
||||
* This class inherided from [[Action]]
|
||||
*
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionAppend(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
* Note: options.nargs should be optional for constants
|
||||
* and more then zero for other
|
||||
**/
|
||||
var ActionAppend = module.exports = function ActionAppend(options) {
|
||||
options = options || {};
|
||||
if (this.nargs <= 0) {
|
||||
throw new Error('nargs for append actions must be > 0; if arg ' +
|
||||
'strings are not supplying the value to append, ' +
|
||||
'the append const action may be more appropriate');
|
||||
}
|
||||
if (!!this.constant && this.nargs !== c.OPTIONAL) {
|
||||
throw new Error('nargs must be OPTIONAL to supply const');
|
||||
}
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionAppend, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionAppend#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Save result in namespace object
|
||||
**/
|
||||
ActionAppend.prototype.call = function (parser, namespace, values) {
|
||||
var items = (namespace[this.dest] || []).slice();
|
||||
items.push(values);
|
||||
namespace.set(this.dest, items);
|
||||
};
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionAppendConstant
|
||||
*
|
||||
* This stores a list, and appends the value specified by
|
||||
* the const keyword argument to the list.
|
||||
* (Note that the const keyword argument defaults to null.)
|
||||
* The 'appendConst' action is typically useful when multiple
|
||||
* arguments need to store constants to the same list.
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../../action');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionAppendConstant(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionAppendConstant = module.exports = function ActionAppendConstant(options) {
|
||||
options = options || {};
|
||||
options.nargs = 0;
|
||||
if (typeof options.constant === 'undefined') {
|
||||
throw new Error('constant option is required for appendAction');
|
||||
}
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionAppendConstant, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionAppendConstant#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Save result in namespace object
|
||||
**/
|
||||
ActionAppendConstant.prototype.call = function (parser, namespace) {
|
||||
var items = [].concat(namespace[this.dest] || []);
|
||||
items.push(this.constant);
|
||||
namespace.set(this.dest, items);
|
||||
};
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionCount
|
||||
*
|
||||
* This counts the number of times a keyword argument occurs.
|
||||
* For example, this is useful for increasing verbosity levels
|
||||
*
|
||||
* This class inherided from [[Action]]
|
||||
*
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionCount(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionCount = module.exports = function ActionCount(options) {
|
||||
options = options || {};
|
||||
options.nargs = 0;
|
||||
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionCount, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionCount#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Save result in namespace object
|
||||
**/
|
||||
ActionCount.prototype.call = function (parser, namespace) {
|
||||
namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
|
||||
};
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionHelp
|
||||
*
|
||||
* Support action for printing help
|
||||
* This class inherided from [[Action]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionHelp(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionHelp = module.exports = function ActionHelp(options) {
|
||||
options = options || {};
|
||||
if (options.defaultValue !== null) {
|
||||
options.defaultValue = options.defaultValue;
|
||||
} else {
|
||||
options.defaultValue = c.SUPPRESS;
|
||||
}
|
||||
options.dest = (options.dest !== null ? options.dest : c.SUPPRESS);
|
||||
options.nargs = 0;
|
||||
Action.call(this, options);
|
||||
|
||||
};
|
||||
util.inherits(ActionHelp, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionHelp#call(parser, namespace, values, optionString)
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Print help and exit
|
||||
**/
|
||||
ActionHelp.prototype.call = function (parser) {
|
||||
parser.printHelp();
|
||||
parser.exit();
|
||||
};
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionStore
|
||||
*
|
||||
* This action just stores the argument’s value. This is the default action.
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
*
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionStore(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionStore = module.exports = function ActionStore(options) {
|
||||
options = options || {};
|
||||
if (this.nargs <= 0) {
|
||||
throw new Error('nargs for store actions must be > 0; if you ' +
|
||||
'have nothing to store, actions such as store ' +
|
||||
'true or store const may be more appropriate');
|
||||
|
||||
}
|
||||
if (typeof this.constant !== 'undefined' && this.nargs !== c.OPTIONAL) {
|
||||
throw new Error('nargs must be OPTIONAL to supply const');
|
||||
}
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionStore, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionStore#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Save result in namespace object
|
||||
**/
|
||||
ActionStore.prototype.call = function (parser, namespace, values) {
|
||||
namespace.set(this.dest, values);
|
||||
};
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionStoreConstant
|
||||
*
|
||||
* This action stores the value specified by the const keyword argument.
|
||||
* (Note that the const keyword argument defaults to the rather unhelpful null.)
|
||||
* The 'store_const' action is most commonly used with optional
|
||||
* arguments that specify some sort of flag.
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../../action');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionStoreConstant(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionStoreConstant = module.exports = function ActionStoreConstant(options) {
|
||||
options = options || {};
|
||||
options.nargs = 0;
|
||||
if (typeof options.constant === 'undefined') {
|
||||
throw new Error('constant option is required for storeAction');
|
||||
}
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionStoreConstant, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionStoreConstant#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Save result in namespace object
|
||||
**/
|
||||
ActionStoreConstant.prototype.call = function (parser, namespace) {
|
||||
namespace.set(this.dest, this.constant);
|
||||
};
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionStoreFalse
|
||||
*
|
||||
* This action store the values False respectively.
|
||||
* This is special cases of 'storeConst'
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var ActionStoreConstant = require('./constant');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionStoreFalse(options)
|
||||
* - options (object): hash of options see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionStoreFalse = module.exports = function ActionStoreFalse(options) {
|
||||
options = options || {};
|
||||
options.constant = false;
|
||||
options.defaultValue = options.defaultValue !== null ? options.defaultValue : true;
|
||||
ActionStoreConstant.call(this, options);
|
||||
};
|
||||
util.inherits(ActionStoreFalse, ActionStoreConstant);
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionStoreTrue
|
||||
*
|
||||
* This action store the values True respectively.
|
||||
* This isspecial cases of 'storeConst'
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var ActionStoreConstant = require('./constant');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionStoreTrue(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionStoreTrue = module.exports = function ActionStoreTrue(options) {
|
||||
options = options || {};
|
||||
options.constant = true;
|
||||
options.defaultValue = options.defaultValue !== null ? options.defaultValue : false;
|
||||
ActionStoreConstant.call(this, options);
|
||||
};
|
||||
util.inherits(ActionStoreTrue, ActionStoreConstant);
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
/** internal
|
||||
* class ActionSubparsers
|
||||
*
|
||||
* Support the creation of such sub-commands with the addSubparsers()
|
||||
*
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var format = require('util').format;
|
||||
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
// Errors
|
||||
var argumentErrorHelper = require('../argument/error');
|
||||
|
||||
|
||||
/*:nodoc:*
|
||||
* new ChoicesPseudoAction(name, help)
|
||||
*
|
||||
* Create pseudo action for correct help text
|
||||
*
|
||||
**/
|
||||
function ChoicesPseudoAction(name, help) {
|
||||
var options = {
|
||||
optionStrings: [],
|
||||
dest: name,
|
||||
help: help
|
||||
};
|
||||
|
||||
Action.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(ChoicesPseudoAction, Action);
|
||||
|
||||
/**
|
||||
* new ActionSubparsers(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
function ActionSubparsers(options) {
|
||||
options = options || {};
|
||||
options.dest = options.dest || c.SUPPRESS;
|
||||
options.nargs = c.PARSER;
|
||||
|
||||
this.debug = (options.debug === true);
|
||||
|
||||
this._progPrefix = options.prog;
|
||||
this._parserClass = options.parserClass;
|
||||
this._nameParserMap = {};
|
||||
this._choicesActions = [];
|
||||
|
||||
options.choices = this._nameParserMap;
|
||||
Action.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(ActionSubparsers, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionSubparsers#addParser(name, options) -> ArgumentParser
|
||||
* - name (string): sub-command name
|
||||
* - options (object): see [[ArgumentParser.new]]
|
||||
*
|
||||
* Note:
|
||||
* addParser supports an additional aliases option,
|
||||
* which allows multiple strings to refer to the same subparser.
|
||||
* This example, like svn, aliases co as a shorthand for checkout
|
||||
*
|
||||
**/
|
||||
ActionSubparsers.prototype.addParser = function (name, options) {
|
||||
var parser;
|
||||
|
||||
var self = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
options.debug = (this.debug === true);
|
||||
|
||||
// set program from the existing prefix
|
||||
if (!options.prog) {
|
||||
options.prog = this._progPrefix + ' ' + name;
|
||||
}
|
||||
|
||||
var aliases = options.aliases || [];
|
||||
|
||||
// create a pseudo-action to hold the choice help
|
||||
if (!!options.help || typeof options.help === 'string') {
|
||||
var help = options.help;
|
||||
delete options.help;
|
||||
|
||||
var choiceAction = new ChoicesPseudoAction(name, help);
|
||||
this._choicesActions.push(choiceAction);
|
||||
}
|
||||
|
||||
// create the parser and add it to the map
|
||||
parser = new this._parserClass(options);
|
||||
this._nameParserMap[name] = parser;
|
||||
|
||||
// make parser available under aliases also
|
||||
aliases.forEach(function (alias) {
|
||||
self._nameParserMap[alias] = parser;
|
||||
});
|
||||
|
||||
return parser;
|
||||
};
|
||||
|
||||
ActionSubparsers.prototype._getSubactions = function () {
|
||||
return this._choicesActions;
|
||||
};
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionSubparsers#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Call the action. Parse input aguments
|
||||
**/
|
||||
ActionSubparsers.prototype.call = function (parser, namespace, values) {
|
||||
var parserName = values[0];
|
||||
var argStrings = values.slice(1);
|
||||
|
||||
// set the parser name if requested
|
||||
if (this.dest !== c.SUPPRESS) {
|
||||
namespace[this.dest] = parserName;
|
||||
}
|
||||
|
||||
// select the parser
|
||||
if (this._nameParserMap[parserName]) {
|
||||
parser = this._nameParserMap[parserName];
|
||||
} else {
|
||||
throw argumentErrorHelper(format(
|
||||
'Unknown parser "%s" (choices: [%s]).',
|
||||
parserName,
|
||||
Object.keys(this._nameParserMap).join(', ')
|
||||
));
|
||||
}
|
||||
|
||||
// parse all the remaining options into the namespace
|
||||
parser.parseArgs(argStrings, namespace);
|
||||
};
|
||||
|
||||
module.exports = ActionSubparsers;
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*:nodoc:*
|
||||
* class ActionVersion
|
||||
*
|
||||
* Support action for printing program version
|
||||
* This class inherited from [[Action]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var Action = require('../action');
|
||||
|
||||
//
|
||||
// Constants
|
||||
//
|
||||
var c = require('../const');
|
||||
|
||||
/*:nodoc:*
|
||||
* new ActionVersion(options)
|
||||
* - options (object): options hash see [[Action.new]]
|
||||
*
|
||||
**/
|
||||
var ActionVersion = module.exports = function ActionVersion(options) {
|
||||
options = options || {};
|
||||
options.defaultValue = (options.defaultValue ? options.defaultValue : c.SUPPRESS);
|
||||
options.dest = (options.dest || c.SUPPRESS);
|
||||
options.nargs = 0;
|
||||
this.version = options.version;
|
||||
Action.call(this, options);
|
||||
};
|
||||
util.inherits(ActionVersion, Action);
|
||||
|
||||
/*:nodoc:*
|
||||
* ActionVersion#call(parser, namespace, values, optionString) -> Void
|
||||
* - parser (ArgumentParser): current parser
|
||||
* - namespace (Namespace): namespace for output data
|
||||
* - values (Array): parsed values
|
||||
* - optionString (Array): input option string(not parsed)
|
||||
*
|
||||
* Print version and exit
|
||||
**/
|
||||
ActionVersion.prototype.call = function (parser) {
|
||||
var version = this.version || parser.version;
|
||||
var formatter = parser._getFormatter();
|
||||
formatter.addText(version);
|
||||
parser.exit(0, formatter.formatHelp());
|
||||
};
|
||||
-482
@@ -1,482 +0,0 @@
|
||||
/** internal
|
||||
* class ActionContainer
|
||||
*
|
||||
* Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
var format = require('util').format;
|
||||
|
||||
// Constants
|
||||
var c = require('./const');
|
||||
|
||||
var $$ = require('./utils');
|
||||
|
||||
//Actions
|
||||
var ActionHelp = require('./action/help');
|
||||
var ActionAppend = require('./action/append');
|
||||
var ActionAppendConstant = require('./action/append/constant');
|
||||
var ActionCount = require('./action/count');
|
||||
var ActionStore = require('./action/store');
|
||||
var ActionStoreConstant = require('./action/store/constant');
|
||||
var ActionStoreTrue = require('./action/store/true');
|
||||
var ActionStoreFalse = require('./action/store/false');
|
||||
var ActionVersion = require('./action/version');
|
||||
var ActionSubparsers = require('./action/subparsers');
|
||||
|
||||
// Errors
|
||||
var argumentErrorHelper = require('./argument/error');
|
||||
|
||||
/**
|
||||
* new ActionContainer(options)
|
||||
*
|
||||
* Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
|
||||
*
|
||||
* ##### Options:
|
||||
*
|
||||
* - `description` -- A description of what the program does
|
||||
* - `prefixChars` -- Characters that prefix optional arguments
|
||||
* - `argumentDefault` -- The default value for all arguments
|
||||
* - `conflictHandler` -- The conflict handler to use for duplicate arguments
|
||||
**/
|
||||
var ActionContainer = module.exports = function ActionContainer(options) {
|
||||
options = options || {};
|
||||
|
||||
this.description = options.description;
|
||||
this.argumentDefault = options.argumentDefault;
|
||||
this.prefixChars = options.prefixChars || '';
|
||||
this.conflictHandler = options.conflictHandler;
|
||||
|
||||
// set up registries
|
||||
this._registries = {};
|
||||
|
||||
// register actions
|
||||
this.register('action', null, ActionStore);
|
||||
this.register('action', 'store', ActionStore);
|
||||
this.register('action', 'storeConst', ActionStoreConstant);
|
||||
this.register('action', 'storeTrue', ActionStoreTrue);
|
||||
this.register('action', 'storeFalse', ActionStoreFalse);
|
||||
this.register('action', 'append', ActionAppend);
|
||||
this.register('action', 'appendConst', ActionAppendConstant);
|
||||
this.register('action', 'count', ActionCount);
|
||||
this.register('action', 'help', ActionHelp);
|
||||
this.register('action', 'version', ActionVersion);
|
||||
this.register('action', 'parsers', ActionSubparsers);
|
||||
|
||||
// raise an exception if the conflict handler is invalid
|
||||
this._getHandler();
|
||||
|
||||
// action storage
|
||||
this._actions = [];
|
||||
this._optionStringActions = {};
|
||||
|
||||
// groups
|
||||
this._actionGroups = [];
|
||||
this._mutuallyExclusiveGroups = [];
|
||||
|
||||
// defaults storage
|
||||
this._defaults = {};
|
||||
|
||||
// determines whether an "option" looks like a negative number
|
||||
// -1, -1.5 -5e+4
|
||||
this._regexpNegativeNumber = new RegExp('^[-]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$');
|
||||
|
||||
// whether or not there are any optionals that look like negative
|
||||
// numbers -- uses a list so it can be shared and edited
|
||||
this._hasNegativeNumberOptionals = [];
|
||||
};
|
||||
|
||||
// Groups must be required, then ActionContainer already defined
|
||||
var ArgumentGroup = require('./argument/group');
|
||||
var MutuallyExclusiveGroup = require('./argument/exclusive');
|
||||
|
||||
//
|
||||
// Registration methods
|
||||
//
|
||||
|
||||
/**
|
||||
* ActionContainer#register(registryName, value, object) -> Void
|
||||
* - registryName (String) : object type action|type
|
||||
* - value (string) : keyword
|
||||
* - object (Object|Function) : handler
|
||||
*
|
||||
* Register handlers
|
||||
**/
|
||||
ActionContainer.prototype.register = function (registryName, value, object) {
|
||||
this._registries[registryName] = this._registries[registryName] || {};
|
||||
this._registries[registryName][value] = object;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._registryGet = function (registryName, value, defaultValue) {
|
||||
if (arguments.length < 3) {
|
||||
defaultValue = null;
|
||||
}
|
||||
return this._registries[registryName][value] || defaultValue;
|
||||
};
|
||||
|
||||
//
|
||||
// Namespace default accessor methods
|
||||
//
|
||||
|
||||
/**
|
||||
* ActionContainer#setDefaults(options) -> Void
|
||||
* - options (object):hash of options see [[Action.new]]
|
||||
*
|
||||
* Set defaults
|
||||
**/
|
||||
ActionContainer.prototype.setDefaults = function (options) {
|
||||
options = options || {};
|
||||
for (var property in options) {
|
||||
if ($$.has(options, property)) {
|
||||
this._defaults[property] = options[property];
|
||||
}
|
||||
}
|
||||
|
||||
// if these defaults match any existing arguments, replace the previous
|
||||
// default on the object with the new one
|
||||
this._actions.forEach(function (action) {
|
||||
if ($$.has(options, action.dest)) {
|
||||
action.defaultValue = options[action.dest];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ActionContainer#getDefault(dest) -> Mixed
|
||||
* - dest (string): action destination
|
||||
*
|
||||
* Return action default value
|
||||
**/
|
||||
ActionContainer.prototype.getDefault = function (dest) {
|
||||
var result = $$.has(this._defaults, dest) ? this._defaults[dest] : null;
|
||||
|
||||
this._actions.forEach(function (action) {
|
||||
if (action.dest === dest && $$.has(action, 'defaultValue')) {
|
||||
result = action.defaultValue;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
//
|
||||
// Adding argument actions
|
||||
//
|
||||
|
||||
/**
|
||||
* ActionContainer#addArgument(args, options) -> Object
|
||||
* - args (String|Array): argument key, or array of argument keys
|
||||
* - options (Object): action objects see [[Action.new]]
|
||||
*
|
||||
* #### Examples
|
||||
* - addArgument([ '-f', '--foo' ], { action: 'store', defaultValue: 1, ... })
|
||||
* - addArgument([ 'bar' ], { action: 'store', nargs: 1, ... })
|
||||
* - addArgument('--baz', { action: 'store', nargs: 1, ... })
|
||||
**/
|
||||
ActionContainer.prototype.addArgument = function (args, options) {
|
||||
args = args;
|
||||
options = options || {};
|
||||
|
||||
if (typeof args === 'string') {
|
||||
args = [ args ];
|
||||
}
|
||||
if (!Array.isArray(args)) {
|
||||
throw new TypeError('addArgument first argument should be a string or an array');
|
||||
}
|
||||
if (typeof options !== 'object' || Array.isArray(options)) {
|
||||
throw new TypeError('addArgument second argument should be a hash');
|
||||
}
|
||||
|
||||
// if no positional args are supplied or only one is supplied and
|
||||
// it doesn't look like an option string, parse a positional argument
|
||||
if (!args || args.length === 1 && this.prefixChars.indexOf(args[0][0]) < 0) {
|
||||
if (args && !!options.dest) {
|
||||
throw new Error('dest supplied twice for positional argument');
|
||||
}
|
||||
options = this._getPositional(args, options);
|
||||
|
||||
// otherwise, we're adding an optional argument
|
||||
} else {
|
||||
options = this._getOptional(args, options);
|
||||
}
|
||||
|
||||
// if no default was supplied, use the parser-level default
|
||||
if (typeof options.defaultValue === 'undefined') {
|
||||
var dest = options.dest;
|
||||
if ($$.has(this._defaults, dest)) {
|
||||
options.defaultValue = this._defaults[dest];
|
||||
} else if (typeof this.argumentDefault !== 'undefined') {
|
||||
options.defaultValue = this.argumentDefault;
|
||||
}
|
||||
}
|
||||
|
||||
// create the action object, and add it to the parser
|
||||
var ActionClass = this._popActionClass(options);
|
||||
if (typeof ActionClass !== 'function') {
|
||||
throw new Error(format('Unknown action "%s".', ActionClass));
|
||||
}
|
||||
var action = new ActionClass(options);
|
||||
|
||||
// throw an error if the action type is not callable
|
||||
var typeFunction = this._registryGet('type', action.type, action.type);
|
||||
if (typeof typeFunction !== 'function') {
|
||||
throw new Error(format('"%s" is not callable', typeFunction));
|
||||
}
|
||||
|
||||
return this._addAction(action);
|
||||
};
|
||||
|
||||
/**
|
||||
* ActionContainer#addArgumentGroup(options) -> ArgumentGroup
|
||||
* - options (Object): hash of options see [[ArgumentGroup.new]]
|
||||
*
|
||||
* Create new arguments groups
|
||||
**/
|
||||
ActionContainer.prototype.addArgumentGroup = function (options) {
|
||||
var group = new ArgumentGroup(this, options);
|
||||
this._actionGroups.push(group);
|
||||
return group;
|
||||
};
|
||||
|
||||
/**
|
||||
* ActionContainer#addMutuallyExclusiveGroup(options) -> ArgumentGroup
|
||||
* - options (Object): {required: false}
|
||||
*
|
||||
* Create new mutual exclusive groups
|
||||
**/
|
||||
ActionContainer.prototype.addMutuallyExclusiveGroup = function (options) {
|
||||
var group = new MutuallyExclusiveGroup(this, options);
|
||||
this._mutuallyExclusiveGroups.push(group);
|
||||
return group;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._addAction = function (action) {
|
||||
var self = this;
|
||||
|
||||
// resolve any conflicts
|
||||
this._checkConflict(action);
|
||||
|
||||
// add to actions list
|
||||
this._actions.push(action);
|
||||
action.container = this;
|
||||
|
||||
// index the action by any option strings it has
|
||||
action.optionStrings.forEach(function (optionString) {
|
||||
self._optionStringActions[optionString] = action;
|
||||
});
|
||||
|
||||
// set the flag if any option strings look like negative numbers
|
||||
action.optionStrings.forEach(function (optionString) {
|
||||
if (optionString.match(self._regexpNegativeNumber)) {
|
||||
if (!self._hasNegativeNumberOptionals.some(Boolean)) {
|
||||
self._hasNegativeNumberOptionals.push(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// return the created action
|
||||
return action;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._removeAction = function (action) {
|
||||
var actionIndex = this._actions.indexOf(action);
|
||||
if (actionIndex >= 0) {
|
||||
this._actions.splice(actionIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
ActionContainer.prototype._addContainerActions = function (container) {
|
||||
// collect groups by titles
|
||||
var titleGroupMap = {};
|
||||
this._actionGroups.forEach(function (group) {
|
||||
if (titleGroupMap[group.title]) {
|
||||
throw new Error(format('Cannot merge actions - two groups are named "%s".', group.title));
|
||||
}
|
||||
titleGroupMap[group.title] = group;
|
||||
});
|
||||
|
||||
// map each action to its group
|
||||
var groupMap = {};
|
||||
function actionHash(action) {
|
||||
// unique (hopefully?) string suitable as dictionary key
|
||||
return action.getName();
|
||||
}
|
||||
container._actionGroups.forEach(function (group) {
|
||||
// if a group with the title exists, use that, otherwise
|
||||
// create a new group matching the container's group
|
||||
if (!titleGroupMap[group.title]) {
|
||||
titleGroupMap[group.title] = this.addArgumentGroup({
|
||||
title: group.title,
|
||||
description: group.description
|
||||
});
|
||||
}
|
||||
|
||||
// map the actions to their new group
|
||||
group._groupActions.forEach(function (action) {
|
||||
groupMap[actionHash(action)] = titleGroupMap[group.title];
|
||||
});
|
||||
}, this);
|
||||
|
||||
// add container's mutually exclusive groups
|
||||
// NOTE: if add_mutually_exclusive_group ever gains title= and
|
||||
// description= then this code will need to be expanded as above
|
||||
var mutexGroup;
|
||||
container._mutuallyExclusiveGroups.forEach(function (group) {
|
||||
mutexGroup = this.addMutuallyExclusiveGroup({
|
||||
required: group.required
|
||||
});
|
||||
// map the actions to their new mutex group
|
||||
group._groupActions.forEach(function (action) {
|
||||
groupMap[actionHash(action)] = mutexGroup;
|
||||
});
|
||||
}, this); // forEach takes a 'this' argument
|
||||
|
||||
// add all actions to this container or their group
|
||||
container._actions.forEach(function (action) {
|
||||
var key = actionHash(action);
|
||||
if (groupMap[key]) {
|
||||
groupMap[key]._addAction(action);
|
||||
} else {
|
||||
this._addAction(action);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ActionContainer.prototype._getPositional = function (dest, options) {
|
||||
if (Array.isArray(dest)) {
|
||||
dest = dest[0];
|
||||
}
|
||||
// make sure required is not specified
|
||||
if (options.required) {
|
||||
throw new Error('"required" is an invalid argument for positionals.');
|
||||
}
|
||||
|
||||
// mark positional arguments as required if at least one is
|
||||
// always required
|
||||
if (options.nargs !== c.OPTIONAL && options.nargs !== c.ZERO_OR_MORE) {
|
||||
options.required = true;
|
||||
}
|
||||
if (options.nargs === c.ZERO_OR_MORE && typeof options.defaultValue === 'undefined') {
|
||||
options.required = true;
|
||||
}
|
||||
|
||||
// return the keyword arguments with no option strings
|
||||
options.dest = dest;
|
||||
options.optionStrings = [];
|
||||
return options;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._getOptional = function (args, options) {
|
||||
var prefixChars = this.prefixChars;
|
||||
var optionStrings = [];
|
||||
var optionStringsLong = [];
|
||||
|
||||
// determine short and long option strings
|
||||
args.forEach(function (optionString) {
|
||||
// error on strings that don't start with an appropriate prefix
|
||||
if (prefixChars.indexOf(optionString[0]) < 0) {
|
||||
throw new Error(format('Invalid option string "%s": must start with a "%s".',
|
||||
optionString,
|
||||
prefixChars
|
||||
));
|
||||
}
|
||||
|
||||
// strings starting with two prefix characters are long options
|
||||
optionStrings.push(optionString);
|
||||
if (optionString.length > 1 && prefixChars.indexOf(optionString[1]) >= 0) {
|
||||
optionStringsLong.push(optionString);
|
||||
}
|
||||
});
|
||||
|
||||
// infer dest, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
|
||||
var dest = options.dest || null;
|
||||
delete options.dest;
|
||||
|
||||
if (!dest) {
|
||||
var optionStringDest = optionStringsLong.length ? optionStringsLong[0] : optionStrings[0];
|
||||
dest = $$.trimChars(optionStringDest, this.prefixChars);
|
||||
|
||||
if (dest.length === 0) {
|
||||
throw new Error(
|
||||
format('dest= is required for options like "%s"', optionStrings.join(', '))
|
||||
);
|
||||
}
|
||||
dest = dest.replace(/-/g, '_');
|
||||
}
|
||||
|
||||
// return the updated keyword arguments
|
||||
options.dest = dest;
|
||||
options.optionStrings = optionStrings;
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._popActionClass = function (options, defaultValue) {
|
||||
defaultValue = defaultValue || null;
|
||||
|
||||
var action = (options.action || defaultValue);
|
||||
delete options.action;
|
||||
|
||||
var actionClass = this._registryGet('action', action, action);
|
||||
return actionClass;
|
||||
};
|
||||
|
||||
ActionContainer.prototype._getHandler = function () {
|
||||
var handlerString = this.conflictHandler;
|
||||
var handlerFuncName = '_handleConflict' + $$.capitalize(handlerString);
|
||||
var func = this[handlerFuncName];
|
||||
if (typeof func === 'undefined') {
|
||||
var msg = 'invalid conflict resolution value: ' + handlerString;
|
||||
throw new Error(msg);
|
||||
} else {
|
||||
return func;
|
||||
}
|
||||
};
|
||||
|
||||
ActionContainer.prototype._checkConflict = function (action) {
|
||||
var optionStringActions = this._optionStringActions;
|
||||
var conflictOptionals = [];
|
||||
|
||||
// find all options that conflict with this option
|
||||
// collect pairs, the string, and an existing action that it conflicts with
|
||||
action.optionStrings.forEach(function (optionString) {
|
||||
var conflOptional = optionStringActions[optionString];
|
||||
if (typeof conflOptional !== 'undefined') {
|
||||
conflictOptionals.push([ optionString, conflOptional ]);
|
||||
}
|
||||
});
|
||||
|
||||
if (conflictOptionals.length > 0) {
|
||||
var conflictHandler = this._getHandler();
|
||||
conflictHandler.call(this, action, conflictOptionals);
|
||||
}
|
||||
};
|
||||
|
||||
ActionContainer.prototype._handleConflictError = function (action, conflOptionals) {
|
||||
var conflicts = conflOptionals.map(function (pair) { return pair[0]; });
|
||||
conflicts = conflicts.join(', ');
|
||||
throw argumentErrorHelper(
|
||||
action,
|
||||
format('Conflicting option string(s): %s', conflicts)
|
||||
);
|
||||
};
|
||||
|
||||
ActionContainer.prototype._handleConflictResolve = function (action, conflOptionals) {
|
||||
// remove all conflicting options
|
||||
var self = this;
|
||||
conflOptionals.forEach(function (pair) {
|
||||
var optionString = pair[0];
|
||||
var conflictingAction = pair[1];
|
||||
// remove the conflicting option string
|
||||
var i = conflictingAction.optionStrings.indexOf(optionString);
|
||||
if (i >= 0) {
|
||||
conflictingAction.optionStrings.splice(i, 1);
|
||||
}
|
||||
delete self._optionStringActions[optionString];
|
||||
// if the option now has no option string, remove it from the
|
||||
// container holding it
|
||||
if (conflictingAction.optionStrings.length === 0) {
|
||||
conflictingAction.container._removeAction(conflictingAction);
|
||||
}
|
||||
});
|
||||
};
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports.ArgumentParser = require('./argument_parser.js');
|
||||
module.exports.Namespace = require('./namespace');
|
||||
module.exports.Action = require('./action');
|
||||
module.exports.HelpFormatter = require('./help/formatter.js');
|
||||
module.exports.Const = require('./const.js');
|
||||
|
||||
module.exports.ArgumentDefaultsHelpFormatter =
|
||||
require('./help/added_formatters.js').ArgumentDefaultsHelpFormatter;
|
||||
module.exports.RawDescriptionHelpFormatter =
|
||||
require('./help/added_formatters.js').RawDescriptionHelpFormatter;
|
||||
module.exports.RawTextHelpFormatter =
|
||||
require('./help/added_formatters.js').RawTextHelpFormatter;
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
var format = require('util').format;
|
||||
|
||||
|
||||
var ERR_CODE = 'ARGError';
|
||||
|
||||
/*:nodoc:*
|
||||
* argumentError(argument, message) -> TypeError
|
||||
* - argument (Object): action with broken argument
|
||||
* - message (String): error message
|
||||
*
|
||||
* Error format helper. An error from creating or using an argument
|
||||
* (optional or positional). The string value of this exception
|
||||
* is the message, augmented with information
|
||||
* about the argument that caused it.
|
||||
*
|
||||
* #####Example
|
||||
*
|
||||
* var argumentErrorHelper = require('./argument/error');
|
||||
* if (conflictOptionals.length > 0) {
|
||||
* throw argumentErrorHelper(
|
||||
* action,
|
||||
* format('Conflicting option string(s): %s', conflictOptionals.join(', '))
|
||||
* );
|
||||
* }
|
||||
*
|
||||
**/
|
||||
module.exports = function (argument, message) {
|
||||
var argumentName = null;
|
||||
var errMessage;
|
||||
var err;
|
||||
|
||||
if (argument.getName) {
|
||||
argumentName = argument.getName();
|
||||
} else {
|
||||
argumentName = '' + argument;
|
||||
}
|
||||
|
||||
if (!argumentName) {
|
||||
errMessage = message;
|
||||
} else {
|
||||
errMessage = format('argument "%s": %s', argumentName, message);
|
||||
}
|
||||
|
||||
err = new TypeError(errMessage);
|
||||
err.code = ERR_CODE;
|
||||
return err;
|
||||
};
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/** internal
|
||||
* class MutuallyExclusiveGroup
|
||||
*
|
||||
* Group arguments.
|
||||
* By default, ArgumentParser groups command-line arguments
|
||||
* into “positional arguments” and “optional arguments”
|
||||
* when displaying help messages. When there is a better
|
||||
* conceptual grouping of arguments than this default one,
|
||||
* appropriate groups can be created using the addArgumentGroup() method
|
||||
*
|
||||
* This class inherited from [[ArgumentContainer]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var ArgumentGroup = require('./group');
|
||||
|
||||
/**
|
||||
* new MutuallyExclusiveGroup(container, options)
|
||||
* - container (object): main container
|
||||
* - options (object): options.required -> true/false
|
||||
*
|
||||
* `required` could be an argument itself, but making it a property of
|
||||
* the options argument is more consistent with the JS adaptation of the Python)
|
||||
**/
|
||||
var MutuallyExclusiveGroup = module.exports = function MutuallyExclusiveGroup(container, options) {
|
||||
var required;
|
||||
options = options || {};
|
||||
required = options.required || false;
|
||||
ArgumentGroup.call(this, container);
|
||||
this.required = required;
|
||||
|
||||
};
|
||||
util.inherits(MutuallyExclusiveGroup, ArgumentGroup);
|
||||
|
||||
|
||||
MutuallyExclusiveGroup.prototype._addAction = function (action) {
|
||||
var msg;
|
||||
if (action.required) {
|
||||
msg = 'mutually exclusive arguments must be optional';
|
||||
throw new Error(msg);
|
||||
}
|
||||
action = this._container._addAction(action);
|
||||
this._groupActions.push(action);
|
||||
return action;
|
||||
};
|
||||
|
||||
|
||||
MutuallyExclusiveGroup.prototype._removeAction = function (action) {
|
||||
this._container._removeAction(action);
|
||||
this._groupActions.remove(action);
|
||||
};
|
||||
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
/** internal
|
||||
* class ArgumentGroup
|
||||
*
|
||||
* Group arguments.
|
||||
* By default, ArgumentParser groups command-line arguments
|
||||
* into “positional arguments” and “optional arguments”
|
||||
* when displaying help messages. When there is a better
|
||||
* conceptual grouping of arguments than this default one,
|
||||
* appropriate groups can be created using the addArgumentGroup() method
|
||||
*
|
||||
* This class inherited from [[ArgumentContainer]]
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var ActionContainer = require('../action_container');
|
||||
|
||||
|
||||
/**
|
||||
* new ArgumentGroup(container, options)
|
||||
* - container (object): main container
|
||||
* - options (object): hash of group options
|
||||
*
|
||||
* #### options
|
||||
* - **prefixChars** group name prefix
|
||||
* - **argumentDefault** default argument value
|
||||
* - **title** group title
|
||||
* - **description** group description
|
||||
*
|
||||
**/
|
||||
var ArgumentGroup = module.exports = function ArgumentGroup(container, options) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
// add any missing keyword arguments by checking the container
|
||||
options.conflictHandler = (options.conflictHandler || container.conflictHandler);
|
||||
options.prefixChars = (options.prefixChars || container.prefixChars);
|
||||
options.argumentDefault = (options.argumentDefault || container.argumentDefault);
|
||||
|
||||
ActionContainer.call(this, options);
|
||||
|
||||
// group attributes
|
||||
this.title = options.title;
|
||||
this._groupActions = [];
|
||||
|
||||
// share most attributes with the container
|
||||
this._container = container;
|
||||
this._registries = container._registries;
|
||||
this._actions = container._actions;
|
||||
this._optionStringActions = container._optionStringActions;
|
||||
this._defaults = container._defaults;
|
||||
this._hasNegativeNumberOptionals = container._hasNegativeNumberOptionals;
|
||||
this._mutuallyExclusiveGroups = container._mutuallyExclusiveGroups;
|
||||
};
|
||||
util.inherits(ArgumentGroup, ActionContainer);
|
||||
|
||||
|
||||
ArgumentGroup.prototype._addAction = function (action) {
|
||||
// Parent add action
|
||||
action = ActionContainer.prototype._addAction.call(this, action);
|
||||
this._groupActions.push(action);
|
||||
return action;
|
||||
};
|
||||
|
||||
|
||||
ArgumentGroup.prototype._removeAction = function (action) {
|
||||
// Parent remove action
|
||||
ActionContainer.prototype._removeAction.call(this, action);
|
||||
var actionIndex = this._groupActions.indexOf(action);
|
||||
if (actionIndex >= 0) {
|
||||
this._groupActions.splice(actionIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
-1161
File diff suppressed because it is too large.
Load diff
-21
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Constants
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports.EOL = '\n';
|
||||
|
||||
module.exports.SUPPRESS = '==SUPPRESS==';
|
||||
|
||||
module.exports.OPTIONAL = '?';
|
||||
|
||||
module.exports.ZERO_OR_MORE = '*';
|
||||
|
||||
module.exports.ONE_OR_MORE = '+';
|
||||
|
||||
module.exports.PARSER = 'A...';
|
||||
|
||||
module.exports.REMAINDER = '...';
|
||||
|
||||
module.exports._UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args';
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
var $$ = require('../utils');
|
||||
var HelpFormatter = require('./formatter.js');
|
||||
|
||||
/**
|
||||
* new RawDescriptionHelpFormatter(options)
|
||||
* new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
|
||||
*
|
||||
* Help message formatter which adds default values to argument help.
|
||||
*
|
||||
* Only the name of this class is considered a public API. All the methods
|
||||
* provided by the class are considered an implementation detail.
|
||||
**/
|
||||
|
||||
function ArgumentDefaultsHelpFormatter(options) {
|
||||
HelpFormatter.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(ArgumentDefaultsHelpFormatter, HelpFormatter);
|
||||
|
||||
ArgumentDefaultsHelpFormatter.prototype._getHelpString = function (action) {
|
||||
var help = action.help;
|
||||
if (action.help.indexOf('%(defaultValue)s') === -1) {
|
||||
if (action.defaultValue !== c.SUPPRESS) {
|
||||
var defaulting_nargs = [ c.OPTIONAL, c.ZERO_OR_MORE ];
|
||||
if (action.isOptional() || (defaulting_nargs.indexOf(action.nargs) >= 0)) {
|
||||
help += ' (default: %(defaultValue)s)';
|
||||
}
|
||||
}
|
||||
}
|
||||
return help;
|
||||
};
|
||||
|
||||
module.exports.ArgumentDefaultsHelpFormatter = ArgumentDefaultsHelpFormatter;
|
||||
|
||||
/**
|
||||
* new RawDescriptionHelpFormatter(options)
|
||||
* new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
|
||||
*
|
||||
* Help message formatter which retains any formatting in descriptions.
|
||||
*
|
||||
* Only the name of this class is considered a public API. All the methods
|
||||
* provided by the class are considered an implementation detail.
|
||||
**/
|
||||
|
||||
function RawDescriptionHelpFormatter(options) {
|
||||
HelpFormatter.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(RawDescriptionHelpFormatter, HelpFormatter);
|
||||
|
||||
RawDescriptionHelpFormatter.prototype._fillText = function (text, width, indent) {
|
||||
var lines = text.split('\n');
|
||||
lines = lines.map(function (line) {
|
||||
return $$.trimEnd(indent + line);
|
||||
});
|
||||
return lines.join('\n');
|
||||
};
|
||||
module.exports.RawDescriptionHelpFormatter = RawDescriptionHelpFormatter;
|
||||
|
||||
/**
|
||||
* new RawTextHelpFormatter(options)
|
||||
* new ArgumentParser({formatterClass: argparse.RawTextHelpFormatter, ...})
|
||||
*
|
||||
* Help message formatter which retains formatting of all help text.
|
||||
*
|
||||
* Only the name of this class is considered a public API. All the methods
|
||||
* provided by the class are considered an implementation detail.
|
||||
**/
|
||||
|
||||
function RawTextHelpFormatter(options) {
|
||||
RawDescriptionHelpFormatter.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(RawTextHelpFormatter, RawDescriptionHelpFormatter);
|
||||
|
||||
RawTextHelpFormatter.prototype._splitLines = function (text) {
|
||||
return text.split('\n');
|
||||
};
|
||||
|
||||
module.exports.RawTextHelpFormatter = RawTextHelpFormatter;
|
||||
-795
@@ -1,795 +0,0 @@
|
||||
/**
|
||||
* class HelpFormatter
|
||||
*
|
||||
* Formatter for generating usage messages and argument help strings. Only the
|
||||
* name of this class is considered a public API. All the methods provided by
|
||||
* the class are considered an implementation detail.
|
||||
*
|
||||
* Do not call in your code, use this class only for inherits your own forvatter
|
||||
*
|
||||
* ToDo add [additonal formatters][1]
|
||||
*
|
||||
* [1]:http://docs.python.org/dev/library/argparse.html#formatter-class
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var sprintf = require('sprintf-js').sprintf;
|
||||
|
||||
// Constants
|
||||
var c = require('../const');
|
||||
|
||||
var $$ = require('../utils');
|
||||
|
||||
|
||||
/*:nodoc:* internal
|
||||
* new Support(parent, heding)
|
||||
* - parent (object): parent section
|
||||
* - heading (string): header string
|
||||
*
|
||||
**/
|
||||
function Section(parent, heading) {
|
||||
this._parent = parent;
|
||||
this._heading = heading;
|
||||
this._items = [];
|
||||
}
|
||||
|
||||
/*:nodoc:* internal
|
||||
* Section#addItem(callback) -> Void
|
||||
* - callback (array): tuple with function and args
|
||||
*
|
||||
* Add function for single element
|
||||
**/
|
||||
Section.prototype.addItem = function (callback) {
|
||||
this._items.push(callback);
|
||||
};
|
||||
|
||||
/*:nodoc:* internal
|
||||
* Section#formatHelp(formatter) -> string
|
||||
* - formatter (HelpFormatter): current formatter
|
||||
*
|
||||
* Form help section string
|
||||
*
|
||||
**/
|
||||
Section.prototype.formatHelp = function (formatter) {
|
||||
var itemHelp, heading;
|
||||
|
||||
// format the indented section
|
||||
if (this._parent) {
|
||||
formatter._indent();
|
||||
}
|
||||
|
||||
itemHelp = this._items.map(function (item) {
|
||||
var obj, func, args;
|
||||
|
||||
obj = formatter;
|
||||
func = item[0];
|
||||
args = item[1];
|
||||
return func.apply(obj, args);
|
||||
});
|
||||
itemHelp = formatter._joinParts(itemHelp);
|
||||
|
||||
if (this._parent) {
|
||||
formatter._dedent();
|
||||
}
|
||||
|
||||
// return nothing if the section was empty
|
||||
if (!itemHelp) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// add the heading if the section was non-empty
|
||||
heading = '';
|
||||
if (this._heading && this._heading !== c.SUPPRESS) {
|
||||
var currentIndent = formatter.currentIndent;
|
||||
heading = $$.repeat(' ', currentIndent) + this._heading + ':' + c.EOL;
|
||||
}
|
||||
|
||||
// join the section-initialize newline, the heading and the help
|
||||
return formatter._joinParts([ c.EOL, heading, itemHelp, c.EOL ]);
|
||||
};
|
||||
|
||||
/**
|
||||
* new HelpFormatter(options)
|
||||
*
|
||||
* #### Options:
|
||||
* - `prog`: program name
|
||||
* - `indentIncriment`: indent step, default value 2
|
||||
* - `maxHelpPosition`: max help position, default value = 24
|
||||
* - `width`: line width
|
||||
*
|
||||
**/
|
||||
var HelpFormatter = module.exports = function HelpFormatter(options) {
|
||||
options = options || {};
|
||||
|
||||
this._prog = options.prog;
|
||||
|
||||
this._maxHelpPosition = options.maxHelpPosition || 24;
|
||||
this._width = (options.width || ((process.env.COLUMNS || 80) - 2));
|
||||
|
||||
this._currentIndent = 0;
|
||||
this._indentIncriment = options.indentIncriment || 2;
|
||||
this._level = 0;
|
||||
this._actionMaxLength = 0;
|
||||
|
||||
this._rootSection = new Section(null);
|
||||
this._currentSection = this._rootSection;
|
||||
|
||||
this._whitespaceMatcher = new RegExp('\\s+', 'g');
|
||||
this._longBreakMatcher = new RegExp(c.EOL + c.EOL + c.EOL + '+', 'g');
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._indent = function () {
|
||||
this._currentIndent += this._indentIncriment;
|
||||
this._level += 1;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._dedent = function () {
|
||||
this._currentIndent -= this._indentIncriment;
|
||||
this._level -= 1;
|
||||
if (this._currentIndent < 0) {
|
||||
throw new Error('Indent decreased below 0.');
|
||||
}
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._addItem = function (func, args) {
|
||||
this._currentSection.addItem([ func, args ]);
|
||||
};
|
||||
|
||||
//
|
||||
// Message building methods
|
||||
//
|
||||
|
||||
/**
|
||||
* HelpFormatter#startSection(heading) -> Void
|
||||
* - heading (string): header string
|
||||
*
|
||||
* Start new help section
|
||||
*
|
||||
* See alse [code example][1]
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.startSection(actionGroup.title);
|
||||
* formatter.addText(actionGroup.description);
|
||||
* formatter.addArguments(actionGroup._groupActions);
|
||||
* formatter.endSection();
|
||||
*
|
||||
**/
|
||||
HelpFormatter.prototype.startSection = function (heading) {
|
||||
this._indent();
|
||||
var section = new Section(this._currentSection, heading);
|
||||
var func = section.formatHelp.bind(section);
|
||||
this._addItem(func, [ this ]);
|
||||
this._currentSection = section;
|
||||
};
|
||||
|
||||
/**
|
||||
* HelpFormatter#endSection -> Void
|
||||
*
|
||||
* End help section
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.startSection(actionGroup.title);
|
||||
* formatter.addText(actionGroup.description);
|
||||
* formatter.addArguments(actionGroup._groupActions);
|
||||
* formatter.endSection();
|
||||
**/
|
||||
HelpFormatter.prototype.endSection = function () {
|
||||
this._currentSection = this._currentSection._parent;
|
||||
this._dedent();
|
||||
};
|
||||
|
||||
/**
|
||||
* HelpFormatter#addText(text) -> Void
|
||||
* - text (string): plain text
|
||||
*
|
||||
* Add plain text into current section
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.startSection(actionGroup.title);
|
||||
* formatter.addText(actionGroup.description);
|
||||
* formatter.addArguments(actionGroup._groupActions);
|
||||
* formatter.endSection();
|
||||
*
|
||||
**/
|
||||
HelpFormatter.prototype.addText = function (text) {
|
||||
if (text && text !== c.SUPPRESS) {
|
||||
this._addItem(this._formatText, [ text ]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* HelpFormatter#addUsage(usage, actions, groups, prefix) -> Void
|
||||
* - usage (string): usage text
|
||||
* - actions (array): actions list
|
||||
* - groups (array): groups list
|
||||
* - prefix (string): usage prefix
|
||||
*
|
||||
* Add usage data into current section
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.addUsage(this.usage, this._actions, []);
|
||||
* return formatter.formatHelp();
|
||||
*
|
||||
**/
|
||||
HelpFormatter.prototype.addUsage = function (usage, actions, groups, prefix) {
|
||||
if (usage !== c.SUPPRESS) {
|
||||
this._addItem(this._formatUsage, [ usage, actions, groups, prefix ]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* HelpFormatter#addArgument(action) -> Void
|
||||
* - action (object): action
|
||||
*
|
||||
* Add argument into current section
|
||||
*
|
||||
* Single variant of [[HelpFormatter#addArguments]]
|
||||
**/
|
||||
HelpFormatter.prototype.addArgument = function (action) {
|
||||
if (action.help !== c.SUPPRESS) {
|
||||
var self = this;
|
||||
|
||||
// find all invocations
|
||||
var invocations = [ this._formatActionInvocation(action) ];
|
||||
var invocationLength = invocations[0].length;
|
||||
|
||||
var actionLength;
|
||||
|
||||
if (action._getSubactions) {
|
||||
this._indent();
|
||||
action._getSubactions().forEach(function (subaction) {
|
||||
|
||||
var invocationNew = self._formatActionInvocation(subaction);
|
||||
invocations.push(invocationNew);
|
||||
invocationLength = Math.max(invocationLength, invocationNew.length);
|
||||
|
||||
});
|
||||
this._dedent();
|
||||
}
|
||||
|
||||
// update the maximum item length
|
||||
actionLength = invocationLength + this._currentIndent;
|
||||
this._actionMaxLength = Math.max(this._actionMaxLength, actionLength);
|
||||
|
||||
// add the item to the list
|
||||
this._addItem(this._formatAction, [ action ]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* HelpFormatter#addArguments(actions) -> Void
|
||||
* - actions (array): actions list
|
||||
*
|
||||
* Mass add arguments into current section
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.startSection(actionGroup.title);
|
||||
* formatter.addText(actionGroup.description);
|
||||
* formatter.addArguments(actionGroup._groupActions);
|
||||
* formatter.endSection();
|
||||
*
|
||||
**/
|
||||
HelpFormatter.prototype.addArguments = function (actions) {
|
||||
var self = this;
|
||||
actions.forEach(function (action) {
|
||||
self.addArgument(action);
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Help-formatting methods
|
||||
//
|
||||
|
||||
/**
|
||||
* HelpFormatter#formatHelp -> string
|
||||
*
|
||||
* Format help
|
||||
*
|
||||
* ##### Example
|
||||
*
|
||||
* formatter.addText(this.epilog);
|
||||
* return formatter.formatHelp();
|
||||
*
|
||||
**/
|
||||
HelpFormatter.prototype.formatHelp = function () {
|
||||
var help = this._rootSection.formatHelp(this);
|
||||
if (help) {
|
||||
help = help.replace(this._longBreakMatcher, c.EOL + c.EOL);
|
||||
help = $$.trimChars(help, c.EOL) + c.EOL;
|
||||
}
|
||||
return help;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._joinParts = function (partStrings) {
|
||||
return partStrings.filter(function (part) {
|
||||
return (part && part !== c.SUPPRESS);
|
||||
}).join('');
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatUsage = function (usage, actions, groups, prefix) {
|
||||
if (!prefix && typeof prefix !== 'string') {
|
||||
prefix = 'usage: ';
|
||||
}
|
||||
|
||||
actions = actions || [];
|
||||
groups = groups || [];
|
||||
|
||||
|
||||
// if usage is specified, use that
|
||||
if (usage) {
|
||||
usage = sprintf(usage, { prog: this._prog });
|
||||
|
||||
// if no optionals or positionals are available, usage is just prog
|
||||
} else if (!usage && actions.length === 0) {
|
||||
usage = this._prog;
|
||||
|
||||
// if optionals and positionals are available, calculate usage
|
||||
} else if (!usage) {
|
||||
var prog = this._prog;
|
||||
var optionals = [];
|
||||
var positionals = [];
|
||||
var actionUsage;
|
||||
var textWidth;
|
||||
|
||||
// split optionals from positionals
|
||||
actions.forEach(function (action) {
|
||||
if (action.isOptional()) {
|
||||
optionals.push(action);
|
||||
} else {
|
||||
positionals.push(action);
|
||||
}
|
||||
});
|
||||
|
||||
// build full usage string
|
||||
actionUsage = this._formatActionsUsage([].concat(optionals, positionals), groups);
|
||||
usage = [ prog, actionUsage ].join(' ');
|
||||
|
||||
// wrap the usage parts if it's too long
|
||||
textWidth = this._width - this._currentIndent;
|
||||
if ((prefix.length + usage.length) > textWidth) {
|
||||
|
||||
// break usage into wrappable parts
|
||||
var regexpPart = new RegExp('\\(.*?\\)+|\\[.*?\\]+|\\S+', 'g');
|
||||
var optionalUsage = this._formatActionsUsage(optionals, groups);
|
||||
var positionalUsage = this._formatActionsUsage(positionals, groups);
|
||||
|
||||
|
||||
var optionalParts = optionalUsage.match(regexpPart);
|
||||
var positionalParts = positionalUsage.match(regexpPart) || [];
|
||||
|
||||
if (optionalParts.join(' ') !== optionalUsage) {
|
||||
throw new Error('assert "optionalParts.join(\' \') === optionalUsage"');
|
||||
}
|
||||
if (positionalParts.join(' ') !== positionalUsage) {
|
||||
throw new Error('assert "positionalParts.join(\' \') === positionalUsage"');
|
||||
}
|
||||
|
||||
// helper for wrapping lines
|
||||
/*eslint-disable func-style*/ // node 0.10 compat
|
||||
var _getLines = function (parts, indent, prefix) {
|
||||
var lines = [];
|
||||
var line = [];
|
||||
|
||||
var lineLength = prefix ? prefix.length - 1 : indent.length - 1;
|
||||
|
||||
parts.forEach(function (part) {
|
||||
if (lineLength + 1 + part.length > textWidth) {
|
||||
lines.push(indent + line.join(' '));
|
||||
line = [];
|
||||
lineLength = indent.length - 1;
|
||||
}
|
||||
line.push(part);
|
||||
lineLength += part.length + 1;
|
||||
});
|
||||
|
||||
if (line) {
|
||||
lines.push(indent + line.join(' '));
|
||||
}
|
||||
if (prefix) {
|
||||
lines[0] = lines[0].substr(indent.length);
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
|
||||
var lines, indent, parts;
|
||||
// if prog is short, follow it with optionals or positionals
|
||||
if (prefix.length + prog.length <= 0.75 * textWidth) {
|
||||
indent = $$.repeat(' ', (prefix.length + prog.length + 1));
|
||||
if (optionalParts) {
|
||||
lines = [].concat(
|
||||
_getLines([ prog ].concat(optionalParts), indent, prefix),
|
||||
_getLines(positionalParts, indent)
|
||||
);
|
||||
} else if (positionalParts) {
|
||||
lines = _getLines([ prog ].concat(positionalParts), indent, prefix);
|
||||
} else {
|
||||
lines = [ prog ];
|
||||
}
|
||||
|
||||
// if prog is long, put it on its own line
|
||||
} else {
|
||||
indent = $$.repeat(' ', prefix.length);
|
||||
parts = optionalParts.concat(positionalParts);
|
||||
lines = _getLines(parts, indent);
|
||||
if (lines.length > 1) {
|
||||
lines = [].concat(
|
||||
_getLines(optionalParts, indent),
|
||||
_getLines(positionalParts, indent)
|
||||
);
|
||||
}
|
||||
lines = [ prog ].concat(lines);
|
||||
}
|
||||
// join lines into usage
|
||||
usage = lines.join(c.EOL);
|
||||
}
|
||||
}
|
||||
|
||||
// prefix with 'usage:'
|
||||
return prefix + usage + c.EOL + c.EOL;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {
|
||||
// find group indices and identify actions in groups
|
||||
var groupActions = [];
|
||||
var inserts = [];
|
||||
var self = this;
|
||||
|
||||
groups.forEach(function (group) {
|
||||
var end;
|
||||
var i;
|
||||
|
||||
var start = actions.indexOf(group._groupActions[0]);
|
||||
if (start >= 0) {
|
||||
end = start + group._groupActions.length;
|
||||
|
||||
//if (actions.slice(start, end) === group._groupActions) {
|
||||
if ($$.arrayEqual(actions.slice(start, end), group._groupActions)) {
|
||||
group._groupActions.forEach(function (action) {
|
||||
groupActions.push(action);
|
||||
});
|
||||
|
||||
if (!group.required) {
|
||||
if (inserts[start]) {
|
||||
inserts[start] += ' [';
|
||||
} else {
|
||||
inserts[start] = '[';
|
||||
}
|
||||
inserts[end] = ']';
|
||||
} else {
|
||||
if (inserts[start]) {
|
||||
inserts[start] += ' (';
|
||||
} else {
|
||||
inserts[start] = '(';
|
||||
}
|
||||
inserts[end] = ')';
|
||||
}
|
||||
for (i = start + 1; i < end; i += 1) {
|
||||
inserts[i] = '|';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// collect all actions format strings
|
||||
var parts = [];
|
||||
|
||||
actions.forEach(function (action, actionIndex) {
|
||||
var part;
|
||||
var optionString;
|
||||
var argsDefault;
|
||||
var argsString;
|
||||
|
||||
// suppressed arguments are marked with None
|
||||
// remove | separators for suppressed arguments
|
||||
if (action.help === c.SUPPRESS) {
|
||||
parts.push(null);
|
||||
if (inserts[actionIndex] === '|') {
|
||||
inserts.splice(actionIndex, actionIndex);
|
||||
} else if (inserts[actionIndex + 1] === '|') {
|
||||
inserts.splice(actionIndex + 1, actionIndex + 1);
|
||||
}
|
||||
|
||||
// produce all arg strings
|
||||
} else if (!action.isOptional()) {
|
||||
part = self._formatArgs(action, action.dest);
|
||||
|
||||
// if it's in a group, strip the outer []
|
||||
if (groupActions.indexOf(action) >= 0) {
|
||||
if (part[0] === '[' && part[part.length - 1] === ']') {
|
||||
part = part.slice(1, -1);
|
||||
}
|
||||
}
|
||||
// add the action string to the list
|
||||
parts.push(part);
|
||||
|
||||
// produce the first way to invoke the option in brackets
|
||||
} else {
|
||||
optionString = action.optionStrings[0];
|
||||
|
||||
// if the Optional doesn't take a value, format is: -s or --long
|
||||
if (action.nargs === 0) {
|
||||
part = '' + optionString;
|
||||
|
||||
// if the Optional takes a value, format is: -s ARGS or --long ARGS
|
||||
} else {
|
||||
argsDefault = action.dest.toUpperCase();
|
||||
argsString = self._formatArgs(action, argsDefault);
|
||||
part = optionString + ' ' + argsString;
|
||||
}
|
||||
// make it look optional if it's not required or in a group
|
||||
if (!action.required && groupActions.indexOf(action) < 0) {
|
||||
part = '[' + part + ']';
|
||||
}
|
||||
// add the action string to the list
|
||||
parts.push(part);
|
||||
}
|
||||
});
|
||||
|
||||
// insert things at the necessary indices
|
||||
for (var i = inserts.length - 1; i >= 0; --i) {
|
||||
if (inserts[i] !== null) {
|
||||
parts.splice(i, 0, inserts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// join all the action items with spaces
|
||||
var text = parts.filter(function (part) {
|
||||
return !!part;
|
||||
}).join(' ');
|
||||
|
||||
// clean up separators for mutually exclusive groups
|
||||
text = text.replace(/([\[(]) /g, '$1'); // remove spaces
|
||||
text = text.replace(/ ([\])])/g, '$1');
|
||||
text = text.replace(/\[ *\]/g, ''); // remove empty groups
|
||||
text = text.replace(/\( *\)/g, '');
|
||||
text = text.replace(/\(([^|]*)\)/g, '$1'); // remove () from single action groups
|
||||
|
||||
text = text.trim();
|
||||
|
||||
// return the text
|
||||
return text;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatText = function (text) {
|
||||
text = sprintf(text, { prog: this._prog });
|
||||
var textWidth = this._width - this._currentIndent;
|
||||
var indentIncriment = $$.repeat(' ', this._currentIndent);
|
||||
return this._fillText(text, textWidth, indentIncriment) + c.EOL + c.EOL;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatAction = function (action) {
|
||||
var self = this;
|
||||
|
||||
var helpText;
|
||||
var helpLines;
|
||||
var parts;
|
||||
var indentFirst;
|
||||
|
||||
// determine the required width and the entry label
|
||||
var helpPosition = Math.min(this._actionMaxLength + 2, this._maxHelpPosition);
|
||||
var helpWidth = this._width - helpPosition;
|
||||
var actionWidth = helpPosition - this._currentIndent - 2;
|
||||
var actionHeader = this._formatActionInvocation(action);
|
||||
|
||||
// no help; start on same line and add a final newline
|
||||
if (!action.help) {
|
||||
actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
|
||||
|
||||
// short action name; start on the same line and pad two spaces
|
||||
} else if (actionHeader.length <= actionWidth) {
|
||||
actionHeader = $$.repeat(' ', this._currentIndent) +
|
||||
actionHeader +
|
||||
' ' +
|
||||
$$.repeat(' ', actionWidth - actionHeader.length);
|
||||
indentFirst = 0;
|
||||
|
||||
// long action name; start on the next line
|
||||
} else {
|
||||
actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
|
||||
indentFirst = helpPosition;
|
||||
}
|
||||
|
||||
// collect the pieces of the action help
|
||||
parts = [ actionHeader ];
|
||||
|
||||
// if there was help for the action, add lines of help text
|
||||
if (action.help) {
|
||||
helpText = this._expandHelp(action);
|
||||
helpLines = this._splitLines(helpText, helpWidth);
|
||||
parts.push($$.repeat(' ', indentFirst) + helpLines[0] + c.EOL);
|
||||
helpLines.slice(1).forEach(function (line) {
|
||||
parts.push($$.repeat(' ', helpPosition) + line + c.EOL);
|
||||
});
|
||||
|
||||
// or add a newline if the description doesn't end with one
|
||||
} else if (actionHeader.charAt(actionHeader.length - 1) !== c.EOL) {
|
||||
parts.push(c.EOL);
|
||||
}
|
||||
// if there are any sub-actions, add their help as well
|
||||
if (action._getSubactions) {
|
||||
this._indent();
|
||||
action._getSubactions().forEach(function (subaction) {
|
||||
parts.push(self._formatAction(subaction));
|
||||
});
|
||||
this._dedent();
|
||||
}
|
||||
// return a single string
|
||||
return this._joinParts(parts);
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatActionInvocation = function (action) {
|
||||
if (!action.isOptional()) {
|
||||
var format_func = this._metavarFormatter(action, action.dest);
|
||||
var metavars = format_func(1);
|
||||
return metavars[0];
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
var argsDefault;
|
||||
var argsString;
|
||||
|
||||
// if the Optional doesn't take a value, format is: -s, --long
|
||||
if (action.nargs === 0) {
|
||||
parts = parts.concat(action.optionStrings);
|
||||
|
||||
// if the Optional takes a value, format is: -s ARGS, --long ARGS
|
||||
} else {
|
||||
argsDefault = action.dest.toUpperCase();
|
||||
argsString = this._formatArgs(action, argsDefault);
|
||||
action.optionStrings.forEach(function (optionString) {
|
||||
parts.push(optionString + ' ' + argsString);
|
||||
});
|
||||
}
|
||||
return parts.join(', ');
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._metavarFormatter = function (action, metavarDefault) {
|
||||
var result;
|
||||
|
||||
if (action.metavar || action.metavar === '') {
|
||||
result = action.metavar;
|
||||
} else if (action.choices) {
|
||||
var choices = action.choices;
|
||||
|
||||
if (typeof choices === 'string') {
|
||||
choices = choices.split('').join(', ');
|
||||
} else if (Array.isArray(choices)) {
|
||||
choices = choices.join(',');
|
||||
} else {
|
||||
choices = Object.keys(choices).join(',');
|
||||
}
|
||||
result = '{' + choices + '}';
|
||||
} else {
|
||||
result = metavarDefault;
|
||||
}
|
||||
|
||||
return function (size) {
|
||||
if (Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
var metavars = [];
|
||||
for (var i = 0; i < size; i += 1) {
|
||||
metavars.push(result);
|
||||
}
|
||||
return metavars;
|
||||
};
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._formatArgs = function (action, metavarDefault) {
|
||||
var result;
|
||||
var metavars;
|
||||
|
||||
var buildMetavar = this._metavarFormatter(action, metavarDefault);
|
||||
|
||||
switch (action.nargs) {
|
||||
/*eslint-disable no-undefined*/
|
||||
case undefined:
|
||||
case null:
|
||||
metavars = buildMetavar(1);
|
||||
result = '' + metavars[0];
|
||||
break;
|
||||
case c.OPTIONAL:
|
||||
metavars = buildMetavar(1);
|
||||
result = '[' + metavars[0] + ']';
|
||||
break;
|
||||
case c.ZERO_OR_MORE:
|
||||
metavars = buildMetavar(2);
|
||||
result = '[' + metavars[0] + ' [' + metavars[1] + ' ...]]';
|
||||
break;
|
||||
case c.ONE_OR_MORE:
|
||||
metavars = buildMetavar(2);
|
||||
result = '' + metavars[0] + ' [' + metavars[1] + ' ...]';
|
||||
break;
|
||||
case c.REMAINDER:
|
||||
result = '...';
|
||||
break;
|
||||
case c.PARSER:
|
||||
metavars = buildMetavar(1);
|
||||
result = metavars[0] + ' ...';
|
||||
break;
|
||||
default:
|
||||
metavars = buildMetavar(action.nargs);
|
||||
result = metavars.join(' ');
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._expandHelp = function (action) {
|
||||
var params = { prog: this._prog };
|
||||
|
||||
Object.keys(action).forEach(function (actionProperty) {
|
||||
var actionValue = action[actionProperty];
|
||||
|
||||
if (actionValue !== c.SUPPRESS) {
|
||||
params[actionProperty] = actionValue;
|
||||
}
|
||||
});
|
||||
|
||||
if (params.choices) {
|
||||
if (typeof params.choices === 'string') {
|
||||
params.choices = params.choices.split('').join(', ');
|
||||
} else if (Array.isArray(params.choices)) {
|
||||
params.choices = params.choices.join(', ');
|
||||
} else {
|
||||
params.choices = Object.keys(params.choices).join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(this._getHelpString(action), params);
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._splitLines = function (text, width) {
|
||||
var lines = [];
|
||||
var delimiters = [ ' ', '.', ',', '!', '?' ];
|
||||
var re = new RegExp('[' + delimiters.join('') + '][^' + delimiters.join('') + ']*$');
|
||||
|
||||
text = text.replace(/[\n\|\t]/g, ' ');
|
||||
|
||||
text = text.trim();
|
||||
text = text.replace(this._whitespaceMatcher, ' ');
|
||||
|
||||
// Wraps the single paragraph in text (a string) so every line
|
||||
// is at most width characters long.
|
||||
text.split(c.EOL).forEach(function (line) {
|
||||
if (width >= line.length) {
|
||||
lines.push(line);
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapStart = 0;
|
||||
var wrapEnd = width;
|
||||
var delimiterIndex = 0;
|
||||
while (wrapEnd <= line.length) {
|
||||
if (wrapEnd !== line.length && delimiters.indexOf(line[wrapEnd] < -1)) {
|
||||
delimiterIndex = (re.exec(line.substring(wrapStart, wrapEnd)) || {}).index;
|
||||
wrapEnd = wrapStart + delimiterIndex + 1;
|
||||
}
|
||||
lines.push(line.substring(wrapStart, wrapEnd));
|
||||
wrapStart = wrapEnd;
|
||||
wrapEnd += width;
|
||||
}
|
||||
if (wrapStart < line.length) {
|
||||
lines.push(line.substring(wrapStart, wrapEnd));
|
||||
}
|
||||
});
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._fillText = function (text, width, indent) {
|
||||
var lines = this._splitLines(text, width);
|
||||
lines = lines.map(function (line) {
|
||||
return indent + line;
|
||||
});
|
||||
return lines.join(c.EOL);
|
||||
};
|
||||
|
||||
HelpFormatter.prototype._getHelpString = function (action) {
|
||||
return action.help;
|
||||
};
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* class Namespace
|
||||
*
|
||||
* Simple object for storing attributes. Implements equality by attribute names
|
||||
* and values, and provides a simple string representation.
|
||||
*
|
||||
* See also [original guide][1]
|
||||
*
|
||||
* [1]:http://docs.python.org/dev/library/argparse.html#the-namespace-object
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var $$ = require('./utils');
|
||||
|
||||
/**
|
||||
* new Namespace(options)
|
||||
* - options(object): predefined propertis for result object
|
||||
*
|
||||
**/
|
||||
var Namespace = module.exports = function Namespace(options) {
|
||||
$$.extend(this, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Namespace#isset(key) -> Boolean
|
||||
* - key (string|number): property name
|
||||
*
|
||||
* Tells whenever `namespace` contains given `key` or not.
|
||||
**/
|
||||
Namespace.prototype.isset = function (key) {
|
||||
return $$.has(this, key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Namespace#set(key, value) -> self
|
||||
* -key (string|number|object): propery name
|
||||
* -value (mixed): new property value
|
||||
*
|
||||
* Set the property named key with value.
|
||||
* If key object then set all key properties to namespace object
|
||||
**/
|
||||
Namespace.prototype.set = function (key, value) {
|
||||
if (typeof (key) === 'object') {
|
||||
$$.extend(this, key);
|
||||
} else {
|
||||
this[key] = value;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Namespace#get(key, defaultValue) -> mixed
|
||||
* - key (string|number): property name
|
||||
* - defaultValue (mixed): default value
|
||||
*
|
||||
* Return the property key or defaulValue if not set
|
||||
**/
|
||||
Namespace.prototype.get = function (key, defaultValue) {
|
||||
return !this[key] ? defaultValue : this[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Namespace#unset(key, defaultValue) -> mixed
|
||||
* - key (string|number): property name
|
||||
* - defaultValue (mixed): default value
|
||||
*
|
||||
* Return data[key](and delete it) or defaultValue
|
||||
**/
|
||||
Namespace.prototype.unset = function (key, defaultValue) {
|
||||
var value = this[key];
|
||||
if (value !== null) {
|
||||
delete this[key];
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.repeat = function (str, num) {
|
||||
var result = '';
|
||||
for (var i = 0; i < num; i++) { result += str; }
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.arrayEqual = function (a, b) {
|
||||
if (a.length !== b.length) { return false; }
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) { return false; }
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
exports.trimChars = function (str, chars) {
|
||||
var start = 0;
|
||||
var end = str.length - 1;
|
||||
while (chars.indexOf(str.charAt(start)) >= 0) { start++; }
|
||||
while (chars.indexOf(str.charAt(end)) >= 0) { end--; }
|
||||
return str.slice(start, end + 1);
|
||||
};
|
||||
|
||||
exports.capitalize = function (str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
};
|
||||
|
||||
exports.arrayUnion = function () {
|
||||
var result = [];
|
||||
for (var i = 0, values = {}; i < arguments.length; i++) {
|
||||
var arr = arguments[i];
|
||||
for (var j = 0; j < arr.length; j++) {
|
||||
if (!values[arr[j]]) {
|
||||
values[arr[j]] = true;
|
||||
result.push(arr[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
function has(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
exports.has = has;
|
||||
|
||||
exports.extend = function (dest, src) {
|
||||
for (var i in src) {
|
||||
if (has(src, i)) { dest[i] = src[i]; }
|
||||
}
|
||||
};
|
||||
|
||||
exports.trimEnd = function (str) {
|
||||
return str.replace(/\s+$/g, '');
|
||||
};
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"argparse@1.0.10",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "argparse@1.0.10",
|
||||
"_id": "argparse@1.0.10",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"_location": "/argparse",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "argparse@1.0.10",
|
||||
"name": "argparse",
|
||||
"escapedName": "argparse",
|
||||
"rawSpec": "1.0.10",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.0.10"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/js-yaml"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"_spec": "1.0.10",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nodeca/argparse/issues"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Eugene Shkuropat"
|
||||
},
|
||||
{
|
||||
"name": "Paul Jacobson"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
},
|
||||
"description": "Very powerful CLI arguments parser. Native port of argparse - python's options parsing library",
|
||||
"devDependencies": {
|
||||
"eslint": "^2.13.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.1.0",
|
||||
"ndoc": "^5.0.1"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib/"
|
||||
],
|
||||
"homepage": "https://github.com/nodeca/argparse#readme",
|
||||
"keywords": [
|
||||
"cli",
|
||||
"parser",
|
||||
"argparse",
|
||||
"option",
|
||||
"args"
|
||||
],
|
||||
"license": "MIT",
|
||||
"name": "argparse",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nodeca/argparse.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"version": "1.0.10"
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/*!
|
||||
* assertion-error
|
||||
* Copyright(c) 2013 Jake Luer <jake@qualiancy.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Return a function that will copy properties from
|
||||
* one object to another excluding any originally
|
||||
* listed. Returned function will create a new `{}`.
|
||||
*
|
||||
* @param {String} excluded properties ...
|
||||
* @return {Function}
|
||||
*/
|
||||
|
||||
function exclude () {
|
||||
var excludes = [].slice.call(arguments);
|
||||
|
||||
function excludeProps (res, obj) {
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
if (!~excludes.indexOf(key)) res[key] = obj[key];
|
||||
});
|
||||
}
|
||||
|
||||
return function extendExclude () {
|
||||
var args = [].slice.call(arguments)
|
||||
, i = 0
|
||||
, res = {};
|
||||
|
||||
for (; i < args.length; i++) {
|
||||
excludeProps(res, args[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
};
|
||||
|
||||
/*!
|
||||
* Primary Exports
|
||||
*/
|
||||
|
||||
module.exports = AssertionError;
|
||||
|
||||
/**
|
||||
* ### AssertionError
|
||||
*
|
||||
* An extension of the JavaScript `Error` constructor for
|
||||
* assertion and validation scenarios.
|
||||
*
|
||||
* @param {String} message
|
||||
* @param {Object} properties to include (optional)
|
||||
* @param {callee} start stack function (optional)
|
||||
*/
|
||||
|
||||
function AssertionError (message, _props, ssf) {
|
||||
var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON')
|
||||
, props = extend(_props || {});
|
||||
|
||||
// default values
|
||||
this.message = message || 'Unspecified AssertionError';
|
||||
this.showDiff = false;
|
||||
|
||||
// copy from properties
|
||||
for (var key in props) {
|
||||
this[key] = props[key];
|
||||
}
|
||||
|
||||
// capture stack trace
|
||||
ssf = ssf || AssertionError;
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, ssf);
|
||||
} else {
|
||||
try {
|
||||
throw new Error();
|
||||
} catch(e) {
|
||||
this.stack = e.stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Inherit from Error.prototype
|
||||
*/
|
||||
|
||||
AssertionError.prototype = Object.create(Error.prototype);
|
||||
|
||||
/*!
|
||||
* Statically set name
|
||||
*/
|
||||
|
||||
AssertionError.prototype.name = 'AssertionError';
|
||||
|
||||
/*!
|
||||
* Ensure correct constructor
|
||||
*/
|
||||
|
||||
AssertionError.prototype.constructor = AssertionError;
|
||||
|
||||
/**
|
||||
* Allow errors to be converted to JSON for static transfer.
|
||||
*
|
||||
* @param {Boolean} include stack (default: `true`)
|
||||
* @return {Object} object that can be `JSON.stringify`
|
||||
*/
|
||||
|
||||
AssertionError.prototype.toJSON = function (stack) {
|
||||
var extend = exclude('constructor', 'toJSON', 'stack')
|
||||
, props = extend({ name: this.name }, this);
|
||||
|
||||
// include stack if exists and not turned off
|
||||
if (false !== stack && this.stack) {
|
||||
props.stack = this.stack;
|
||||
}
|
||||
|
||||
return props;
|
||||
};
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"assertion-error@1.1.0",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "assertion-error@1.1.0",
|
||||
"_id": "assertion-error@1.1.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||
"_location": "/assertion-error",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "assertion-error@1.1.0",
|
||||
"name": "assertion-error",
|
||||
"escapedName": "assertion-error",
|
||||
"rawSpec": "1.1.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.1.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/chai"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
"_spec": "1.1.0",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Jake Luer",
|
||||
"email": "jake@qualiancy.com",
|
||||
"url": "http://qualiancy.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/chaijs/assertion-error/issues"
|
||||
},
|
||||
"dependencies": {},
|
||||
"description": "Error constructor for test and validation frameworks that implements standardized AssertionError specification.",
|
||||
"devDependencies": {
|
||||
"component": "*",
|
||||
"typescript": "^2.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"homepage": "https://github.com/chaijs/assertion-error#readme",
|
||||
"keywords": [
|
||||
"test",
|
||||
"assertion",
|
||||
"assertion-error"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./index",
|
||||
"name": "assertion-error",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/chaijs/assertion-error.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"types": "./index.d.ts",
|
||||
"version": "1.1.0"
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
var WritableStream = require('stream').Writable
|
||||
var inherits = require('util').inherits
|
||||
|
||||
module.exports = BrowserStdout
|
||||
|
||||
|
||||
inherits(BrowserStdout, WritableStream)
|
||||
|
||||
function BrowserStdout(opts) {
|
||||
if (!(this instanceof BrowserStdout)) return new BrowserStdout(opts)
|
||||
|
||||
opts = opts || {}
|
||||
WritableStream.call(this, opts)
|
||||
this.label = (opts.label !== undefined) ? opts.label : 'stdout'
|
||||
}
|
||||
|
||||
BrowserStdout.prototype._write = function(chunks, encoding, cb) {
|
||||
var output = chunks.toString ? chunks.toString() : chunks
|
||||
if (this.label === false) {
|
||||
console.log(output)
|
||||
} else {
|
||||
console.log(this.label+':', output)
|
||||
}
|
||||
process.nextTick(cb)
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"browser-stdout@1.3.1",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "browser-stdout@1.3.1",
|
||||
"_id": "browser-stdout@1.3.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
|
||||
"_location": "/browser-stdout",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "browser-stdout@1.3.1",
|
||||
"name": "browser-stdout",
|
||||
"escapedName": "browser-stdout",
|
||||
"rawSpec": "1.3.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.3.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/mocha"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||
"_spec": "1.3.1",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "kumavis"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/kumavis/browser-stdout/issues"
|
||||
},
|
||||
"description": "`process.stdout` in your browser.",
|
||||
"homepage": "https://github.com/kumavis/browser-stdout#readme",
|
||||
"license": "ISC",
|
||||
"main": "index.js",
|
||||
"name": "browser-stdout",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/kumavis/browser-stdout.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "1.3.1"
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const preserveCamelCase = string => {
|
||||
let isLastCharLower = false;
|
||||
let isLastCharUpper = false;
|
||||
let isLastLastCharUpper = false;
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
const character = string[i];
|
||||
|
||||
if (isLastCharLower && /[a-zA-Z]/.test(character) && character.toUpperCase() === character) {
|
||||
string = string.slice(0, i) + '-' + string.slice(i);
|
||||
isLastCharLower = false;
|
||||
isLastLastCharUpper = isLastCharUpper;
|
||||
isLastCharUpper = true;
|
||||
i++;
|
||||
} else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(character) && character.toLowerCase() === character) {
|
||||
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
||||
isLastLastCharUpper = isLastCharUpper;
|
||||
isLastCharUpper = false;
|
||||
isLastCharLower = true;
|
||||
} else {
|
||||
isLastCharLower = character.toLowerCase() === character && character.toUpperCase() !== character;
|
||||
isLastLastCharUpper = isLastCharUpper;
|
||||
isLastCharUpper = character.toUpperCase() === character && character.toLowerCase() !== character;
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
const camelCase = (input, options) => {
|
||||
if (!(typeof input === 'string' || Array.isArray(input))) {
|
||||
throw new TypeError('Expected the input to be `string | string[]`');
|
||||
}
|
||||
|
||||
options = Object.assign({
|
||||
pascalCase: false
|
||||
}, options);
|
||||
|
||||
const postProcess = x => options.pascalCase ? x.charAt(0).toUpperCase() + x.slice(1) : x;
|
||||
|
||||
if (Array.isArray(input)) {
|
||||
input = input.map(x => x.trim())
|
||||
.filter(x => x.length)
|
||||
.join('-');
|
||||
} else {
|
||||
input = input.trim();
|
||||
}
|
||||
|
||||
if (input.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (input.length === 1) {
|
||||
return options.pascalCase ? input.toUpperCase() : input.toLowerCase();
|
||||
}
|
||||
|
||||
const hasUpperCase = input !== input.toLowerCase();
|
||||
|
||||
if (hasUpperCase) {
|
||||
input = preserveCamelCase(input);
|
||||
}
|
||||
|
||||
input = input
|
||||
.replace(/^[_.\- ]+/, '')
|
||||
.toLowerCase()
|
||||
.replace(/[_.\- ]+(\w|$)/g, (_, p1) => p1.toUpperCase())
|
||||
.replace(/\d+(\w|$)/g, m => m.toUpperCase());
|
||||
|
||||
return postProcess(input);
|
||||
};
|
||||
|
||||
module.exports = camelCase;
|
||||
// TODO: Remove this for the next major release
|
||||
module.exports.default = camelCase;
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"camelcase@5.3.1",
|
||||
"C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer"
|
||||
]
|
||||
],
|
||||
"_from": "camelcase@5.3.1",
|
||||
"_id": "camelcase@5.3.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"_location": "/camelcase",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "camelcase@5.3.1",
|
||||
"name": "camelcase",
|
||||
"escapedName": "camelcase",
|
||||
"rawSpec": "5.3.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "5.3.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/yargs-parser",
|
||||
"/yargs-unparser/yargs-parser"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"_spec": "5.3.1",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\WebServer",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "sindresorhus.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/sindresorhus/camelcase/issues"
|
||||
},
|
||||
"description": "Convert a dash/dot/underscore/space separated string to camelCase or PascalCase: `foo-bar` → `fooBar`",
|
||||
"devDependencies": {
|
||||
"ava": "^1.4.1",
|
||||
"tsd": "^0.7.1",
|
||||
"xo": "^0.24.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"homepage": "https://github.com/sindresorhus/camelcase#readme",
|
||||
"keywords": [
|
||||
"camelcase",
|
||||
"camel-case",
|
||||
"camel",
|
||||
"case",
|
||||
"dash",
|
||||
"hyphen",
|
||||
"dot",
|
||||
"underscore",
|
||||
"separator",
|
||||
"string",
|
||||
"text",
|
||||
"convert",
|
||||
"pascalcase",
|
||||
"pascal-case"
|
||||
],
|
||||
"license": "MIT",
|
||||
"name": "camelcase",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sindresorhus/camelcase.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && ava && tsd"
|
||||
},
|
||||
"version": "5.3.1"
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
"use strict"
|
||||
|
||||
const { execSync } = require ('child_process')
|
||||
const log = require ('ololog').noLocate
|
||||
const { groupBy } = require ('../ccxt.js')
|
||||
const { values } = Object
|
||||
const assert = require ('assert')
|
||||
|
||||
function cleanupOldTags () {
|
||||
|
||||
const tags = execSync ('git tag').toString ().split ('\n').filter (s => s).map (t => {
|
||||
|
||||
const [major, minor, patch] = t.replace ('v', '').split ('.').map (Number)
|
||||
|
||||
assert (major < 100)
|
||||
assert (minor < 100)
|
||||
|
||||
return {
|
||||
key: (major * 100) + minor,
|
||||
tag: t,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
}
|
||||
})
|
||||
|
||||
const tagsByMajorMinor = values (groupBy (tags, 'key')).sort ((a, b) => a[0].key - b[0].key)
|
||||
|
||||
// Preserve all tags for first 3 minor versions
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
|
||||
const tags = tagsByMajorMinor.pop ()
|
||||
|
||||
if (tags) {
|
||||
log.green.bright ('Preserving', tags[0].tag, '...', tags[tags.length - 1].tag)
|
||||
}
|
||||
}
|
||||
|
||||
// For older versions, leave only "round" numbered versions (1/10th)
|
||||
|
||||
let tagsToDelete = []
|
||||
|
||||
for (const tags of tagsByMajorMinor) {
|
||||
|
||||
for (const { tag, patch } of tags) {
|
||||
|
||||
if (patch === 1) {
|
||||
log.green ('Preserving', tag)
|
||||
|
||||
} else {
|
||||
tagsToDelete.push (tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.bright.red ('Deleting', tagsToDelete.length, 'tags...')
|
||||
|
||||
if (!process.argv.includes ('--paper')) {
|
||||
|
||||
/* If it happens on a CI server, we don't want it to fail the build because of a super
|
||||
long execution time (one tag deletion takes ~5 sec...), hence that limit here */
|
||||
|
||||
if (process.argv.includes ('--limit')) {
|
||||
tagsToDelete = tagsToDelete.slice (-500)
|
||||
}
|
||||
|
||||
for (const tag of tagsToDelete) {
|
||||
|
||||
log.dim ('Deleting', tag)
|
||||
execSync (`git tag -d ${tag} && git push origin :refs/tags/${tag}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// main entry point
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
// if called directly like `node module`
|
||||
|
||||
cleanupOldTags ()
|
||||
|
||||
} else {
|
||||
|
||||
// do nothing if required as a module
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
module.exports = {
|
||||
cleanupOldTags,
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
|
||||
AF: 'Afghanistan',
|
||||
AX: 'Åland Islands',
|
||||
AL: 'Albania',
|
||||
DZ: 'Algeria',
|
||||
AS: 'American Samoa',
|
||||
AD: 'Andorra',
|
||||
AO: 'Angola',
|
||||
AI: 'Anguilla',
|
||||
AQ: 'Antarctica',
|
||||
AG: 'Antigua and Barbuda',
|
||||
AR: 'Argentina',
|
||||
AM: 'Armenia',
|
||||
AW: 'Aruba',
|
||||
AU: 'Australia',
|
||||
AT: 'Austria',
|
||||
AZ: 'Azerbaijan',
|
||||
BS: 'Bahamas',
|
||||
BH: 'Bahrain',
|
||||
BD: 'Bangladesh',
|
||||
BB: 'Barbados',
|
||||
BY: 'Belarus',
|
||||
BE: 'Belgium',
|
||||
BZ: 'Belize',
|
||||
BJ: 'Benin',
|
||||
BM: 'Bermuda',
|
||||
BT: 'Bhutan',
|
||||
BO: 'Bolivia',
|
||||
BQ: 'Bonaire, Sint Eustatius and Saba',
|
||||
BA: 'Bosnia and Herzegovina',
|
||||
BW: 'Botswana',
|
||||
BV: 'Bouvet Island',
|
||||
BR: 'Brazil',
|
||||
IO: 'British Indian Ocean Territory',
|
||||
BN: 'Brunei Darussalam',
|
||||
BG: 'Bulgaria',
|
||||
BF: 'Burkina Faso',
|
||||
BI: 'Burundi',
|
||||
KH: 'Cambodia',
|
||||
CM: 'Cameroon',
|
||||
CA: 'Canada',
|
||||
CV: 'Cabo Verde',
|
||||
KY: 'Cayman Islands',
|
||||
CF: 'Central African Republic',
|
||||
TD: 'Chad',
|
||||
CL: 'Chile',
|
||||
CN: 'China',
|
||||
CX: 'Christmas Island',
|
||||
CC: 'Cocos (Keeling) Islands',
|
||||
CO: 'Colombia',
|
||||
KM: 'Comoros',
|
||||
CG: 'Congo',
|
||||
CD: 'Congo',
|
||||
CK: 'Cook Islands',
|
||||
CR: 'Costa Rica',
|
||||
CI: 'Côte d\'Ivoire',
|
||||
HR: 'Croatia',
|
||||
CU: 'Cuba',
|
||||
CW: 'Curaçao',
|
||||
CY: 'Cyprus',
|
||||
CZ: 'Czech Republic',
|
||||
DK: 'Denmark',
|
||||
DJ: 'Djibouti',
|
||||
DM: 'Dominica',
|
||||
DO: 'Dominican Republic',
|
||||
EC: 'Ecuador',
|
||||
EG: 'Egypt',
|
||||
SV: 'El Salvador',
|
||||
GQ: 'Equatorial Guinea',
|
||||
ER: 'Eritrea',
|
||||
EE: 'Estonia',
|
||||
ET: 'Ethiopia',
|
||||
EU: 'EU',
|
||||
FK: 'Falkland Islands',
|
||||
FO: 'Faroe Islands',
|
||||
FJ: 'Fiji',
|
||||
FI: 'Finland',
|
||||
FR: 'France',
|
||||
GF: 'French Guiana',
|
||||
PF: 'French Polynesia',
|
||||
TF: 'French Southern Territories',
|
||||
GA: 'Gabon',
|
||||
GM: 'Gambia',
|
||||
GE: 'Georgia',
|
||||
DE: 'Germany',
|
||||
GH: 'Ghana',
|
||||
GI: 'Gibraltar',
|
||||
GR: 'Greece',
|
||||
GL: 'Greenland',
|
||||
GD: 'Grenada',
|
||||
GP: 'Guadeloupe',
|
||||
GU: 'Guam',
|
||||
GT: 'Guatemala',
|
||||
GG: 'Guernsey',
|
||||
GN: 'Guinea',
|
||||
GW: 'Guinea-Bissau',
|
||||
GY: 'Guyana',
|
||||
HT: 'Haiti',
|
||||
HM: 'Heard Island and McDonald Islands',
|
||||
VA: 'Holy See',
|
||||
HN: 'Honduras',
|
||||
HK: 'Hong Kong',
|
||||
HU: 'Hungary',
|
||||
IS: 'Iceland',
|
||||
IN: 'India',
|
||||
ID: 'Indonesia',
|
||||
IR: 'Iran',
|
||||
IQ: 'Iraq',
|
||||
IE: 'Ireland',
|
||||
IM: 'Isle of Man',
|
||||
IL: 'Israel',
|
||||
IT: 'Italy',
|
||||
JM: 'Jamaica',
|
||||
JP: 'Japan',
|
||||
JE: 'Jersey',
|
||||
JO: 'Jordan',
|
||||
KZ: 'Kazakhstan',
|
||||
KE: 'Kenya',
|
||||
KI: 'Kiribati',
|
||||
KP: 'North Korea (DPRK)',
|
||||
KR: 'South Korea',
|
||||
KW: 'Kuwait',
|
||||
KG: 'Kyrgyzstan',
|
||||
LA: 'Lao People\'s Democratic Republic',
|
||||
LV: 'Latvia',
|
||||
LB: 'Lebanon',
|
||||
LS: 'Lesotho',
|
||||
LR: 'Liberia',
|
||||
LY: 'Libya',
|
||||
LI: 'Liechtenstein',
|
||||
LT: 'Lithuania',
|
||||
LU: 'Luxembourg',
|
||||
MO: 'Macao',
|
||||
MK: 'Macedonia',
|
||||
MG: 'Madagascar',
|
||||
MW: 'Malawi',
|
||||
MY: 'Malaysia',
|
||||
MV: 'Maldives',
|
||||
ML: 'Mali',
|
||||
MT: 'Malta',
|
||||
MH: 'Marshall Islands',
|
||||
MQ: 'Martinique',
|
||||
MR: 'Mauritania',
|
||||
MU: 'Mauritius',
|
||||
YT: 'Mayotte',
|
||||
MX: 'Mexico',
|
||||
FM: 'Micronesia (Federated States of)',
|
||||
MD: 'Moldova',
|
||||
MC: 'Monaco',
|
||||
MN: 'Mongolia',
|
||||
ME: 'Montenegro',
|
||||
MS: 'Montserrat',
|
||||
MA: 'Morocco',
|
||||
MZ: 'Mozambique',
|
||||
MM: 'Myanmar',
|
||||
NA: 'Namibia',
|
||||
NR: 'Nauru',
|
||||
NP: 'Nepal',
|
||||
NL: 'Netherlands',
|
||||
NC: 'New Caledonia',
|
||||
NZ: 'New Zealand',
|
||||
NI: 'Nicaragua',
|
||||
NE: 'Niger',
|
||||
NG: 'Nigeria',
|
||||
NU: 'Niue',
|
||||
NF: 'Norfolk Island',
|
||||
MP: 'Northern Mariana Islands',
|
||||
NO: 'Norway',
|
||||
OM: 'Oman',
|
||||
PK: 'Pakistan',
|
||||
PW: 'Palau',
|
||||
PS: 'Palestine, State of',
|
||||
PA: 'Panama',
|
||||
PG: 'Papua New Guinea',
|
||||
PY: 'Paraguay',
|
||||
PE: 'Peru',
|
||||
PH: 'Philippines',
|
||||
PN: 'Pitcairn',
|
||||
PL: 'Poland',
|
||||
PT: 'Portugal',
|
||||
PR: 'Puerto Rico',
|
||||
QA: 'Qatar',
|
||||
RE: 'Réunion',
|
||||
RO: 'Romania',
|
||||
RU: 'Russia',
|
||||
RW: 'Rwanda',
|
||||
BL: 'Saint Barthélemy',
|
||||
SH: 'Saint Helena, Ascension and Tristan da Cunha',
|
||||
KN: 'Saint Kitts and Nevis',
|
||||
LC: 'Saint Lucia',
|
||||
MF: 'Saint Martin (French)',
|
||||
PM: 'Saint Pierre and Miquelon',
|
||||
VC: 'St. Vincent & Grenadines',
|
||||
WS: 'Samoa',
|
||||
SM: 'San Marino',
|
||||
ST: 'Sao Tome and Principe',
|
||||
SA: 'Saudi Arabia',
|
||||
SN: 'Senegal',
|
||||
RS: 'Serbia',
|
||||
SC: 'Seychelles',
|
||||
SL: 'Sierra Leone',
|
||||
SG: 'Singapore',
|
||||
SX: 'Sint Maarten (Dutch)',
|
||||
SK: 'Slovakia',
|
||||
SI: 'Slovenia',
|
||||
SB: 'Solomon Islands',
|
||||
SO: 'Somalia',
|
||||
ZA: 'South Africa',
|
||||
GS: 'South Georgia and the South Sandwich Islands',
|
||||
SS: 'South Sudan',
|
||||
ES: 'Spain',
|
||||
LK: 'Sri Lanka',
|
||||
SD: 'Sudan',
|
||||
SR: 'Suriname',
|
||||
SJ: 'Svalbard and Jan Mayen',
|
||||
SZ: 'Swaziland',
|
||||
SE: 'Sweden',
|
||||
CH: 'Switzerland',
|
||||
SY: 'Syrian Arab Republic',
|
||||
TW: 'Taiwan',
|
||||
TJ: 'Tajikistan',
|
||||
TZ: 'Tanzania',
|
||||
TH: 'Thailand',
|
||||
TL: 'Timor-Leste',
|
||||
TG: 'Togo',
|
||||
TK: 'Tokelau',
|
||||
TO: 'Tonga',
|
||||
TT: 'Trinidad and Tobago',
|
||||
TN: 'Tunisia',
|
||||
TR: 'Turkey',
|
||||
TM: 'Turkmenistan',
|
||||
TC: 'Turks and Caicos Islands',
|
||||
TV: 'Tuvalu',
|
||||
UG: 'Uganda',
|
||||
UA: 'Ukraine',
|
||||
AE: 'United Arab Emirates',
|
||||
GB: 'UK',
|
||||
UK: 'UK',
|
||||
US: 'US',
|
||||
UM: 'US Minor Islands',
|
||||
UY: 'Uruguay',
|
||||
UZ: 'Uzbekistan',
|
||||
VU: 'Vanuatu',
|
||||
VE: 'Venezuela',
|
||||
VN: 'Vietnam',
|
||||
VG: 'British Virgin Islands',
|
||||
VI: 'US Virgin Islands',
|
||||
WF: 'Wallis and Futuna',
|
||||
EH: 'Western Sahara',
|
||||
YE: 'Yemen',
|
||||
ZM: 'Zambia',
|
||||
ZW: 'Zimbabwe',
|
||||
}
|
||||
+373
@@ -0,0 +1,373 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Usage:
|
||||
//
|
||||
// npm run export-exchanges
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
"use strict";
|
||||
|
||||
const fs = require ('fs')
|
||||
, countries = require ('./countries')
|
||||
, asTable = require ('as-table')
|
||||
, execSync = require ('child_process').execSync
|
||||
, log = require ('ololog').unlimited
|
||||
, ansi = require ('ansicolor').nice
|
||||
, { keys, values, entries } = Object
|
||||
, { replaceInFile } = require ('./fs.js')
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function cloneGitHubWiki (gitWikiPath) {
|
||||
|
||||
if (!fs.existsSync (gitWikiPath)) {
|
||||
log.bright.cyan ('Cloning ccxt.wiki...')
|
||||
execSync ('git clone https://github.com/ccxt/ccxt.wiki.git ' + gitWikiPath)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function logExportExchanges (filename, regex, replacement) {
|
||||
log.bright.cyan ('Exporting exchanges →', filename.yellow)
|
||||
replaceInFile (filename, regex, replacement)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function getIncludedExchangeIds () {
|
||||
|
||||
const includedIds = fs.readFileSync ('exchanges.cfg')
|
||||
.toString () // Buffer → String
|
||||
.split ('\n') // String → Array
|
||||
.map (line => line.split ('#')[0].trim ()) // trim comments
|
||||
.filter (exchange => exchange); // filter empty lines
|
||||
|
||||
const isIncluded = (id) => ((includedIds.length === 0) || includedIds.includes (id))
|
||||
|
||||
const ids = fs.readdirSync ('./js/')
|
||||
.filter (file => file.match (/[a-zA-Z0-9_-]+.js$/))
|
||||
.map (file => file.slice (0, -3))
|
||||
.filter (isIncluded);
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function exportExchanges (replacements) {
|
||||
|
||||
log.bright.yellow ('Exporting exchanges...')
|
||||
|
||||
replacements.forEach (({ file, regex, replacement }) => {
|
||||
logExportExchanges (file, regex, replacement)
|
||||
})
|
||||
|
||||
log.bright.green ('Base sources updated successfully.')
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function createExchanges (ids) {
|
||||
|
||||
const ccxt = require ('../ccxt.js')
|
||||
|
||||
const createExchange = (id) => {
|
||||
ccxt[id].prototype.checkRequiredDependencies = () => {} // suppress it
|
||||
return new (ccxt)[id] ()
|
||||
}
|
||||
|
||||
return ccxt.indexBy (ids.map (createExchange), 'id')
|
||||
}
|
||||
|
||||
// // ----------------------------------------------------------------------------
|
||||
// // strategically placed exactly here (we can require it AFTER the export)
|
||||
// const ccxt = require ('../ccxt.js')
|
||||
// // ----------------------------------------------------------------------------
|
||||
// // create exchanges
|
||||
// const createExchange = (id) => {
|
||||
// ccxt[id].prototype.checkRequiredDependencies = () => {} // suppress it
|
||||
// return new (ccxt)[id] ()
|
||||
// }
|
||||
// const exchanges = ccxt.indexBy (ids.map (createExchange), 'id')
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TODO: REWRITE THIS ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
||||
|
||||
function exportSupportedAndCertifiedExchanges (exchanges, wikiPath) {
|
||||
|
||||
// ............................................................................
|
||||
// markup constants and helper functions
|
||||
|
||||
const countryName = (code) => countries[code] || code
|
||||
|
||||
const ccxtCertifiedBadge = '[](https://github.com/ccxt/ccxt/wiki/Certification)'
|
||||
const ccxtProBadge = '[](https://ccxt.pro)'
|
||||
const logoHeading = ' '.repeat (7) + 'logo' + ' '.repeat (7)
|
||||
const tableHeadings = [ logoHeading, 'id', 'name', 'ver', 'doc', 'certified', 'pro' ]
|
||||
const exchangesByCountryHeading = [ 'country / region', ... tableHeadings.slice (0, 5) ]
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// list all supported exchanges
|
||||
|
||||
const exchangesNotListedInDocs = []
|
||||
|
||||
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 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 [
|
||||
'[](' + 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)
|
||||
let lines = table.split ("\n")
|
||||
lines.splice (1,0, lines[0].replace (/[^\|]/g, '-'))
|
||||
let headerLine = lines[1].split ('|')
|
||||
headerLine[3] = ':' + headerLine[3].slice (1, headerLine[3].length - 1) + ':'
|
||||
headerLine[4] = ':' + headerLine[4].slice (1, headerLine[4].length - 1) + ':'
|
||||
lines[1] = headerLine.join ('|')
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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,
|
||||
'[](' + url + ')',
|
||||
exchange.id,
|
||||
'[' + exchange.name + '](' + url + ')',
|
||||
version,
|
||||
'[API](' + doc + ')',
|
||||
// doesn't fit in width
|
||||
// exchange.certified ? ccxtCertifiedBadge : '',
|
||||
]
|
||||
result.push (entry)
|
||||
}
|
||||
})
|
||||
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)
|
||||
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 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function exportExchangeIdsToExchangesJson (exchanges) {
|
||||
log.bright ('Exporting exchange ids to'.cyan, 'exchanges.json'.yellow)
|
||||
fs.writeFileSync ('exchanges.json', JSON.stringify ({ ids: keys (exchanges) }, null, 4))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function exportWikiToGitHub (wikiPath, gitWikiPath) {
|
||||
|
||||
log.bright.cyan ('Exporting wiki to GitHub')
|
||||
|
||||
const ccxtWikiFiles = {
|
||||
'README.md': 'Home.md',
|
||||
'Install.md': 'Install.md',
|
||||
'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)) {
|
||||
|
||||
const sourcePath = wikiPath + '/' + sourceFile
|
||||
const destinationPath = gitWikiPath + '/' + destinationFile
|
||||
log.bright.cyan ('Exporting', sourcePath.yellow, '→', destinationPath.yellow)
|
||||
fs.writeFileSync (destinationPath, fs.readFileSync (sourcePath))
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function exportKeywordsToPackageJson (exchanges) {
|
||||
|
||||
log.bright ('Exporting exchange keywords to'.cyan, 'package.json'.yellow)
|
||||
|
||||
// const packageJSON = require ('../package.json')
|
||||
const packageJSON = JSON.parse (fs.readFileSync ('./package.json'))
|
||||
const keywords = new Set (packageJSON.keywords)
|
||||
|
||||
for (const ex of values (exchanges)) {
|
||||
for (const url of Array.isArray (ex.urls.www) ? ex.urls.www : [ex.urls.www]) {
|
||||
keywords.add (url.replace (/(http|https):\/\/(www\.)?/, '').replace (/\/.*/, ''))
|
||||
}
|
||||
keywords.add (ex.name)
|
||||
}
|
||||
|
||||
packageJSON.keywords = [...keywords]
|
||||
fs.writeFileSync ('./package.json', JSON.stringify (packageJSON, null, 2))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function exportEverything () {
|
||||
|
||||
const wikiPath = 'wiki'
|
||||
, gitWikiPath = 'build/ccxt.wiki'
|
||||
|
||||
cloneGitHubWiki (gitWikiPath)
|
||||
|
||||
const ids = getIncludedExchangeIds ()
|
||||
|
||||
const replacements = [
|
||||
{
|
||||
file: './ccxt.js',
|
||||
regex: /(?:const|var)\s+exchanges\s+\=\s+\{[^\}]+\}/,
|
||||
replacement: "const exchanges = {\n" + ids.map (id => (" '" + id + "':").padEnd (30) + " require ('./js/" + id + ".js'),").join ("\n") + " \n}",
|
||||
},
|
||||
{
|
||||
file: './python/ccxt/__init__.py',
|
||||
regex: /exchanges \= \[[^\]]+\]/,
|
||||
replacement: "exchanges = [\n" + " '" + ids.join ("',\n '") + "'," + "\n]",
|
||||
},
|
||||
{
|
||||
file: './python/ccxt/__init__.py',
|
||||
regex: /(?:from ccxt\.[^\.]+ import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]exchanges/,
|
||||
replacement: ids.map (id => ('from ccxt.' + id + ' import ' + id).padEnd (60) + '# noqa: F401').join ("\n") + "\n\nexchanges",
|
||||
},
|
||||
{
|
||||
file: './python/ccxt/async_support/__init__.py',
|
||||
regex: /(?:from ccxt\.async_support\.[^\.]+ import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]exchanges/,
|
||||
replacement: ids.map (id => ('from ccxt.async_support.' + id + ' import ' + id).padEnd (74) + '# noqa: F401').join ("\n") + "\n\nexchanges",
|
||||
},
|
||||
{
|
||||
file: './python/ccxt/async_support/__init__.py',
|
||||
regex: /exchanges \= \[[^\]]+\]/,
|
||||
replacement: "exchanges = [\n" + " '" + ids.join ("',\n '") + "'," + "\n]",
|
||||
},
|
||||
{
|
||||
file: './php/base/Exchange.php',
|
||||
regex: /public static \$exchanges \= array\s*\([^\)]+\)/,
|
||||
replacement: "public static $exchanges = array(\n '" + ids.join ("',\n '") + "',\n )",
|
||||
},
|
||||
]
|
||||
|
||||
exportExchanges (replacements)
|
||||
|
||||
// strategically placed exactly here (we can require it AFTER the export)
|
||||
const exchanges = createExchanges (ids)
|
||||
|
||||
exportSupportedAndCertifiedExchanges (exchanges, wikiPath)
|
||||
exportExchangeIdsToExchangesJson (exchanges)
|
||||
exportWikiToGitHub (wikiPath, gitWikiPath)
|
||||
exportKeywordsToPackageJson (exchanges)
|
||||
|
||||
log.bright.green ('Exported successfully.')
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// main entry point
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
// if called directly like `node module`
|
||||
|
||||
exportEverything ()
|
||||
|
||||
} else {
|
||||
|
||||
// do nothing if required as a module
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
module.exports = {
|
||||
cloneGitHubWiki,
|
||||
getIncludedExchangeIds,
|
||||
exportExchanges,
|
||||
createExchanges,
|
||||
exportSupportedAndCertifiedExchanges,
|
||||
exportExchangeIdsToExchangesJson,
|
||||
exportWikiToGitHub,
|
||||
exportKeywordsToPackageJson,
|
||||
exportEverything,
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require ('fs')
|
||||
, path = require ('path')
|
||||
|
||||
function replaceInFile (filename, regex, replacement) {
|
||||
const contents = fs.readFileSync (filename, 'utf8')
|
||||
const newContents = contents.replace (regex, replacement)
|
||||
fs.truncateSync (filename)
|
||||
fs.writeFileSync (filename, newContents)
|
||||
}
|
||||
|
||||
function copyFile (oldName, newName) {
|
||||
const contents = fs.readFileSync (oldName, 'utf8')
|
||||
fs.truncateSync (newName)
|
||||
fs.writeFileSync (newName, contents)
|
||||
}
|
||||
|
||||
function overwriteFile (filename, contents) {
|
||||
// log.cyan ('Overwriting → ' + filename.yellow)
|
||||
fs.closeSync (fs.openSync (filename, 'a'));
|
||||
fs.truncateSync (filename)
|
||||
fs.writeFileSync (filename, contents)
|
||||
}
|
||||
|
||||
function createFolder (folder) {
|
||||
try {
|
||||
fs.mkdirSync (folder)
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createFolderRecursively (folder) {
|
||||
const parts = folder.split (path.sep)
|
||||
for (let i = 1; i <= parts.length; i++) {
|
||||
createFolder (path.join.apply (null, parts.slice (0, i)))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
replaceInFile,
|
||||
copyFile,
|
||||
overwriteFile,
|
||||
createFolder,
|
||||
createFolderRecursively,
|
||||
}
|
||||
+1308
File diff suppressed because it is too large.
Load diff
+87
@@ -0,0 +1,87 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Usage:
|
||||
//
|
||||
// npm run vss
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
"use strict";
|
||||
|
||||
const fs = require ('fs')
|
||||
const log = require ('ololog')
|
||||
const ansi = require ('ansicolor').nice
|
||||
const { execSync } = require ('child_process')
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function incrementVersionPatchNumber (version) {
|
||||
|
||||
let [ major, minor, patch ] = version.split ('.')
|
||||
|
||||
// we don't increment it here anymore, because
|
||||
// npm version patch will be explicitly called before
|
||||
|
||||
// patch = (parseInt (patch) + 1).toString ()
|
||||
|
||||
version = [ major, minor, patch ].join ('.')
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function vss (filename, template, version) {
|
||||
log.bright.cyan ('Single-sourcing version', version, './package.json → ' + filename.yellow)
|
||||
const content = fs.readFileSync (filename, 'utf8')
|
||||
const regexp = new RegExp (template.replace (/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // escape string for use in regexp
|
||||
.replace ('\\{version\\}', '\\d+\\.\\d+\\.\\d+'), 'g')
|
||||
fs.truncateSync (filename)
|
||||
fs.writeFileSync (filename, content.replace (regexp, template.replace ('{version}', version)))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
function vssEverything () {
|
||||
|
||||
let { version } = require ('../package.json')
|
||||
|
||||
log.bright ('Old version: '.dim, version)
|
||||
version = incrementVersionPatchNumber (version)
|
||||
log.bright ('New version: '.cyan, version)
|
||||
|
||||
vss ('./ccxt.js', "const version = '{version}'", version)
|
||||
vss ('./php/base/Exchange.php', "$version = '{version}'", version)
|
||||
vss ('./php/base/Exchange.php', "VERSION = '{version}'", version)
|
||||
vss ('./python/ccxt/__init__.py', "__version__ = '{version}'", version)
|
||||
vss ('./python/ccxt/base/exchange.py', "__version__ = '{version}'", version)
|
||||
vss ('./python/ccxt/async_support/__init__.py', "__version__ = '{version}'", version)
|
||||
vss ('./python/ccxt/async_support/base/exchange.py', "__version__ = '{version}'", version)
|
||||
|
||||
vss ('./README.md', "ccxt@{version}", version)
|
||||
vss ('./wiki/Install.md', "ccxt@{version}", version)
|
||||
|
||||
execSync ('cp ./package.json ./LICENSE.txt ./keys.json ./python/')
|
||||
|
||||
log.bright.green ('Version single-sourced successfully.')
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// main entry point
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
// if called directly like `node module`
|
||||
|
||||
vssEverything ()
|
||||
|
||||
} else {
|
||||
|
||||
// do nothing if required as a module
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
module.exports = {
|
||||
incrementVersionPatchNumber,
|
||||
vss,
|
||||
vssEverything,
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/* A entry point for the browser bundle version. This gets compiled by:
|
||||
|
||||
browserify --debug ./ccxt.browser.js > ./dist/ccxt.browser.js
|
||||
*/
|
||||
|
||||
window.ccxt = require ('./ccxt')
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Igor Kroitor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./js/base/Exchange')
|
||||
, functions = require ('./js/base/functions')
|
||||
, errors = require ('./js/base/errors')
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// this is updated by vss.js when building
|
||||
|
||||
const version = '1.23.67'
|
||||
|
||||
Exchange.ccxtVersion = version
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const exchanges = {
|
||||
'_1btcxe': require ('./js/_1btcxe.js'),
|
||||
'acx': require ('./js/acx.js'),
|
||||
'adara': require ('./js/adara.js'),
|
||||
'anxpro': require ('./js/anxpro.js'),
|
||||
'bcex': require ('./js/bcex.js'),
|
||||
'bequant': require ('./js/bequant.js'),
|
||||
'bibox': require ('./js/bibox.js'),
|
||||
'bigone': require ('./js/bigone.js'),
|
||||
'binance': require ('./js/binance.js'),
|
||||
'binanceje': require ('./js/binanceje.js'),
|
||||
'binanceus': require ('./js/binanceus.js'),
|
||||
'bit2c': require ('./js/bit2c.js'),
|
||||
'bitbank': require ('./js/bitbank.js'),
|
||||
'bitbay': require ('./js/bitbay.js'),
|
||||
'bitfinex': require ('./js/bitfinex.js'),
|
||||
'bitfinex2': require ('./js/bitfinex2.js'),
|
||||
'bitflyer': require ('./js/bitflyer.js'),
|
||||
'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'),
|
||||
'bitso': require ('./js/bitso.js'),
|
||||
'bitstamp': require ('./js/bitstamp.js'),
|
||||
'bitstamp1': require ('./js/bitstamp1.js'),
|
||||
'bittrex': require ('./js/bittrex.js'),
|
||||
'bitz': require ('./js/bitz.js'),
|
||||
'bl3p': require ('./js/bl3p.js'),
|
||||
'bleutrade': require ('./js/bleutrade.js'),
|
||||
'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'),
|
||||
'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'),
|
||||
'coincheck': require ('./js/coincheck.js'),
|
||||
'coinegg': require ('./js/coinegg.js'),
|
||||
'coinex': require ('./js/coinex.js'),
|
||||
'coinfalcon': require ('./js/coinfalcon.js'),
|
||||
'coinfloor': require ('./js/coinfloor.js'),
|
||||
'coingi': require ('./js/coingi.js'),
|
||||
'coinmarketcap': require ('./js/coinmarketcap.js'),
|
||||
'coinmate': require ('./js/coinmate.js'),
|
||||
'coinone': require ('./js/coinone.js'),
|
||||
'coinspot': require ('./js/coinspot.js'),
|
||||
'coolcoin': require ('./js/coolcoin.js'),
|
||||
'coss': require ('./js/coss.js'),
|
||||
'crex24': require ('./js/crex24.js'),
|
||||
'deribit': require ('./js/deribit.js'),
|
||||
'digifinex': require ('./js/digifinex.js'),
|
||||
'dsx': require ('./js/dsx.js'),
|
||||
'exmo': require ('./js/exmo.js'),
|
||||
'exx': require ('./js/exx.js'),
|
||||
'fcoin': require ('./js/fcoin.js'),
|
||||
'fcoinjp': require ('./js/fcoinjp.js'),
|
||||
'flowbtc': require ('./js/flowbtc.js'),
|
||||
'foxbit': require ('./js/foxbit.js'),
|
||||
'ftx': require ('./js/ftx.js'),
|
||||
'fybse': require ('./js/fybse.js'),
|
||||
'gateio': require ('./js/gateio.js'),
|
||||
'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'),
|
||||
'idex': require ('./js/idex.js'),
|
||||
'independentreserve': require ('./js/independentreserve.js'),
|
||||
'indodax': require ('./js/indodax.js'),
|
||||
'itbit': require ('./js/itbit.js'),
|
||||
'kkex': require ('./js/kkex.js'),
|
||||
'kraken': require ('./js/kraken.js'),
|
||||
'kucoin': require ('./js/kucoin.js'),
|
||||
'kuna': require ('./js/kuna.js'),
|
||||
'lakebtc': require ('./js/lakebtc.js'),
|
||||
'latoken': require ('./js/latoken.js'),
|
||||
'lbank': require ('./js/lbank.js'),
|
||||
'liquid': require ('./js/liquid.js'),
|
||||
'livecoin': require ('./js/livecoin.js'),
|
||||
'luno': require ('./js/luno.js'),
|
||||
'lykke': require ('./js/lykke.js'),
|
||||
'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'),
|
||||
'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'),
|
||||
'southxchange': require ('./js/southxchange.js'),
|
||||
'stex': require ('./js/stex.js'),
|
||||
'stronghold': require ('./js/stronghold.js'),
|
||||
'surbitcoin': require ('./js/surbitcoin.js'),
|
||||
'theocean': require ('./js/theocean.js'),
|
||||
'therock': require ('./js/therock.js'),
|
||||
'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'),
|
||||
'whitebit': require ('./js/whitebit.js'),
|
||||
'xbtce': require ('./js/xbtce.js'),
|
||||
'yobit': require ('./js/yobit.js'),
|
||||
'zaif': require ('./js/zaif.js'),
|
||||
'zb': require ('./js/zb.js'),
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = Object.assign ({ version, Exchange, 'exchanges': Object.keys (exchanges) }, exchanges, functions, errors)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
+125502
File diff suppressed because it is too large.
Load diff
+305
@@ -0,0 +1,305 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, ExchangeNotAvailable } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class _1btcxe extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': '_1btcxe',
|
||||
'name': '1BTCXE',
|
||||
'countries': [ 'PA' ], // Panama
|
||||
'comment': 'Crypto Capital API',
|
||||
'has': {
|
||||
'CORS': true,
|
||||
'withdraw': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1d': '1year',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/27766049-2b294408-5ecc-11e7-85cc-adaff013dc1a.jpg',
|
||||
'api': 'https://1btcxe.com/api',
|
||||
'www': 'https://1btcxe.com',
|
||||
'doc': 'https://1btcxe.com/api-docs.php',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'stats',
|
||||
'historical-prices',
|
||||
'order-book',
|
||||
'transactions',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'balances-and-info',
|
||||
'open-orders',
|
||||
'user-transactions',
|
||||
'btc-deposit-address/get',
|
||||
'btc-deposit-address/new',
|
||||
'deposits/get',
|
||||
'withdrawals/get',
|
||||
'orders/new',
|
||||
'orders/edit',
|
||||
'orders/cancel',
|
||||
'orders/status',
|
||||
'withdrawals/new',
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
return [
|
||||
{ 'id': 'USD', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'BTC', 'quoteId': 'USD' },
|
||||
{ 'id': 'EUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR' },
|
||||
{ 'id': 'CNY', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY', 'baseId': 'BTC', 'quoteId': 'CNY' },
|
||||
{ 'id': 'RUB', 'symbol': 'BTC/RUB', 'base': 'BTC', 'quote': 'RUB', 'baseId': 'BTC', 'quoteId': 'RUB' },
|
||||
{ 'id': 'CHF', 'symbol': 'BTC/CHF', 'base': 'BTC', 'quote': 'CHF', 'baseId': 'BTC', 'quoteId': 'CHF' },
|
||||
{ 'id': 'JPY', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'BTC', 'quoteId': 'JPY' },
|
||||
{ 'id': 'GBP', 'symbol': 'BTC/GBP', 'base': 'BTC', 'quote': 'GBP', 'baseId': 'BTC', 'quoteId': 'GBP' },
|
||||
{ 'id': 'CAD', 'symbol': 'BTC/CAD', 'base': 'BTC', 'quote': 'CAD', 'baseId': 'BTC', 'quoteId': 'CAD' },
|
||||
{ 'id': 'AUD', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'BTC', 'quoteId': 'AUD' },
|
||||
{ 'id': 'AED', 'symbol': 'BTC/AED', 'base': 'BTC', 'quote': 'AED', 'baseId': 'BTC', 'quoteId': 'AED' },
|
||||
{ 'id': 'BGN', 'symbol': 'BTC/BGN', 'base': 'BTC', 'quote': 'BGN', 'baseId': 'BTC', 'quoteId': 'BGN' },
|
||||
{ 'id': 'CZK', 'symbol': 'BTC/CZK', 'base': 'BTC', 'quote': 'CZK', 'baseId': 'BTC', 'quoteId': 'CZK' },
|
||||
{ 'id': 'DKK', 'symbol': 'BTC/DKK', 'base': 'BTC', 'quote': 'DKK', 'baseId': 'BTC', 'quoteId': 'DKK' },
|
||||
{ 'id': 'HKD', 'symbol': 'BTC/HKD', 'base': 'BTC', 'quote': 'HKD', 'baseId': 'BTC', 'quoteId': 'HKD' },
|
||||
{ 'id': 'HRK', 'symbol': 'BTC/HRK', 'base': 'BTC', 'quote': 'HRK', 'baseId': 'BTC', 'quoteId': 'HRK' },
|
||||
{ 'id': 'HUF', 'symbol': 'BTC/HUF', 'base': 'BTC', 'quote': 'HUF', 'baseId': 'BTC', 'quoteId': 'HUF' },
|
||||
{ 'id': 'ILS', 'symbol': 'BTC/ILS', 'base': 'BTC', 'quote': 'ILS', 'baseId': 'BTC', 'quoteId': 'ILS' },
|
||||
{ 'id': 'INR', 'symbol': 'BTC/INR', 'base': 'BTC', 'quote': 'INR', 'baseId': 'BTC', 'quoteId': 'INR' },
|
||||
{ 'id': 'MUR', 'symbol': 'BTC/MUR', 'base': 'BTC', 'quote': 'MUR', 'baseId': 'BTC', 'quoteId': 'MUR' },
|
||||
{ 'id': 'MXN', 'symbol': 'BTC/MXN', 'base': 'BTC', 'quote': 'MXN', 'baseId': 'BTC', 'quoteId': 'MXN' },
|
||||
{ 'id': 'NOK', 'symbol': 'BTC/NOK', 'base': 'BTC', 'quote': 'NOK', 'baseId': 'BTC', 'quoteId': 'NOK' },
|
||||
{ 'id': 'NZD', 'symbol': 'BTC/NZD', 'base': 'BTC', 'quote': 'NZD', 'baseId': 'BTC', 'quoteId': 'NZD' },
|
||||
{ 'id': 'PLN', 'symbol': 'BTC/PLN', 'base': 'BTC', 'quote': 'PLN', 'baseId': 'BTC', 'quoteId': 'PLN' },
|
||||
{ 'id': 'RON', 'symbol': 'BTC/RON', 'base': 'BTC', 'quote': 'RON', 'baseId': 'BTC', 'quoteId': 'RON' },
|
||||
{ 'id': 'SEK', 'symbol': 'BTC/SEK', 'base': 'BTC', 'quote': 'SEK', 'baseId': 'BTC', 'quoteId': 'SEK' },
|
||||
{ 'id': 'SGD', 'symbol': 'BTC/SGD', 'base': 'BTC', 'quote': 'SGD', 'baseId': 'BTC', 'quoteId': 'SGD' },
|
||||
{ 'id': 'THB', 'symbol': 'BTC/THB', 'base': 'BTC', 'quote': 'THB', 'baseId': 'BTC', 'quoteId': 'THB' },
|
||||
{ 'id': 'TRY', 'symbol': 'BTC/TRY', 'base': 'BTC', 'quote': 'TRY', 'baseId': 'BTC', 'quoteId': 'TRY' },
|
||||
{ 'id': 'ZAR', 'symbol': 'BTC/ZAR', 'base': 'BTC', 'quote': 'ZAR', 'baseId': 'BTC', 'quoteId': 'ZAR' },
|
||||
];
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privatePostBalancesAndInfo (params);
|
||||
const balance = response['balances-and-info'];
|
||||
const result = { 'info': balance };
|
||||
const codes = Object.keys (this.currencies);
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const currency = this.currency (code);
|
||||
const currencyId = currency['id'];
|
||||
const account = this.account ();
|
||||
account['free'] = this.safeFloat (balance['available'], currencyId);
|
||||
account['used'] = this.safeFloat (balance['on_hold'], currencyId);
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'currency': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetOrderBook (this.extend (request, params));
|
||||
return this.parseOrderBook (response['order-book'], undefined, 'bid', 'ask', 'price', 'order_amount');
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'currency': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetStats (this.extend (request, params));
|
||||
const ticker = this.safeValue (response, 'stats', {});
|
||||
const last = this.safeFloat (ticker, 'last_price');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': undefined,
|
||||
'datetime': undefined,
|
||||
'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, 'open'),
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': this.safeFloat (ticker, 'daily_change'),
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': undefined,
|
||||
'quoteVolume': this.safeFloat (ticker, 'total_btc_traded'),
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) {
|
||||
return [
|
||||
this.parse8601 (ohlcv['date'] + ' 00:00:00'),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
this.safeFloat (ohlcv, 'price'),
|
||||
undefined,
|
||||
];
|
||||
}
|
||||
|
||||
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'],
|
||||
'timeframe': this.timeframes[timeframe],
|
||||
}, params));
|
||||
const ohlcvs = this.toArray (this.omit (response['historical-prices'], 'request_currency'));
|
||||
return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.safeTimestamp (trade, 'timestamp');
|
||||
const id = this.safeString (trade, 'id');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const type = undefined;
|
||||
const side = this.safeString (trade, 'maker_type');
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (amount !== undefined) {
|
||||
if (price !== undefined) {
|
||||
cost = amount * price;
|
||||
}
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': undefined,
|
||||
'type': type,
|
||||
'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 request = {
|
||||
'currency': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
const response = await this.publicGetTransactions (this.extend (request, params));
|
||||
const trades = this.toArray (this.omit (response['transactions'], 'request_currency'));
|
||||
return this.parseTrades (trades, market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'side': side,
|
||||
'type': type,
|
||||
'currency': this.marketId (symbol),
|
||||
'amount': amount,
|
||||
};
|
||||
if (type === 'limit') {
|
||||
request['limit_price'] = price;
|
||||
}
|
||||
const result = await this.privatePostOrdersNew (this.extend (request, params));
|
||||
return {
|
||||
'info': result,
|
||||
'id': result,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
return await this.privatePostOrdersCancel (this.extend (request, params));
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'currency': currency['id'],
|
||||
'amount': parseFloat (amount),
|
||||
'address': address,
|
||||
};
|
||||
const response = await this.privatePostWithdrawalsNew (this.extend (request, params));
|
||||
return {
|
||||
'info': response,
|
||||
'id': response['result']['uuid'],
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
if (this.id === 'cryptocapital') {
|
||||
throw new ExchangeError (this.id + ' is an abstract base API for _1btcxe');
|
||||
}
|
||||
let url = this.urls['api'] + '/' + path;
|
||||
if (api === 'public') {
|
||||
if (Object.keys (params).length) {
|
||||
url += '?' + this.urlencode (params);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
const query = this.extend ({
|
||||
'api_key': this.apiKey,
|
||||
'nonce': this.nonce (),
|
||||
}, params);
|
||||
const request = this.json (query);
|
||||
query['signature'] = this.hmac (this.encode (request), this.encode (this.secret));
|
||||
body = this.json (query);
|
||||
headers = { 'Content-Type': 'application/json' };
|
||||
}
|
||||
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 (typeof response === 'string') {
|
||||
if (response.indexOf ('Maintenance') >= 0) {
|
||||
throw new ExchangeNotAvailable (this.id + ' on maintenance');
|
||||
}
|
||||
}
|
||||
if ('errors' in response) {
|
||||
let errors = [];
|
||||
for (let e = 0; e < response['errors'].length; e++) {
|
||||
const error = response['errors'][e];
|
||||
errors.push (error['code'] + ': ' + error['message']);
|
||||
}
|
||||
errors = errors.join (' ');
|
||||
throw new ExchangeError (this.id + ' ' + errors);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
+468
@@ -0,0 +1,468 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { InsufficientFunds, OrderNotFound } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class acx extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'acx',
|
||||
'name': 'ACX',
|
||||
'countries': [ 'AU' ],
|
||||
'rateLimit': 1000,
|
||||
'version': 'v2',
|
||||
'has': {
|
||||
'CORS': true,
|
||||
'fetchTickers': true,
|
||||
'fetchOHLCV': true,
|
||||
'withdraw': true,
|
||||
'fetchOrder': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': '1',
|
||||
'5m': '5',
|
||||
'15m': '15',
|
||||
'30m': '30',
|
||||
'1h': '60',
|
||||
'2h': '120',
|
||||
'4h': '240',
|
||||
'12h': '720',
|
||||
'1d': '1440',
|
||||
'3d': '4320',
|
||||
'1w': '10080',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg',
|
||||
'extension': '.json',
|
||||
'api': 'https://acx.io/api',
|
||||
'www': 'https://acx.io',
|
||||
'doc': 'https://acx.io/documents/api_v2',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'depth', // Get depth or specified market Both asks and bids are sorted from highest price to lowest.
|
||||
'k_with_pending_trades', // Get K data with pending trades, which are the trades not included in K data yet, because there's delay between trade generated and processed by K data generator
|
||||
'k', // Get OHLC(k line) of specific market
|
||||
'markets', // Get all available markets
|
||||
'order_book', // Get the order book of specified market
|
||||
'order_book/{market}',
|
||||
'tickers', // Get ticker of all markets
|
||||
'tickers/{market}', // Get ticker of specific market
|
||||
'timestamp', // Get server current time, in seconds since Unix epoch
|
||||
'trades', // Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order.
|
||||
'trades/{market}',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'members/me', // Get your profile and accounts info
|
||||
'deposits', // Get your deposits history
|
||||
'deposit', // Get details of specific deposit
|
||||
'deposit_address', // Where to deposit The address field could be empty when a new address is generating (e.g. for bitcoin), you should try again later in that case.
|
||||
'orders', // Get your orders, results is paginated
|
||||
'order', // Get information of specified order
|
||||
'trades/my', // Get your executed trades Trades are sorted in reverse creation order.
|
||||
'withdraws', // Get your cryptocurrency withdraws
|
||||
'withdraw', // Get your cryptocurrency withdraw
|
||||
],
|
||||
'post': [
|
||||
'orders', // Create a Sell/Buy order
|
||||
'orders/multi', // Create multiple sell/buy orders
|
||||
'orders/clear', // Cancel all my orders
|
||||
'order/delete', // Cancel an order
|
||||
'withdraw', // Create a withdraw
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'maker': 0.2 / 100,
|
||||
'taker': 0.2 / 100,
|
||||
},
|
||||
'funding': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'withdraw': {}, // There is only 1% fee on withdrawals to your bank account.
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'PLA': 'Plair',
|
||||
},
|
||||
'exceptions': {
|
||||
'2002': InsufficientFunds,
|
||||
'2003': OrderNotFound,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const markets = await this.publicGetMarkets (params);
|
||||
const result = [];
|
||||
for (let i = 0; i < markets.length; i++) {
|
||||
const market = markets[i];
|
||||
const id = market['id'];
|
||||
const symbol = market['name'];
|
||||
let baseId = this.safeString (market, 'base_unit');
|
||||
let quoteId = this.safeString (market, 'quote_unit');
|
||||
if ((baseId === undefined) || (quoteId === undefined)) {
|
||||
const ids = symbol.split ('/');
|
||||
baseId = ids[0].toLowerCase ();
|
||||
quoteId = ids[1].toLowerCase ();
|
||||
}
|
||||
let base = baseId.toUpperCase ();
|
||||
let quote = quoteId.toUpperCase ();
|
||||
base = this.safeCurrencyCode (base);
|
||||
quote = this.safeCurrencyCode (quote);
|
||||
// todo: find out their undocumented precision and limits
|
||||
const precision = {
|
||||
'amount': 8,
|
||||
'price': 8,
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'precision': precision,
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetMembersMe (params);
|
||||
const balances = this.safeValue (response, 'accounts');
|
||||
const result = { 'info': 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['free'] = this.safeFloat (balance, 'balance');
|
||||
account['used'] = this.safeFloat (balance, 'locked');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit; // default = 300
|
||||
}
|
||||
const orderbook = await this.publicGetDepth (this.extend (request, params));
|
||||
const timestamp = this.safeTimestamp (orderbook, 'timestamp');
|
||||
return this.parseOrderBook (orderbook, timestamp);
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
const timestamp = this.safeTimestamp (ticker, 'at');
|
||||
ticker = ticker['ticker'];
|
||||
let symbol = undefined;
|
||||
if (market) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'high'),
|
||||
'low': this.safeFloat (ticker, 'low'),
|
||||
'bid': this.safeFloat (ticker, 'buy'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'sell'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'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,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.publicGetTickers (params);
|
||||
const ids = Object.keys (response);
|
||||
const result = {};
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
let market = undefined;
|
||||
let symbol = id;
|
||||
if (id in this.markets_by_id) {
|
||||
market = this.markets_by_id[id];
|
||||
symbol = market['symbol'];
|
||||
} else {
|
||||
let base = id.slice (0, 3);
|
||||
let quote = id.slice (3, 6);
|
||||
base = base.toUpperCase ();
|
||||
quote = quote.toUpperCase ();
|
||||
base = this.safeCurrencyCode (base);
|
||||
quote = this.safeCurrencyCode (quote);
|
||||
symbol = base + '/' + quote;
|
||||
}
|
||||
result[symbol] = this.parseTicker (response[id], market);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.publicGetTickersMarket (this.extend (request, params));
|
||||
return this.parseTicker (response, market);
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.parse8601 (this.safeString (trade, 'created_at'));
|
||||
const id = this.safeString (trade, 'tid');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': undefined,
|
||||
'order': undefined,
|
||||
'takerOrMaker': undefined,
|
||||
'price': this.safeFloat (trade, 'price'),
|
||||
'amount': this.safeFloat (trade, 'volume'),
|
||||
'cost': this.safeFloat (trade, 'funds'),
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.publicGetTrades (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
ohlcv[0] * 1000,
|
||||
ohlcv[1],
|
||||
ohlcv[2],
|
||||
ohlcv[3],
|
||||
ohlcv[4],
|
||||
ohlcv[5],
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
if (limit === undefined) {
|
||||
limit = 500; // default is 30
|
||||
}
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
'period': this.timeframes[timeframe],
|
||||
'limit': limit,
|
||||
};
|
||||
if (since !== undefined) {
|
||||
request['timestamp'] = parseInt (since / 1000);
|
||||
}
|
||||
const response = await this.publicGetK (this.extend (request, params));
|
||||
return this.parseOHLCVs (response, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'done': 'closed',
|
||||
'wait': 'open',
|
||||
'cancel': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
} else {
|
||||
const marketId = this.safeString (order, 'market');
|
||||
symbol = this.markets_by_id[marketId]['symbol'];
|
||||
}
|
||||
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'state'));
|
||||
const type = this.safeString (order, 'type');
|
||||
const side = this.safeString (order, 'side');
|
||||
const id = this.safeString (order, 'id');
|
||||
return {
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': this.safeFloat (order, 'price'),
|
||||
'amount': this.safeFloat (order, 'volume'),
|
||||
'filled': this.safeFloat (order, 'executed_volume'),
|
||||
'remaining': this.safeFloat (order, 'remaining_volume'),
|
||||
'trades': undefined,
|
||||
'fee': undefined,
|
||||
'info': order,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': parseInt (id),
|
||||
};
|
||||
const response = await this.privateGetOrder (this.extend (request, params));
|
||||
return this.parseOrder (response);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'market': this.marketId (symbol),
|
||||
'side': side,
|
||||
'volume': amount.toString (),
|
||||
'ord_type': type,
|
||||
};
|
||||
if (type === 'limit') {
|
||||
request['price'] = price.toString ();
|
||||
}
|
||||
const response = await this.privatePostOrders (this.extend (request, params));
|
||||
const marketId = this.safeValue (response, 'market');
|
||||
const market = this.safeValue (this.markets_by_id, marketId);
|
||||
return this.parseOrder (response, market);
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
const response = await this.privatePostOrderDelete (this.extend (request, params));
|
||||
const order = this.parseOrder (response);
|
||||
const status = order['status'];
|
||||
if (status === 'closed' || status === 'canceled') {
|
||||
throw new OrderNotFound (this.id + ' ' + this.json (order));
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
// they have XRP but no docs on memo/tag
|
||||
const request = {
|
||||
'currency': currency['id'],
|
||||
'sum': amount,
|
||||
'address': address,
|
||||
};
|
||||
const response = await this.privatePostWithdraw (this.extend (request, params));
|
||||
// withdrawal response is undocumented
|
||||
return {
|
||||
'info': response,
|
||||
'id': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
nonce () {
|
||||
return this.milliseconds ();
|
||||
}
|
||||
|
||||
encodeParams (params) {
|
||||
if ('orders' in params) {
|
||||
const orders = params['orders'];
|
||||
let query = this.urlencode (this.keysort (this.omit (params, 'orders')));
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
const order = orders[i];
|
||||
const keys = Object.keys (order);
|
||||
for (let k = 0; k < keys.length; k++) {
|
||||
const key = keys[k];
|
||||
const value = order[key];
|
||||
query += '&orders%5B%5D%5B' + key + '%5D=' + value.toString ();
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
return this.urlencode (this.keysort (params));
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let request = '/api/' + this.version + '/' + this.implodeParams (path, params);
|
||||
if ('extension' in this.urls) {
|
||||
request += this.urls['extension'];
|
||||
}
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
let url = this.urls['api'] + request;
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ().toString ();
|
||||
const query = this.encodeParams (this.extend ({
|
||||
'access_key': this.apiKey,
|
||||
'tonce': nonce,
|
||||
}, params));
|
||||
const auth = method + '|' + request + '|' + query;
|
||||
const signed = this.hmac (this.encode (auth), this.encode (this.secret));
|
||||
const suffix = query + '&signature=' + signed;
|
||||
if (method === 'GET') {
|
||||
url += '?' + suffix;
|
||||
} else {
|
||||
body = suffix;
|
||||
headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
}
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
|
||||
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if (code === 400) {
|
||||
const error = this.safeValue (response, 'error');
|
||||
const errorCode = this.safeString (error, 'code');
|
||||
const feedback = this.id + ' ' + this.json (response);
|
||||
this.throwExactlyMatchedException (this.exceptions, errorCode, feedback);
|
||||
// fallback to default error handler
|
||||
}
|
||||
}
|
||||
};
|
||||
+1254
File diff suppressed because it is too large.
Load diff
+1216
File diff suppressed because it is too large.
Load diff
+43
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "script",
|
||||
},
|
||||
"rules": {
|
||||
"semi": "off",
|
||||
"no-unused-vars": ["off"],
|
||||
"quotes": ["off", "single"],
|
||||
"quote-props": "off",
|
||||
"func-call-spacing": ["error", "always"],
|
||||
"one-var": "off",
|
||||
"indent": ["warn", 4, { "ignoredNodes": ["ObjectPattern"] }],
|
||||
"comma-style": "off",
|
||||
"no-multi-spaces": "off",
|
||||
"comma-dangle": "off",
|
||||
"spaced-comment": "off",
|
||||
"camelcase": "off",
|
||||
"padded-blocks": "off",
|
||||
"multiline-comment-style": "off",
|
||||
"curly": "off",
|
||||
"rest-spread-spacing": "off",
|
||||
"no-multiple-empty-lines": "off",
|
||||
"no-undef-init": "off",
|
||||
"no-useless-return": "off",
|
||||
"no-console": "off",
|
||||
"operator-linebreak": "off",
|
||||
"key-spacing": "off",
|
||||
"brace-style": "off",
|
||||
"padding-line-between-statements": ["off",
|
||||
{"blankLine": "never", "prev":"*", "next": "*" },
|
||||
{"blankLine": "always", "prev":"directive", "next": "*" },
|
||||
{"blankLine": "always", "prev":"*", "next": "cjs-export" },
|
||||
],
|
||||
},
|
||||
}
|
||||
+1442
File diff suppressed because it is too large.
Load diff
+44
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
const errorHierarchy = {
|
||||
'BaseError': {
|
||||
'ExchangeError': {
|
||||
'AuthenticationError': {
|
||||
'PermissionDenied': {},
|
||||
'AccountSuspended': {},
|
||||
},
|
||||
'ArgumentsRequired': {},
|
||||
'BadRequest': {
|
||||
'BadSymbol': {},
|
||||
},
|
||||
'BadResponse': {
|
||||
'NullResponse': {},
|
||||
},
|
||||
'InsufficientFunds': {},
|
||||
'InvalidAddress': {
|
||||
'AddressPending': {},
|
||||
},
|
||||
'InvalidOrder': {
|
||||
'OrderNotFound': {},
|
||||
'OrderNotCached': {},
|
||||
'CancelPending': {},
|
||||
'OrderImmediatelyFillable': {},
|
||||
'OrderNotFillable': {},
|
||||
'DuplicateOrderId': {},
|
||||
},
|
||||
'NotSupported': {},
|
||||
},
|
||||
'NetworkError': {
|
||||
'DDoSProtection': {
|
||||
'RateLimitExceeded': {},
|
||||
},
|
||||
'ExchangeNotAvailable': {
|
||||
'OnMaintenance': {},
|
||||
},
|
||||
'InvalidNonce': {},
|
||||
'RequestTimeout': {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = errorHierarchy;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const errorHierarchy = require ('./errorHierarchy.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
function subclass (BaseClass, classes, namespace = {}) {
|
||||
|
||||
for (const [className, subclasses] of Object.entries (classes)) {
|
||||
|
||||
const Class = Object.assign (namespace, {
|
||||
|
||||
/* By creating a named property, we trick compiler to assign our class constructor function a name.
|
||||
Otherwise, all our error constructors would be shown as [Function: Error] in the debugger! And
|
||||
the super-useful `e.constructor.name` magic wouldn't work — we then would have no chance to
|
||||
obtain a error type string from an error instance programmatically! */
|
||||
|
||||
[className]: class extends BaseClass {
|
||||
|
||||
constructor (message) {
|
||||
|
||||
super (message)
|
||||
|
||||
/* A workaround to make `instanceof` work on custom Error classes in transpiled ES5.
|
||||
See my blog post for the explanation of this hack:
|
||||
|
||||
https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801 */
|
||||
|
||||
this.constructor = Class
|
||||
this.__proto__ = Class.prototype
|
||||
this.name = className
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
})[className]
|
||||
|
||||
subclass (Class, subclasses, namespace)
|
||||
}
|
||||
|
||||
return namespace
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = subclass (
|
||||
// Root class
|
||||
Error,
|
||||
// Derived class hierarchy
|
||||
errorHierarchy
|
||||
)
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { unCamelCase } = require ('./functions/string')
|
||||
|
||||
const unCamelCasePropertyNames = x => {
|
||||
for (const k in x) x[unCamelCase (k)] = x[k] // camel_case_method = camelCaseMethod
|
||||
return x
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = unCamelCasePropertyNames (Object.assign ({}
|
||||
|
||||
, require ('./functions/platform')
|
||||
, require ('./functions/generic')
|
||||
, require ('./functions/string')
|
||||
, require ('./functions/type')
|
||||
, require ('./functions/number')
|
||||
, require ('./functions/encode')
|
||||
, require ('./functions/crypto')
|
||||
, require ('./functions/time')
|
||||
, require ('./functions/throttle')
|
||||
, require ('./functions/misc')
|
||||
))
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js')
|
||||
const { capitalize } = require ('./string')
|
||||
const { stringToBase64, utf16ToBase64, urlencodeBase64 } = require ('./encode')
|
||||
const NodeRSA = require ('./../../static_dependencies/node-rsa/NodeRSA')
|
||||
const { numberToLE } = require ('./encode')
|
||||
const EC = require ('./../../static_dependencies/elliptic/lib/elliptic').ec
|
||||
const { ArgumentsRequired } = require ('./../errors')
|
||||
const BN = require ('../../static_dependencies/BN/bn.js')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const hash = (request, hash = 'md5', digest = 'hex') => {
|
||||
const options = {}
|
||||
if (hash === 'keccak') {
|
||||
hash = 'SHA3'
|
||||
options['outputLength'] = 256
|
||||
}
|
||||
const result = CryptoJS[hash.toUpperCase ()] (request, options)
|
||||
return (digest === 'binary') ? result : result.toString (CryptoJS.enc[capitalize (digest)])
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
const hmac = (request, secret, hash = 'sha256', digest = 'hex') => {
|
||||
const result = CryptoJS['Hmac' + hash.toUpperCase ()] (request, secret)
|
||||
if (digest) {
|
||||
const encoding = (digest === 'binary') ? 'Latin1' : capitalize (digest)
|
||||
return result.toString (CryptoJS.enc[capitalize (encoding)])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
function rsa (request, secret, alg = 'RS256') {
|
||||
const algos = {
|
||||
'RS256': 'pkcs1-sha256',
|
||||
'RS512': 'pkcs1-sha512',
|
||||
}
|
||||
if (!(alg in algos)) {
|
||||
throw new ExchangeError (alg + ' is not a supported rsa signing algorithm.')
|
||||
}
|
||||
const algorithm = algos[alg]
|
||||
let key = new NodeRSA (secret, {
|
||||
'environment': 'browser',
|
||||
'signingScheme': algorithm,
|
||||
})
|
||||
return key.sign (request, 'base64', 'binary')
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
function jwt (request, secret, alg = 'HS256') {
|
||||
const algos = {
|
||||
'HS256': 'sha256',
|
||||
'HS384': 'sha384',
|
||||
'HS512': 'sha512',
|
||||
};
|
||||
const encodedHeader = urlencodeBase64 (stringToBase64 (JSON.stringify ({ 'alg': alg, 'typ': 'JWT' })))
|
||||
const encodedData = urlencodeBase64 (stringToBase64 (JSON.stringify (request)))
|
||||
const token = [ encodedHeader, encodedData ].join ('.')
|
||||
const algoType = alg.slice (0, 2);
|
||||
const algorithm = algos[alg]
|
||||
let signature = undefined
|
||||
if (algoType === 'HS') {
|
||||
signature = urlencodeBase64 (hmac (token, secret, algorithm, 'base64'))
|
||||
} else if (algoType === 'RS') {
|
||||
signature = urlencodeBase64 (rsa (token, secret, alg))
|
||||
}
|
||||
return [ token, signature ].join ('.')
|
||||
}
|
||||
|
||||
function ecdsa (request, secret, algorithm = 'p256', hashFunction = undefined, fixedLength = false) {
|
||||
let digest = request
|
||||
if (hashFunction !== undefined) {
|
||||
digest = hash (request, hashFunction, 'hex')
|
||||
}
|
||||
const curve = new EC (algorithm)
|
||||
let signature = curve.sign (digest, secret, 'hex', { 'canonical': true })
|
||||
let counter = new BN ('0')
|
||||
const minimum_size = new BN ('1').shln (8 * 31).sub (new BN ('1'))
|
||||
while (fixedLength && (signature.r.gt (curve.nh) || signature.r.lte (minimum_size) || signature.s.lte (minimum_size))) {
|
||||
signature = curve.sign (digest, secret, 'hex', { 'canonical': true, 'extraEntropy': counter.toArray ('le', 32)})
|
||||
counter = counter.add (new BN ('1'))
|
||||
}
|
||||
return {
|
||||
'r': signature.r.toString (16).padStart (64, '0'),
|
||||
's': signature.s.toString (16).padStart (64, '0'),
|
||||
'v': signature.recoveryParam,
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const totp = (secret) => {
|
||||
|
||||
const dec2hex = s => ((s < 15.5 ? '0' : '') + Math.round (s).toString (16))
|
||||
, hex2dec = s => parseInt (s, 16)
|
||||
, leftpad = (s, p) => (p + s).slice (-p.length) // both s and p are short strings
|
||||
|
||||
const base32tohex = (base32) => {
|
||||
let base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
|
||||
let bits = ''
|
||||
let hex = ''
|
||||
for (let i = 0; i < base32.length; i++) {
|
||||
let val = base32chars.indexOf (base32.charAt (i).toUpperCase ())
|
||||
bits += leftpad (val.toString (2), '00000')
|
||||
}
|
||||
for (let i = 0; i + 4 <= bits.length; i += 4) {
|
||||
let chunk = bits.substr (i, 4)
|
||||
hex = hex + parseInt (chunk, 2).toString (16)
|
||||
}
|
||||
return hex
|
||||
}
|
||||
|
||||
const getOTP = (secret) => {
|
||||
secret = secret.replace (' ', '') // support 2fa-secrets with spaces like "4TDV WOGO" → "4TDVWOGO"
|
||||
let epoch = Math.round (new Date ().getTime () / 1000.0)
|
||||
let time = leftpad (dec2hex (Math.floor (epoch / 30)), '0000000000000000')
|
||||
let hmacRes = hmac (CryptoJS.enc.Hex.parse (time), CryptoJS.enc.Hex.parse (base32tohex (secret)), 'sha1', 'hex')
|
||||
let offset = hex2dec (hmacRes.substring (hmacRes.length - 1))
|
||||
let otp = (hex2dec (hmacRes.substr (offset * 2, 8)) & hex2dec ('7fffffff')) + ''
|
||||
otp = (otp).substr (otp.length - 6, 6)
|
||||
return otp
|
||||
}
|
||||
|
||||
return getOTP (secret)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = {
|
||||
hash,
|
||||
hmac,
|
||||
jwt,
|
||||
totp,
|
||||
rsa,
|
||||
ecdsa,
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const CryptoJS = require ('../../static_dependencies/crypto-js/crypto-js')
|
||||
const qs = require ('../../static_dependencies/qs/index')
|
||||
const BN = require ('../../static_dependencies/BN/bn')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports =
|
||||
|
||||
{ json: (data, params = undefined) => JSON.stringify (data)
|
||||
, unjson: JSON.parse
|
||||
|
||||
, isJsonEncodedObject: object => (
|
||||
(typeof object === 'string') &&
|
||||
(object.length >= 2) &&
|
||||
((object[0] === '{') || (object[0] === '['))
|
||||
)
|
||||
|
||||
, stringToBinary (str) {
|
||||
const arr = new Uint8Array (str.length)
|
||||
for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt (i); }
|
||||
return CryptoJS.lib.WordArray.create (arr)
|
||||
}
|
||||
|
||||
, stringToBase64: string => CryptoJS.enc.Latin1.parse (string).toString (CryptoJS.enc.Base64)
|
||||
, utf16ToBase64: string => CryptoJS.enc.Utf16 .parse (string).toString (CryptoJS.enc.Base64)
|
||||
, base64ToBinary: string => CryptoJS.enc.Base64.parse (string)
|
||||
, base64ToString: string => CryptoJS.enc.Base64.parse (string).toString (CryptoJS.enc.Utf8)
|
||||
, binaryToBase64: binary => binary.toString (CryptoJS.enc.Base64)
|
||||
, base16ToBinary: string => CryptoJS.enc.Hex.parse (string)
|
||||
, binaryToBase16: binary => binary.toString (CryptoJS.enc.Hex)
|
||||
, binaryConcat: (...args) => args.reduce ((a, b) => a.concat (b))
|
||||
, binaryConcatArray: (arr) => arr.reduce ((a, b) => a.concat (b))
|
||||
|
||||
, urlencode: object => qs.stringify (object)
|
||||
, urlencodeWithArrayRepeat: object => qs.stringify (object, { arrayFormat: 'repeat' })
|
||||
, rawencode: object => qs.stringify (object, { encode: false })
|
||||
, encode: x => x
|
||||
, decode: x => x
|
||||
|
||||
// Url-safe-base64 without equals signs, with + replaced by - and slashes replaced by underscores
|
||||
|
||||
, urlencodeBase64: base64string => base64string.replace (/[=]+$/, '')
|
||||
.replace (/\+/g, '-')
|
||||
.replace (/\//g, '_')
|
||||
|
||||
, numberToLE: (n, padding) => {
|
||||
const hexArray = new BN (n).toArray ('le', padding)
|
||||
return byteArrayToWordArray (hexArray)
|
||||
}
|
||||
|
||||
, numberToBE: (n, padding) => {
|
||||
const hexArray = new BN (n).toArray ('be', padding)
|
||||
return byteArrayToWordArray (hexArray)
|
||||
}
|
||||
}
|
||||
|
||||
function byteArrayToWordArray(ba) {
|
||||
const wa = []
|
||||
for (let i = 0; i < ba.length; i++) {
|
||||
wa[(i / 4) | 0] |= ba[i] << (24 - 8 * i)
|
||||
}
|
||||
return CryptoJS.lib.WordArray.create (wa, ba.length)
|
||||
}
|
||||
|
||||
module.exports['byteArrayToWordArray'] = byteArrayToWordArray
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
"use strict";
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { isObject, isNumber, isDictionary, isArray } = require ('./type')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const keys = Object.keys
|
||||
|
||||
, values = x => !isArray (x) // don't copy arrays if they're already arrays!
|
||||
? Object.values (x)
|
||||
: x
|
||||
|
||||
, index = x => new Set (values (x))
|
||||
|
||||
, extend = (...args) => Object.assign ({}, ...args) // NB: side-effect free
|
||||
|
||||
, clone = x => isArray (x)
|
||||
? Array.from (x) // clones arrays
|
||||
: extend (x) // clones objects
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports =
|
||||
|
||||
{ keys
|
||||
, values
|
||||
, extend
|
||||
, clone
|
||||
, 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)
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, inArray (needle, haystack) {
|
||||
|
||||
return haystack.includes (needle)
|
||||
}
|
||||
|
||||
, toArray (object) {
|
||||
|
||||
return Object.values (object)
|
||||
}
|
||||
|
||||
, isEmpty (object) {
|
||||
if (!object)
|
||||
return true;
|
||||
return (Array.isArray (object) ? object : Object.keys (object)).length < 1;
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, keysort (x, out = {}) {
|
||||
|
||||
for (const k of keys (x).sort ())
|
||||
out[k] = x[k]
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
/*
|
||||
Accepts a map/array of objects and a key name to be used as an index:
|
||||
array = [
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
{ someKey: 'value2', anotherKey: 'anotherValue2' },
|
||||
{ someKey: 'value3', anotherKey: 'anotherValue3' },
|
||||
]
|
||||
key = 'someKey'
|
||||
|
||||
Returns a map:
|
||||
{
|
||||
value1: { someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
value2: { someKey: 'value2', anotherKey: 'anotherValue2' },
|
||||
value3: { someKey: 'value3', anotherKey: 'anotherValue3' },
|
||||
}
|
||||
*/
|
||||
|
||||
, indexBy (x, k, out = {}) {
|
||||
|
||||
for (const v of values (x))
|
||||
if (k in v)
|
||||
out[v[k]] = v
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
/*
|
||||
Accepts a map/array of objects and a key name to be used as a grouping parameter:
|
||||
array = [
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue2' },
|
||||
{ someKey: 'value3', anotherKey: 'anotherValue3' },
|
||||
]
|
||||
key = 'someKey'
|
||||
|
||||
Returns a map:
|
||||
{
|
||||
value1: [
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue2' },
|
||||
]
|
||||
value3: [
|
||||
{ someKey: 'value3', anotherKey: 'anotherValue3' }
|
||||
],
|
||||
}
|
||||
*/
|
||||
|
||||
, groupBy (x, k, out = {}) {
|
||||
|
||||
for (const v of values (x)) {
|
||||
if (k in v) {
|
||||
const p = v[k]
|
||||
out[p] = out[p] || []
|
||||
out[p].push (v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
/*
|
||||
Accepts a map/array of objects, a key name and a key value to be used as a filter:
|
||||
array = [
|
||||
{ someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
{ someKey: 'value2', anotherKey: 'anotherValue2' },
|
||||
{ someKey: 'value3', anotherKey: 'anotherValue3' },
|
||||
]
|
||||
key = 'someKey'
|
||||
value = 'value1'
|
||||
|
||||
Returns an array:
|
||||
[
|
||||
value1: { someKey: 'value1', anotherKey: 'anotherValue1' },
|
||||
]
|
||||
*/
|
||||
|
||||
, filterBy (x, k, value = undefined, out = []) {
|
||||
|
||||
for (const v of values (x))
|
||||
if (v[k] === value)
|
||||
out.push (v)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, sortBy: (array, // NB: MUTATES ARRAY!
|
||||
key,
|
||||
descending = false,
|
||||
direction = descending ? -1 : 1) => array.sort ((a, b) =>
|
||||
((a[key] < b[key]) ? -direction :
|
||||
((a[key] > b[key]) ? direction : 0)))
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, flatten: function flatten (x, out = []) {
|
||||
|
||||
for (const v of x) {
|
||||
if (isArray (v)) flatten (v, out)
|
||||
else out.push (v)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, pluck: (x, k) => values (x)
|
||||
.filter (v => k in v)
|
||||
.map (v => v[k])
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, omit (x, ...args) {
|
||||
|
||||
if (!Array.isArray (x)) {
|
||||
|
||||
const out = clone (x)
|
||||
|
||||
for (const k of args) {
|
||||
|
||||
if (isArray (k)) { // omit (x, ['a', 'b'])
|
||||
|
||||
for (const kk of k) {
|
||||
delete out[kk]
|
||||
}
|
||||
}
|
||||
|
||||
else delete out[k] // omit (x, 'a', 'b')
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, sum (...xs) {
|
||||
|
||||
const ns = xs.filter (isNumber) // leave only numbers
|
||||
|
||||
return (ns.length > 0)
|
||||
? ns.reduce ((a, b) => a + b, 0)
|
||||
: undefined
|
||||
}
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
, deepExtend: function deepExtend (...xs) {
|
||||
|
||||
let out = undefined
|
||||
|
||||
for (const x of xs) {
|
||||
|
||||
if (isDictionary (x)) {
|
||||
|
||||
if (!isObject (out))
|
||||
out = {}
|
||||
|
||||
for (const k in x)
|
||||
out[k] = deepExtend (out[k], x[k])
|
||||
|
||||
} else out = x
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
|
||||
const { ROUND_UP, ROUND_DOWN } = require ('./number')
|
||||
const { NotSupported } = require ('./../errors')
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// converts timeframe to seconds
|
||||
const parseTimeframe = (timeframe) => {
|
||||
|
||||
const amount = timeframe.slice (0, -1)
|
||||
const unit = timeframe.slice (-1)
|
||||
let scale = undefined;
|
||||
|
||||
if (unit === 'y') {
|
||||
scale = 60 * 60 * 24 * 365
|
||||
} else if (unit === 'M') {
|
||||
scale = 60 * 60 * 24 * 30
|
||||
} else if (unit === 'w') {
|
||||
scale = 60 * 60 * 24 * 7
|
||||
} else if (unit === 'd') {
|
||||
scale = 60 * 60 * 24
|
||||
} else if (unit === 'h') {
|
||||
scale = 60 * 60
|
||||
} else if (unit === 'm') {
|
||||
scale = 60
|
||||
} else if (unit === 's') {
|
||||
scale = 1
|
||||
} else {
|
||||
throw new NotSupported ('timeframe unit ' + unit + ' is not supported')
|
||||
}
|
||||
|
||||
return amount * scale
|
||||
}
|
||||
|
||||
const roundTimeframe = (timeframe, timestamp, direction = ROUND_DOWN) => {
|
||||
const ms = parseTimeframe (timeframe) * 1000
|
||||
// Get offset based on timeframe in milliseconds
|
||||
const offset = timestamp % ms
|
||||
return timestamp - offset + ((direction === ROUND_UP) ? ms : 0);
|
||||
}
|
||||
|
||||
// given a sorted arrays of trades (recent last) and a timeframe builds an array of OHLCV candles
|
||||
const buildOHLCVC = (trades, timeframe = '1m', since = -Infinity, limit = Infinity) => {
|
||||
const ms = parseTimeframe (timeframe) * 1000;
|
||||
const ohlcvs = [];
|
||||
const [ timestamp, /* open */, high, low, close, volume, count ] = [ 0, 1, 2, 3, 4, 5, 6 ];
|
||||
const oldest = Math.min (trades.length - 1, limit);
|
||||
|
||||
for (let i = 0; i <= oldest; i++) {
|
||||
const trade = trades[i];
|
||||
if (trade.timestamp < since)
|
||||
continue;
|
||||
const openingTime = Math.floor (trade.timestamp / ms) * ms; // shift to the edge of m/h/d (but not M)
|
||||
const candle = ohlcvs.length - 1;
|
||||
|
||||
if (candle === -1 || openingTime >= ohlcvs[candle][timestamp] + ms) {
|
||||
// moved to a new timeframe -> create a new candle from opening trade
|
||||
ohlcvs.push ([
|
||||
openingTime, // timestamp
|
||||
trade.price, // O
|
||||
trade.price, // H
|
||||
trade.price, // L
|
||||
trade.price, // C
|
||||
trade.amount, // V
|
||||
1, // count
|
||||
]);
|
||||
} else {
|
||||
// still processing the same timeframe -> update opening trade
|
||||
ohlcvs[candle][high] = Math.max (ohlcvs[candle][high], trade.price);
|
||||
ohlcvs[candle][low] = Math.min (ohlcvs[candle][low], trade.price);
|
||||
ohlcvs[candle][close] = trade.price;
|
||||
ohlcvs[candle][volume] += trade.amount;
|
||||
ohlcvs[candle][count]++;
|
||||
} // if
|
||||
} // for
|
||||
return ohlcvs;
|
||||
}
|
||||
|
||||
const extractParams = (string) => {
|
||||
const re = /{([\w-]+)}/g
|
||||
const matches = []
|
||||
let match = re.exec (string)
|
||||
while (match) {
|
||||
matches.push (match[1])
|
||||
match = re.exec (string)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
const implodeParams = (string, params) => {
|
||||
if (!Array.isArray (params)) {
|
||||
const keys = Object.keys (params)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (!Array.isArray (params[key])) {
|
||||
string = string.replace ('{' + key + '}', params[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = {
|
||||
|
||||
aggregate (bidasks) {
|
||||
|
||||
const result = {}
|
||||
|
||||
for (let i = 0; i < bidasks.length; i++) {
|
||||
const [ price, volume ] = bidasks[i];
|
||||
if (volume > 0) {
|
||||
result[price] = (result[price] || 0) + volume
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys (result).map (price => [parseFloat (price), parseFloat (result[price])])
|
||||
},
|
||||
|
||||
parseTimeframe,
|
||||
roundTimeframe,
|
||||
buildOHLCVC,
|
||||
ROUND_UP,
|
||||
ROUND_DOWN,
|
||||
|
||||
implodeParams,
|
||||
extractParams
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
'use strict'
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
|
||||
NB: initially, I used objects for options passing:
|
||||
|
||||
decimalToPrecision ('123.456', { digits: 2, round: true, afterPoint: true })
|
||||
|
||||
...but it turns out it's hard to port that across different languages and it is also
|
||||
probably has a performance penalty -- while it's a performance critical code! So
|
||||
I switched to using named constants instead, as it is actually more readable and
|
||||
succinct, and surely doesn't come with any inherent performance downside:
|
||||
|
||||
decimalToPrecision ('123.456', ROUND, 2, DECIMAL_PLACES) */
|
||||
|
||||
const ROUND = 0 // rounding mode
|
||||
, TRUNCATE = 1
|
||||
, ROUND_UP = 2
|
||||
, ROUND_DOWN = 3
|
||||
|
||||
const DECIMAL_PLACES = 0 // digits counting mode
|
||||
, SIGNIFICANT_DIGITS = 1
|
||||
, TICK_SIZE = 2
|
||||
|
||||
const NO_PADDING = 0 // zero-padding mode
|
||||
, PAD_WITH_ZERO = 1
|
||||
|
||||
const precisionConstants = {
|
||||
ROUND,
|
||||
TRUNCATE,
|
||||
ROUND_UP,
|
||||
ROUND_DOWN,
|
||||
DECIMAL_PLACES,
|
||||
SIGNIFICANT_DIGITS,
|
||||
TICK_SIZE,
|
||||
NO_PADDING,
|
||||
PAD_WITH_ZERO,
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
// See https://stackoverflow.com/questions/1685680/how-to-avoid-scientific-notation-for-large-numbers-in-javascript for discussion
|
||||
|
||||
function numberToString (x) { // avoids scientific notation for too large and too small numbers
|
||||
|
||||
if (typeof x === 'string') return x
|
||||
|
||||
if (Math.abs (x) < 1.0) {
|
||||
const s = x.toString ()
|
||||
const e = parseInt (s.split ('e-')[1])
|
||||
const neg = (s[0] === '-')
|
||||
if (e) {
|
||||
x *= Math.pow (10, e - 1)
|
||||
x = (neg ? '-' : '') + '0.' + (new Array (e)).join ('0') + x.toString ().substring (neg ? 3 : 2)
|
||||
}
|
||||
} else {
|
||||
let e = parseInt (x.toString ().split ('+')[1])
|
||||
if (e > 20) {
|
||||
e -= 20
|
||||
x /= Math.pow (10, e)
|
||||
x += (new Array (e + 1)).join ('0')
|
||||
}
|
||||
}
|
||||
return x.toString ()
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// expects non-scientific notation
|
||||
|
||||
const truncate_regExpCache = []
|
||||
, truncate_to_string = (num, precision = 0) => {
|
||||
num = numberToString (num)
|
||||
if (precision > 0) {
|
||||
const re = truncate_regExpCache[precision] || (truncate_regExpCache[precision] = new RegExp ("([-]*\\d+\\.\\d{" + precision + "})(\\d)"))
|
||||
const [ , result] = num.toString ().match (re) || [null, num]
|
||||
return result.toString ()
|
||||
}
|
||||
return parseInt (num).toString ()
|
||||
}
|
||||
, truncate = (num, precision = 0) => parseFloat (truncate_to_string (num, precision))
|
||||
|
||||
function precisionFromString (string) {
|
||||
const split = string.replace (/0+$/g, '').split ('.')
|
||||
return (split.length > 1) ? (split[1].length) : 0
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const decimalToPrecision = (x, roundingMode
|
||||
, numPrecisionDigits
|
||||
, countingMode = DECIMAL_PLACES
|
||||
, paddingMode = NO_PADDING) => {
|
||||
|
||||
if (numPrecisionDigits < 0) {
|
||||
if (countingMode === TICK_SIZE) {
|
||||
throw new Error (`TICK_SIZE cant be used with negative numPrecisionDigits`)
|
||||
}
|
||||
const toNearest = Math.pow (10, -numPrecisionDigits)
|
||||
if (roundingMode === ROUND) {
|
||||
return (toNearest * decimalToPrecision (x / toNearest, roundingMode, 0, countingMode, paddingMode)).toString ()
|
||||
}
|
||||
if (roundingMode === TRUNCATE) {
|
||||
return (x - (x % toNearest)).toString ()
|
||||
}
|
||||
}
|
||||
|
||||
/* handle tick size */
|
||||
if (countingMode === TICK_SIZE) {
|
||||
const precisionDigitsString = decimalToPrecision (numPrecisionDigits, ROUND, 100, DECIMAL_PLACES, NO_PADDING)
|
||||
const newNumPrecisionDigits = precisionFromString (precisionDigitsString)
|
||||
const missing = x % numPrecisionDigits
|
||||
// See: https://github.com/ccxt/ccxt/pull/6486
|
||||
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) {
|
||||
x = x - missing + numPrecisionDigits
|
||||
} else {
|
||||
x = x - missing
|
||||
}
|
||||
} else {
|
||||
if (missing >= numPrecisionDigits / 2) {
|
||||
x = Number (x) - missing
|
||||
} else {
|
||||
x = Number (x) - missing - numPrecisionDigits
|
||||
}
|
||||
}
|
||||
} else if (roundingMode === TRUNCATE) {
|
||||
x = x - missing
|
||||
}
|
||||
}
|
||||
return decimalToPrecision (x, ROUND, newNumPrecisionDigits, DECIMAL_PLACES, paddingMode);
|
||||
}
|
||||
|
||||
/* Convert to a string (if needed), skip leading minus sign (if any) */
|
||||
|
||||
const str = numberToString (x)
|
||||
, isNegative = str[0] === '-'
|
||||
, strStart = isNegative ? 1 : 0
|
||||
, strEnd = str.length
|
||||
|
||||
/* Find the dot position in the source buffer */
|
||||
|
||||
for (var strDot = 0; strDot < strEnd; strDot++)
|
||||
if (str[strDot] === '.')
|
||||
break
|
||||
|
||||
const hasDot = strDot < str.length
|
||||
|
||||
/* Char code constants */
|
||||
|
||||
const MINUS = 45
|
||||
, DOT = 46
|
||||
, ZERO = 48
|
||||
, ONE = (ZERO + 1)
|
||||
, FIVE = (ZERO + 5)
|
||||
, NINE = (ZERO + 9)
|
||||
|
||||
/* For -123.4567 the `chars` array will hold 01234567 (leading zero is reserved for rounding cases when 099 → 100) */
|
||||
|
||||
const chars = new Uint8Array ((strEnd - strStart) + (hasDot ? 0 : 1))
|
||||
chars[0] = ZERO
|
||||
|
||||
/* Validate & copy digits, determine certain locations in the resulting buffer */
|
||||
|
||||
let afterDot = chars.length
|
||||
, digitsStart = -1 // significant digits
|
||||
, digitsEnd = -1
|
||||
|
||||
for (var i = 1, j = strStart; j < strEnd; j++, i++) {
|
||||
|
||||
const c = str.charCodeAt (j)
|
||||
|
||||
if (c === DOT) {
|
||||
afterDot = i--
|
||||
|
||||
} else if ((c < ZERO) || (c > NINE)) {
|
||||
throw new Error (`${str}: invalid number (contains an illegal character '${str[i - 1]}')`)
|
||||
|
||||
} else {
|
||||
chars[i] = c
|
||||
if ((c !== ZERO) && (digitsStart < 0)) digitsStart = i
|
||||
}
|
||||
}
|
||||
|
||||
if (digitsStart < 0) digitsStart = 1
|
||||
|
||||
/* Determine the range to cut */
|
||||
|
||||
let precisionStart = (countingMode === DECIMAL_PLACES) ? afterDot // 0.(0)001234567
|
||||
: digitsStart // 0.00(1)234567
|
||||
, precisionEnd = precisionStart +
|
||||
numPrecisionDigits
|
||||
|
||||
/* Reset the last significant digit index, as it will change during the rounding/truncation. */
|
||||
|
||||
digitsEnd = -1
|
||||
|
||||
/* Perform rounding/truncation per digit, from digitsEnd to digitsStart, by using the following
|
||||
algorithm (rounding 999 → 1000, as an example):
|
||||
|
||||
step = i=3 i=2 i=1 i=0
|
||||
|
||||
chars = 0999 0999 0900 1000
|
||||
memo = ---0 --1- -1-- 0--- */
|
||||
|
||||
let allZeros = true;
|
||||
let signNeeded = isNegative;
|
||||
|
||||
for (let i = chars.length - 1, memo = 0; i >= 0; i--) {
|
||||
|
||||
let c = chars[i]
|
||||
|
||||
if (i !== 0) {
|
||||
c += memo
|
||||
|
||||
if (i >= (precisionStart + numPrecisionDigits)) {
|
||||
|
||||
const ceil = (roundingMode === ROUND) &&
|
||||
(c >= FIVE) &&
|
||||
!((c === FIVE) && memo) // prevents rounding of 1.45 to 2
|
||||
|
||||
c = ceil ? (NINE + 1) : ZERO
|
||||
}
|
||||
if (c > NINE) { c = ZERO; memo = 1; }
|
||||
else memo = 0
|
||||
|
||||
} else if (memo) c = ONE // leading extra digit (0900 → 1000)
|
||||
|
||||
chars[i] = c
|
||||
|
||||
if (c !== ZERO) {
|
||||
allZeros = false
|
||||
digitsStart = i
|
||||
digitsEnd = (digitsEnd < 0) ? (i + 1) : digitsEnd
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the precision range, as `digitsStart` may have changed... & the need for a negative sign if it is only 0 */
|
||||
|
||||
if (countingMode === SIGNIFICANT_DIGITS) {
|
||||
precisionStart = digitsStart
|
||||
precisionEnd = precisionStart + numPrecisionDigits
|
||||
}
|
||||
|
||||
if (allZeros) {
|
||||
signNeeded = false
|
||||
}
|
||||
|
||||
/* Determine the input character range */
|
||||
|
||||
const readStart = ((digitsStart >= afterDot) || allZeros) ? (afterDot - 1) : digitsStart // 0.000(1)234 ----> (0).0001234
|
||||
, readEnd = (digitsEnd < afterDot) ? (afterDot ) : digitsEnd // 12(3)000 ----> 123000( )
|
||||
|
||||
/* Compute various sub-ranges */
|
||||
|
||||
const nSign = (signNeeded ? 1 : 0) // (-)123.456
|
||||
, nBeforeDot = (nSign + (afterDot - readStart)) // (-123).456
|
||||
, nAfterDot = Math.max (readEnd - afterDot, 0) // -123.(456)
|
||||
, actualLength = (readEnd - readStart) // -(123.456)
|
||||
, desiredLength = (paddingMode === NO_PADDING)
|
||||
? (actualLength) // -(123.456)
|
||||
: (precisionEnd - readStart) // -(123.456 )
|
||||
|
||||
, pad = Math.max (desiredLength - actualLength, 0) // -123.456( )
|
||||
, padStart = (nBeforeDot + 1 + nAfterDot) // -123.456( )
|
||||
, padEnd = (padStart + pad) // -123.456 ( )
|
||||
, isInteger = (nAfterDot + pad) === 0 // -123
|
||||
|
||||
/* Fill the output buffer with characters */
|
||||
|
||||
const out = new Uint8Array (nBeforeDot + (isInteger ? 0 : 1) + nAfterDot + pad)
|
||||
// ------------------------------------------------------------------------------------------ // ---------------------
|
||||
if (signNeeded) out[0] = MINUS // - minus sign
|
||||
for (i = nSign, j = readStart; i < nBeforeDot; i++, j++) out[i] = chars[j] // 123 before dot
|
||||
if (!isInteger) out[nBeforeDot] = DOT // . dot
|
||||
for (i = nBeforeDot + 1, j = afterDot; i < padStart; i++, j++) out[i] = chars[j] // 456 after dot
|
||||
for (i = padStart; i < padEnd; i++) out[i] = ZERO // 000 padding
|
||||
|
||||
/* Build a string from the output buffer */
|
||||
|
||||
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,
|
||||
truncate_to_string,
|
||||
truncate,
|
||||
precisionConstants,
|
||||
ROUND,
|
||||
TRUNCATE,
|
||||
ROUND_UP,
|
||||
ROUND_DOWN,
|
||||
DECIMAL_PLACES,
|
||||
SIGNIFICANT_DIGITS,
|
||||
TICK_SIZE,
|
||||
NO_PADDING,
|
||||
PAD_WITH_ZERO,
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const updateCachedOrders = function (openOrders, symbol) {
|
||||
}
|
||||
|
||||
const updateOrders = function (oldOrders, newOrders, closeOpenOrders = false) {
|
||||
|
||||
for (let i = 0; i < newOrders.length; i++) {
|
||||
let newOrder = newOrders[i];
|
||||
}
|
||||
|
||||
// update local cache with open orders
|
||||
for (let j = 0; j < openOrders.length; j++) {
|
||||
const id = openOrders[j]['id'];
|
||||
this.orders[id] = openOrders[j];
|
||||
}
|
||||
let openOrdersIndexedById = this.indexBy (openOrders, 'id');
|
||||
let cachedOrderIds = Object.keys (this.orders);
|
||||
let result = [];
|
||||
for (let k = 0; k < cachedOrderIds.length; k++) {
|
||||
// match each cached order to an order in the open orders array
|
||||
// possible reasons why a cached order may be missing in the open orders array:
|
||||
// - order was closed or canceled -> update cache
|
||||
// - symbol mismatch (e.g. cached BTC/USDT, fetched ETH/USDT) -> skip
|
||||
let id = cachedOrderIds[k];
|
||||
let order = this.orders[id];
|
||||
result.push (order);
|
||||
if (!(id in openOrdersIndexedById)) {
|
||||
// cached order is not in open orders array
|
||||
// if we fetched orders by symbol and it doesn't match the cached order -> won't update the cached order
|
||||
if (symbol !== undefined && symbol !== order['symbol'])
|
||||
continue;
|
||||
// order is cached but not present in the list of open orders -> mark the cached order as closed
|
||||
if (order['status'] === 'open') {
|
||||
order = this.extend (order, {
|
||||
'status': 'closed', // likewise it might have been canceled externally (unnoticed by "us")
|
||||
'cost': undefined,
|
||||
'filled': order['amount'],
|
||||
'remaining': 0.0,
|
||||
});
|
||||
if (order['cost'] === undefined) {
|
||||
if (order['filled'] !== undefined)
|
||||
order['cost'] = order['filled'] * order['price'];
|
||||
}
|
||||
this.orders[id] = order;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
console.log (this.id, orders, openOrders, closedOrders, canceledOrders)
|
||||
process.exit ()
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
|
||||
updateOrders,
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// There's been a lot of messing with this code...
|
||||
// The problem is to satisfy the following requirements:
|
||||
// - properly detect isNode == true on server side and isNode == false in the browser (on client side)
|
||||
// - make sure create-react-app, react-starter-kit and other react frameworks work
|
||||
// - make sure it does not break the browserified version (when linked into a html from a cdn)
|
||||
// - make sure it does not break the webpacking and babel-transpiled scripts
|
||||
// - make sure it works in Electron
|
||||
// - make sure it works with Angular.js
|
||||
// - make sure it does not break other possible usage scenarios
|
||||
|
||||
const isBrowser = typeof window !== 'undefined'
|
||||
|
||||
const isElectron = typeof process !== 'undefined' &&
|
||||
typeof process.versions !== 'undefined' &&
|
||||
typeof process.versions.electron !== 'undefined'
|
||||
|
||||
const isWebWorker = typeof WorkerGlobalScope !== 'undefined' && (self instanceof WorkerGlobalScope)
|
||||
|
||||
const isWindows = typeof process !== 'undefined' && process.platform === "win32"
|
||||
|
||||
const isNode = !(isBrowser || isWebWorker)
|
||||
|
||||
const defaultFetch = typeof (fetch) === "undefined" ? require ('../../static_dependencies/fetch-ponyfill/fetch-node') ().fetch : fetch
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
|
||||
isBrowser,
|
||||
isElectron,
|
||||
isWebWorker,
|
||||
isNode,
|
||||
isWindows,
|
||||
defaultFetch,
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16)
|
||||
: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid)
|
||||
|
||||
module.exports =
|
||||
|
||||
{ uuid
|
||||
|
||||
// hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response
|
||||
, unCamelCase: s => s.match (/^[A-Z0-9_]+$/) ? s : (s.replace (/[a-z0-9][A-Z]/g, x => x[0] + '_' + x[1]).replace(/[A-Z0-9][A-Z0-9][a-z]/g, x => x[0] + '_' + x[1] + x[2]).toLowerCase ())
|
||||
|
||||
, capitalize: s => s.length
|
||||
? (s.charAt (0).toUpperCase () + s.slice (1))
|
||||
: s
|
||||
, strip: s => s.replace(/^\s+|\s+$/g, '')
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const { sleep
|
||||
, now } = require ('./time')
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
module.exports = {
|
||||
|
||||
throttle: function throttle (cfg) {
|
||||
|
||||
let lastTimestamp = now ()
|
||||
, numTokens = (cfg.numTokens !== undefined) ? cfg.numTokens : cfg.capacity
|
||||
, running = false
|
||||
, counter = 0
|
||||
|
||||
const queue = []
|
||||
|
||||
return Object.assign ((rateLimit, cost) => {
|
||||
|
||||
if (queue.length > cfg.maxCapacity)
|
||||
throw new Error ('Backlog is over max capacity of ' + cfg.maxCapacity)
|
||||
|
||||
return new Promise (async (resolve, reject) => {
|
||||
|
||||
try {
|
||||
queue.push ({ cost, resolve, reject })
|
||||
|
||||
if (!running) {
|
||||
running = true
|
||||
while (queue.length > 0) {
|
||||
const hasEnoughTokens = cfg.capacity ? (numTokens > 0) : (numTokens >= 0)
|
||||
if (hasEnoughTokens) {
|
||||
if (queue.length > 0) {
|
||||
let { cost, resolve, reject } = queue[0]
|
||||
cost = (cost || cfg.defaultCost)
|
||||
if (numTokens >= Math.min (cost, cfg.capacity)) {
|
||||
numTokens -= cost
|
||||
queue.shift ()
|
||||
resolve ()
|
||||
}
|
||||
}
|
||||
}
|
||||
const t = now ()
|
||||
, elapsed = t - lastTimestamp
|
||||
lastTimestamp = t
|
||||
numTokens = Math.min (cfg.capacity, numTokens + elapsed * (1 / rateLimit))
|
||||
await sleep (cfg.delay)
|
||||
}
|
||||
running = false
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
reject (e)
|
||||
}
|
||||
})
|
||||
|
||||
}, cfg, { configure: newCfg => throttle (Object.assign ({}, cfg, newCfg)) })
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
'use strict';
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const now = Date.now // TODO: figure out how to utilize performance.now () properly – it's not as easy as it does not return a unix timestamp...
|
||||
const microseconds = () => now () * 1000 // TODO: utilize performance.now for that purpose
|
||||
const milliseconds = now
|
||||
const seconds = () => Math.floor (now () / 1000)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const setTimeout_original = setTimeout
|
||||
const setTimeout_safe = (done, ms, setTimeout = setTimeout_original /* overrideable for mocking purposes */, targetTime = now () + ms) => {
|
||||
|
||||
/* The built-in setTimeout function can fire its callback earlier than specified, so we
|
||||
need to ensure that it does not happen: sleep recursively until `targetTime` is reached... */
|
||||
|
||||
let clearInnerTimeout = () => {}
|
||||
let active = true
|
||||
|
||||
const id = setTimeout (() => {
|
||||
active = true
|
||||
const rest = targetTime - now ()
|
||||
if (rest > 0) {
|
||||
clearInnerTimeout = setTimeout_safe (done, rest, setTimeout, targetTime) // try sleep more
|
||||
} else {
|
||||
done ()
|
||||
}
|
||||
}, ms)
|
||||
|
||||
return function clear () {
|
||||
if (active) {
|
||||
active = false // dunno if IDs are unique on various platforms, so it's better to rely on this flag to exclude the possible cancellation of the wrong timer (if called after completion)
|
||||
clearTimeout (id)
|
||||
}
|
||||
clearInnerTimeout ()
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
class TimedOut extends Error {
|
||||
|
||||
constructor () {
|
||||
const message = 'timed out'
|
||||
super (message)
|
||||
this.constructor = TimedOut
|
||||
this.__proto__ = TimedOut.prototype
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const iso8601 = (timestamp) => {
|
||||
let _timestampNumber = undefined;
|
||||
if (typeof timestamp === 'number') {
|
||||
_timestampNumber = Math.floor (timestamp);
|
||||
} else {
|
||||
_timestampNumber = parseInt (timestamp, 10);
|
||||
}
|
||||
|
||||
// undefined, null and lots of nasty non-numeric values yield NaN
|
||||
if (Number.isNaN (_timestampNumber) || _timestampNumber < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// last line of defence
|
||||
try {
|
||||
return new Date (_timestampNumber).toISOString ();
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const parse8601 = (x) => {
|
||||
if (typeof x !== 'string' || !x) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (x.match (/^[0-9]+$/)) {
|
||||
// a valid number in a string, not a date.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (x.indexOf ('-') < 0 || x.indexOf (':') < 0) { // no date can be without a dash and a colon
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// last line of defence
|
||||
try {
|
||||
const candidate = Date.parse (((x.indexOf ('+') >= 0) || (x.slice (-1) === 'Z')) ? x : (x + 'Z').replace (/\s(\d\d):/, 'T$1:'));
|
||||
if (Number.isNaN (candidate)) {
|
||||
return undefined;
|
||||
}
|
||||
return candidate;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const parseDate = (x) => {
|
||||
if (typeof x !== 'string' || !x) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (x.indexOf ('GMT') >= 0) {
|
||||
try {
|
||||
return Date.parse (x);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return parse8601 (x);
|
||||
}
|
||||
|
||||
const mdy = (timestamp, infix = '-') => {
|
||||
infix = infix || ''
|
||||
const date = new Date (timestamp)
|
||||
const Y = date.getUTCFullYear ().toString ()
|
||||
let m = date.getUTCMonth () + 1
|
||||
let d = date.getUTCDate ()
|
||||
m = m < 10 ? ('0' + m) : m.toString ()
|
||||
d = d < 10 ? ('0' + d) : d.toString ()
|
||||
return m + infix + d + infix + Y
|
||||
}
|
||||
|
||||
const ymd = (timestamp, infix = '-') => {
|
||||
infix = infix || ''
|
||||
const date = new Date (timestamp)
|
||||
const Y = date.getUTCFullYear ().toString ()
|
||||
let m = date.getUTCMonth () + 1
|
||||
let d = date.getUTCDate ()
|
||||
m = m < 10 ? ('0' + m) : m.toString ()
|
||||
d = d < 10 ? ('0' + d) : d.toString ()
|
||||
return Y + infix + m + infix + d
|
||||
}
|
||||
|
||||
const ymdhms = (timestamp, infix = ' ') => {
|
||||
const date = new Date (timestamp)
|
||||
const Y = date.getUTCFullYear ()
|
||||
let m = date.getUTCMonth () + 1
|
||||
let d = date.getUTCDate ()
|
||||
let H = date.getUTCHours ()
|
||||
let M = date.getUTCMinutes ()
|
||||
let S = date.getUTCSeconds ()
|
||||
m = m < 10 ? ('0' + m) : m
|
||||
d = d < 10 ? ('0' + d) : d
|
||||
H = H < 10 ? ('0' + H) : H
|
||||
M = M < 10 ? ('0' + M) : M
|
||||
S = S < 10 ? ('0' + S) : S
|
||||
return Y + '-' + m + '-' + d + infix + H + ':' + M + ':' + S
|
||||
}
|
||||
|
||||
module.exports =
|
||||
|
||||
{
|
||||
now
|
||||
, microseconds
|
||||
, milliseconds
|
||||
, seconds
|
||||
, iso8601
|
||||
, parse8601
|
||||
, parseDate
|
||||
, mdy
|
||||
, ymd
|
||||
, ymdhms
|
||||
, setTimeout_safe
|
||||
, sleep: ms => new Promise (resolve => setTimeout_safe (resolve, ms))
|
||||
, TimedOut
|
||||
, timeout: async (ms, promise) => {
|
||||
|
||||
let clear = () => {}
|
||||
const expires = new Promise (resolve => (clear = setTimeout_safe (resolve, ms)))
|
||||
|
||||
try {
|
||||
return await Promise.race ([promise, expires.then (() => { throw new TimedOut () })])
|
||||
} finally {
|
||||
clear () // fixes https://github.com/ccxt/ccxt/issues/749
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const isNumber = Number.isFinite
|
||||
, isInteger = Number.isInteger
|
||||
, isArray = Array.isArray
|
||||
, hasProps = o => ((o !== undefined) && (o !== null))
|
||||
, isString = s => (typeof s === 'string')
|
||||
, isObject = o => ((o !== null) && (typeof o === 'object'))
|
||||
, isDictionary = o => (isObject (o) && !isArray (o))
|
||||
, isStringCoercible = x => ((hasProps (x) && x.toString) || isNumber (x))
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
const prop = (o, k) => (isObject (o) ? o[k] : undefined)
|
||||
, prop2 = (o, k1, k2) => (!isObject (o) ? undefined : (((k1 in o) && (o[k1] !== null)) ? o[k1] : o[k2]))
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
const asFloat = x => ((isNumber (x) || isString (x)) ? parseFloat (x) : NaN)
|
||||
, asInteger = x => ((isNumber (x) || isString (x)) ? parseInt (x, 10) : NaN)
|
||||
|
||||
/* ............................................. */
|
||||
|
||||
module.exports = {
|
||||
|
||||
isNumber
|
||||
, isInteger
|
||||
, isArray
|
||||
, isObject
|
||||
, isString
|
||||
, isStringCoercible
|
||||
, isDictionary
|
||||
|
||||
, hasProps
|
||||
, prop
|
||||
|
||||
, asFloat
|
||||
, asInteger
|
||||
|
||||
, safeFloat: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? n : $default)
|
||||
, safeInteger: (o, k, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? n : $default)
|
||||
, safeIntegerProduct: (o, k, $factor, $default, n = asInteger (prop (o, k))) => (isNumber (n) ? parseInt (n * $factor) : $default)
|
||||
, safeTimestamp: (o, k, $default, n = asFloat (prop (o, k))) => (isNumber (n) ? parseInt (n * 1000) : $default)
|
||||
, safeValue: (o, k, $default, x = prop (o, k)) => (hasProps (x) ? x : $default)
|
||||
, safeString: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x) : $default)
|
||||
, safeStringLower: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default)
|
||||
, safeStringUpper: (o, k, $default, x = prop (o, k)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default)
|
||||
|
||||
// not using safeFloats with an array argument as we're trying to save some cycles here
|
||||
// we're not using safeFloat3 either because those cases are too rare to deserve their own optimization
|
||||
|
||||
, safeFloat2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default)
|
||||
, safeInteger2: (o, k1, k2, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? n : $default)
|
||||
, safeIntegerProduct2: (o, k1, k2, $factor, $default, n = asInteger (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * $factor) : $default)
|
||||
, safeTimestamp2: (o, k1, k2, $default, n = asFloat (prop2 (o, k1, k2))) => (isNumber (n) ? parseInt (n * 1000) : $default)
|
||||
, safeValue2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (hasProps (x) ? x : $default)
|
||||
, safeString2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x) : $default)
|
||||
, safeStringLower2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toLowerCase () : $default)
|
||||
, safeStringUpper2: (o, k1, k2, $default, x = prop2 (o, k1, k2)) => (isStringCoercible (x) ? String (x).toUpperCase () : $default)
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
+686
@@ -0,0 +1,686 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bcex extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bcex',
|
||||
'name': 'BCEX',
|
||||
'countries': [ 'CN', 'CA' ],
|
||||
'version': '1',
|
||||
'has': {
|
||||
'fetchBalance': true,
|
||||
'fetchMarkets': true,
|
||||
'createOrder': true,
|
||||
'cancelOrder': true,
|
||||
'fetchTicker': true,
|
||||
'fetchTickers': false,
|
||||
'fetchTrades': true,
|
||||
'fetchOrder': true,
|
||||
'fetchOrders': true,
|
||||
'fetchClosedOrders': 'emulated',
|
||||
'fetchOpenOrders': true,
|
||||
'fetchTradingLimits': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg',
|
||||
'api': 'https://www.bcex.top',
|
||||
'www': 'https://www.bcex.top',
|
||||
'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface',
|
||||
'fees': 'https://bcex.udesk.cn/hc/articles/57085',
|
||||
'referral': 'https://www.bcex.top/register?invite_code=758978&lang=en',
|
||||
},
|
||||
'status': {
|
||||
'status': 'error',
|
||||
'updated': undefined,
|
||||
'eta': undefined,
|
||||
'url': undefined,
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'Api_Market/getPriceList', // tickers
|
||||
'Api_Order/ticker', // last ohlcv candle (ticker)
|
||||
'Api_Order/depth', // orderbook
|
||||
'Api_Market/getCoinTrade', // ticker
|
||||
'Api_Order/marketOrder', // trades...
|
||||
],
|
||||
'post': [
|
||||
'Api_Market/getPriceList', // tickers
|
||||
'Api_Order/ticker', // last ohlcv candle (ticker)
|
||||
'Api_Order/depth', // orderbook
|
||||
'Api_Market/getCoinTrade', // ticker
|
||||
'Api_Order/marketOrder', // trades...
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'Api_Order/cancel',
|
||||
'Api_Order/coinTrust', // limit order
|
||||
'Api_Order/orderList', // open / all orders (my trades?)
|
||||
'Api_Order/orderInfo',
|
||||
'Api_Order/tradeList', // open / all orders
|
||||
'Api_Order/trustList', // ?
|
||||
'Api_User/userBalance',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'buy': 0.0,
|
||||
'sell': 0.2 / 100,
|
||||
},
|
||||
'funding': {
|
||||
'tierBased': false,
|
||||
'percentage': false,
|
||||
'withdraw': {
|
||||
'ckusd': 0.0,
|
||||
'other': 0.05 / 100,
|
||||
},
|
||||
'deposit': {},
|
||||
},
|
||||
},
|
||||
'exceptions': {
|
||||
'该币不存在,非法操作': ExchangeError, // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
|
||||
'公钥不合法': AuthenticationError, // { code: 1, msg: '公钥不合法' } - wrong public key
|
||||
'您的可用余额不足': InsufficientFunds, // { code: 1, msg: '您的可用余额不足' } - your available balance is insufficient
|
||||
'您的btc不足': InsufficientFunds, // { code: 1, msg: '您的btc不足' } - your btc is insufficient
|
||||
'参数非法': InvalidOrder, // {'code': 1, 'msg': '参数非法'} - 'Parameter illegal'
|
||||
'订单信息不存在': OrderNotFound, // {'code': 1, 'msg': '订单信息不存在'} - 'Order information does not exist'
|
||||
},
|
||||
'options': {
|
||||
'limits': {
|
||||
// hardcoding is deprecated, using these predefined values is not recommended, use loadTradingLimits instead
|
||||
'AFC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}},
|
||||
'AFC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}},
|
||||
'AFT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}},
|
||||
'AICC/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}},
|
||||
'AIDOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}},
|
||||
'AISI/ETH': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 500 }}},
|
||||
'AIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}},
|
||||
'ANS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 500 }}},
|
||||
'ANS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}},
|
||||
'ARC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 60, 'max': 600000 }}},
|
||||
'AXF/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 1000000 }}},
|
||||
'BASH/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 3000000 }}},
|
||||
'BATT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 60, 'max': 1500000 }}},
|
||||
'BCD/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.3, 'max': 7000 }}},
|
||||
'BHPC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 70000 }}},
|
||||
'BHPC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 60000 }}},
|
||||
'BOPO/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 2000000 }}},
|
||||
'BOPO/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 10000000 }}},
|
||||
'BTC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 10 }}},
|
||||
'BTC/CNET': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0005, 'max': 5 }}},
|
||||
'BTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0002, 'max': 4 }}},
|
||||
'BTE/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}},
|
||||
'BU/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}},
|
||||
'CIC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3000, 'max': 30000000 }}},
|
||||
'CIT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}},
|
||||
'CIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}},
|
||||
'CMT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 2500000 }}},
|
||||
'CNET/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 12, 'max': 120000 }}},
|
||||
'CNMC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 50000 }}},
|
||||
'CTC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 550000 }}},
|
||||
'CZR/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 12, 'max': 500000 }}},
|
||||
'DCON/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 8, 'max': 300000 }}},
|
||||
'DCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 40000 }}},
|
||||
'DCT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 2, 'max': 2000 }}},
|
||||
'DOGE/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 3000, 'max': 14000000 }}},
|
||||
'DOGE/CKUSD': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 500, 'max': 2000000 }}},
|
||||
'DRCT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 16, 'max': 190000 }}},
|
||||
'ELA/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 500 }}},
|
||||
'ELF/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 100000 }}},
|
||||
'ELF/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }}},
|
||||
'EOS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}},
|
||||
'EOS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 30000 }}},
|
||||
'EOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.18, 'max': 1800 }}},
|
||||
'ETC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}},
|
||||
'ETC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}},
|
||||
'ETF/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 150000 }}},
|
||||
'ETH/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.015, 'max': 100 }}},
|
||||
'ETH/CKUSD': { 'precision': { 'amount': 4, 'price': 4 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}},
|
||||
'ETH/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}},
|
||||
'FCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}},
|
||||
'FCT/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}},
|
||||
'GAME/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 1, 'max': 10000 }}},
|
||||
'GOOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}},
|
||||
'GP/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 600, 'max': 6000000 }}},
|
||||
'HSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 1000, 'max': 20000000 }}},
|
||||
'IFISH/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 300, 'max': 8000000 }}},
|
||||
'IIC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 4000000 }}},
|
||||
'IMOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}},
|
||||
'JC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}},
|
||||
'LBTC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 3000 }}},
|
||||
'LEC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 500, 'max': 5000000 }}},
|
||||
'LKY/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 70000 }}},
|
||||
'LKY/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
|
||||
'LMC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}},
|
||||
'LSK/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.3, 'max': 3000 }}},
|
||||
'LTC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}},
|
||||
'LTC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}},
|
||||
'LTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 450 }}},
|
||||
'MC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 10000, 'max': 100000000 }}},
|
||||
'MCC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 30, 'max': 350000 }}},
|
||||
'MOC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 25, 'max': 600000 }}},
|
||||
'MRYC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}},
|
||||
'MT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 6000000 }}},
|
||||
'MXI/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 5000, 'max': 60000000 }}},
|
||||
'NAI/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
|
||||
'NAS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 15000 }}},
|
||||
'NAS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}},
|
||||
'NEWOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 65, 'max': 700000 }}},
|
||||
'NKN/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 3, 'max': 350000 }}},
|
||||
'NTK/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 30000 }}},
|
||||
'ONT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2000 }}},
|
||||
'ONT/ETH': { 'precision': { 'amount': 3, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 1000 }}},
|
||||
'PNT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 80, 'max': 800000 }}},
|
||||
'PST/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}},
|
||||
'PTT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 450, 'max': 10000000 }}},
|
||||
'QTUM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.4, 'max': 2800 }}},
|
||||
'QTUM/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}},
|
||||
'RATING/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}},
|
||||
'RHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}},
|
||||
'SDA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 500000 }}},
|
||||
'SDD/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
|
||||
'SHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 2500000 }}},
|
||||
'SHE/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 5000000 }}},
|
||||
'SMC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}},
|
||||
'SOP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 1000000 }}},
|
||||
'TAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 35, 'max': 800000 }}},
|
||||
'TIP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 200000 }}},
|
||||
'TKT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 40, 'max': 400000 }}},
|
||||
'TLC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}},
|
||||
'TNC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 110000 }}},
|
||||
'TUB/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 8000000 }}},
|
||||
'UC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 3000000 }}},
|
||||
'UDB/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 2000, 'max': 40000000 }}},
|
||||
'UIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}},
|
||||
'VAAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 250000 }}},
|
||||
'VPN/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}},
|
||||
'VSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 30, 'max': 650000 }}},
|
||||
'WAVES/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.15, 'max': 1500 }}},
|
||||
'WDNA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 250000 }}},
|
||||
'WIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3, 'max': 30000 }}},
|
||||
'XAS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 25000 }}},
|
||||
'XLM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 300000 }}},
|
||||
'XLM/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1, 'max': 300000 }}},
|
||||
'XLM/USDT': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}},
|
||||
'XRP/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 24, 'max': 100000 }}},
|
||||
'XRP/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}},
|
||||
'YBCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 200000 }}},
|
||||
'YBCT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 200000 }}},
|
||||
'YBY/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 25000, 'max': 250000000 }}},
|
||||
'ZEC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}},
|
||||
'ZEC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchTradingLimits (symbols = undefined, params = {}) {
|
||||
// this method should not be called directly, use loadTradingLimits () instead
|
||||
// by default it will try load withdrawal fees of all currencies (with separate requests, sequentially)
|
||||
// however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those
|
||||
await this.loadMarkets ();
|
||||
if (symbols === undefined) {
|
||||
symbols = this.symbols;
|
||||
}
|
||||
const result = {};
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
const symbol = symbols[i];
|
||||
result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchTradingLimitsById (id, params = {}) {
|
||||
const request = {
|
||||
'symbol': id,
|
||||
};
|
||||
const response = await this.publicPostApiOrderTicker (this.extend (request, params));
|
||||
//
|
||||
// { code: 0,
|
||||
// msg: "获取牌价信息成功",
|
||||
// data: { high: 0.03721392,
|
||||
// low: 0.03335362,
|
||||
// buy: "0.03525757",
|
||||
// sell: "0.03531160",
|
||||
// last: 0.0352634,
|
||||
// vol: "184742.4176",
|
||||
// min_trade: "0.01500000",
|
||||
// max_trade: "100.00000000",
|
||||
// number_float: "4",
|
||||
// price_float: "8" } } }
|
||||
//
|
||||
return this.parseTradingLimits (this.safeValue (response, 'data', {}));
|
||||
}
|
||||
|
||||
parseTradingLimits (limits, symbol = undefined, params = {}) {
|
||||
//
|
||||
// { high: 0.03721392,
|
||||
// low: 0.03335362,
|
||||
// buy: "0.03525757",
|
||||
// sell: "0.03531160",
|
||||
// last: 0.0352634,
|
||||
// vol: "184742.4176",
|
||||
// min_trade: "0.01500000",
|
||||
// max_trade: "100.00000000",
|
||||
// number_float: "4",
|
||||
// price_float: "8" }
|
||||
//
|
||||
return {
|
||||
'info': limits,
|
||||
'precision': {
|
||||
'amount': this.safeInteger (limits, 'number_float'),
|
||||
'price': this.safeInteger (limits, 'price_float'),
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': this.safeFloat (limits, 'min_trade'),
|
||||
'max': this.safeFloat (limits, 'max_trade'),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const response = await this.publicGetApiMarketGetPriceList (params);
|
||||
const result = [];
|
||||
const keys = Object.keys (response);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const currentMarketId = keys[i];
|
||||
const currentMarkets = response[currentMarketId];
|
||||
for (let j = 0; j < currentMarkets.length; j++) {
|
||||
const market = currentMarkets[j];
|
||||
const baseId = this.safeString (market, 'coin_from');
|
||||
const quoteId = this.safeString (market, 'coin_to');
|
||||
let base = baseId.toUpperCase ();
|
||||
let quote = quoteId.toUpperCase ();
|
||||
base = this.safeCurrencyCode (base);
|
||||
quote = this.safeCurrencyCode (quote);
|
||||
const id = baseId + '2' + quoteId;
|
||||
const symbol = base + '/' + quote;
|
||||
const active = true;
|
||||
const defaults = this.safeValue (this.options['limits'], symbol, {});
|
||||
result.push (this.extend ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'active': active,
|
||||
// overrided by defaults from this.options['limits']
|
||||
'precision': {
|
||||
'amount': undefined,
|
||||
'price': undefined,
|
||||
},
|
||||
// overrided by defaults from this.options['limits']
|
||||
'limits': {
|
||||
'amount': { 'min': undefined, 'max': undefined },
|
||||
'price': { 'min': undefined, 'max': undefined },
|
||||
'cost': { 'min': undefined, 'max': undefined },
|
||||
},
|
||||
'info': market,
|
||||
}, defaults));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeTimestamp2 (trade, 'date', 'created');
|
||||
const id = this.safeString (trade, 'tid');
|
||||
const orderId = this.safeString (trade, 'order_id');
|
||||
const amount = this.safeFloat2 (trade, 'number', 'amount');
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = amount * price;
|
||||
}
|
||||
}
|
||||
let side = this.safeString (trade, 'side');
|
||||
if (side === 'sale') {
|
||||
side = 'sell';
|
||||
}
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'order': orderId,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
const market = this.market (symbol);
|
||||
const response = await this.publicPostApiOrderMarketOrder (this.extend (request, params));
|
||||
return this.parseTrades (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privatePostApiUserUserBalance (params);
|
||||
const data = this.safeValue (response, 'data');
|
||||
let keys = Object.keys (data);
|
||||
const result = { };
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const amount = this.safeFloat (data, key);
|
||||
const parts = key.split ('_');
|
||||
const currencyId = parts[0];
|
||||
const lockOrOver = parts[1];
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
if (!(code in result)) {
|
||||
result[code] = this.account ();
|
||||
}
|
||||
if (lockOrOver === 'lock') {
|
||||
result[code]['used'] = parseFloat (amount);
|
||||
} else {
|
||||
result[code]['free'] = parseFloat (amount);
|
||||
}
|
||||
}
|
||||
keys = Object.keys (result);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
const total = this.sum (result[key]['used'], result[key]['free']);
|
||||
result[key]['total'] = total;
|
||||
}
|
||||
result['info'] = data;
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.markets[symbol];
|
||||
const request = {
|
||||
'part': market['quoteId'],
|
||||
'coin': market['baseId'],
|
||||
};
|
||||
const response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params));
|
||||
const timestamp = this.milliseconds ();
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (response, 'max'),
|
||||
'low': this.safeFloat (response, 'min'),
|
||||
'bid': this.safeFloat (response, 'buy'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (response, 'sale'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': this.safeFloat (response, 'price'),
|
||||
'last': this.safeFloat (response, 'price'),
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': this.safeFloat (response, 'change_24h'),
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (response, 'volume_24h'),
|
||||
'quoteVolume': undefined,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const marketId = this.marketId (symbol);
|
||||
const request = {
|
||||
'symbol': marketId,
|
||||
};
|
||||
const response = await this.publicPostApiOrderDepth (this.extend (request, params));
|
||||
const data = this.safeValue (response, 'data');
|
||||
const timestamp = this.safeTimestamp (data, 'date');
|
||||
return this.parseOrderBook (data, timestamp);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
};
|
||||
const response = await this.privatePostApiOrderOrderList (this.extend (request, params));
|
||||
return this.parseTrades (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'0': 'open',
|
||||
'1': 'open', // partially filled
|
||||
'2': 'closed',
|
||||
'3': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOrder requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'trust_id': id,
|
||||
};
|
||||
const response = await this.privatePostApiOrderOrderInfo (this.extend (request, params));
|
||||
const order = this.safeValue (response, 'data');
|
||||
const timestamp = this.safeTimestamp (order, 'created');
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
let side = this.safeString (order, 'flag');
|
||||
if (side === 'sale') {
|
||||
side = 'sell';
|
||||
}
|
||||
// Can't use parseOrder because the data format is different btw endpoint for fetchOrder and fetchOrders
|
||||
return {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'price': this.safeFloat (order, 'price'),
|
||||
'cost': undefined,
|
||||
'average': this.safeFloat (order, 'avg_price'),
|
||||
'amount': this.safeFloat (order, 'number'),
|
||||
'filled': this.safeFloat (order, 'numberdeal'),
|
||||
'remaining': this.safeFloat (order, 'numberover'),
|
||||
'status': status,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const id = this.safeString (order, 'id');
|
||||
const timestamp = this.safeTimestamp (order, 'datetime');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const type = undefined;
|
||||
let side = this.safeString (order, 'type');
|
||||
if (side === 'sale') {
|
||||
side = 'sell';
|
||||
}
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const average = this.safeFloat (order, 'avg_price');
|
||||
const amount = this.safeFloat (order, 'amount');
|
||||
const remaining = this.safeFloat (order, 'amount_outstanding');
|
||||
const filled = amount - remaining;
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
const cost = filled * price;
|
||||
const fee = undefined;
|
||||
const result = {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'cost': cost,
|
||||
'average': average,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'status': status,
|
||||
'fee': fee,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'type': type,
|
||||
};
|
||||
let market = undefined;
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
request['symbol'] = market['id'];
|
||||
}
|
||||
const response = await this.privatePostApiOrderTradeList (this.extend (request, params));
|
||||
if ('data' in response) {
|
||||
return this.parseOrders (response['data'], market, since, limit);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
return this.fetchOrdersByType ('open', symbol, since, limit, params);
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
const orders = await this.fetchOrders (symbol, since, limit, params);
|
||||
return this.filterBy (orders, 'status', 'closed');
|
||||
}
|
||||
|
||||
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
return this.fetchOrdersByType ('all', symbol, since, limit, params);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'type': side,
|
||||
'price': this.priceToPrecision (symbol, price),
|
||||
'number': this.amountToPrecision (symbol, amount),
|
||||
};
|
||||
const response = await this.privatePostApiOrderCoinTrust (this.extend (request, params));
|
||||
const data = this.safeValue (response, 'data', {});
|
||||
const id = this.safeString (data, 'order_id');
|
||||
return {
|
||||
'info': response,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const request = {};
|
||||
if (symbol !== undefined) {
|
||||
request['symbol'] = this.marketId (symbol);
|
||||
}
|
||||
if (id !== undefined) {
|
||||
request['order_id'] = id;
|
||||
}
|
||||
return await this.privatePostApiOrderCancel (this.extend (request, params));
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + this.implodeParams (path, params);
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
let payload = this.urlencode ({ 'api_key': this.apiKey });
|
||||
if (Object.keys (query).length) {
|
||||
payload += '&' + this.urlencode (this.keysort (query));
|
||||
}
|
||||
const auth = payload + '&secret_key=' + this.secret;
|
||||
const signature = this.hash (this.encode (auth));
|
||||
body = payload + '&sign=' + signature;
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
}
|
||||
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
|
||||
}
|
||||
const errorCode = this.safeValue (response, 'code');
|
||||
if (errorCode !== undefined) {
|
||||
if (errorCode !== 0) {
|
||||
//
|
||||
// { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
|
||||
// { code: 1, msg: '公钥不合法' } - wrong public key
|
||||
// { code: 1, msg: '价格输入有误,请检查你的数值精度' } - 'The price input is incorrect, please check your numerical accuracy'
|
||||
// { code: 1, msg: '单笔最小交易数量不能小于0.00100000,请您重新挂单'} -
|
||||
// 'The minimum number of single transactions cannot be less than 0.00100000. Please re-post the order'
|
||||
//
|
||||
const message = this.safeString (response, 'msg');
|
||||
const feedback = this.id + ' ' + message;
|
||||
this.throwExactlyMatchedException (this.exceptions, message, feedback);
|
||||
if (message.indexOf ('请您重新挂单') >= 0) { // minimum limit
|
||||
throw new InvalidOrder (feedback);
|
||||
} else {
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
};
|
||||
}
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const hitbtc2 = require ('./hitbtc2');
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bequant extends hitbtc2 {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bequant',
|
||||
'name': 'Bequant',
|
||||
'countries': [ 'MT' ], // Malta
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg',
|
||||
'api': 'https://api.bequant.io',
|
||||
'www': 'https://bequant.io',
|
||||
'doc': [
|
||||
'https://api.bequant.io/',
|
||||
],
|
||||
'fees': [
|
||||
'https://bequant.io/fees-and-limits',
|
||||
],
|
||||
'referral': 'https://bequant.io',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
+966
@@ -0,0 +1,966 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bibox extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bibox',
|
||||
'name': 'Bibox',
|
||||
'countries': [ 'CN', 'US', 'KR' ],
|
||||
'version': 'v1',
|
||||
'has': {
|
||||
'CORS': false,
|
||||
'publicAPI': false,
|
||||
'fetchBalance': true,
|
||||
'fetchDeposits': true,
|
||||
'fetchWithdrawals': true,
|
||||
'fetchCurrencies': true,
|
||||
'fetchDepositAddress': true,
|
||||
'fetchFundingFees': true,
|
||||
'fetchTickers': true,
|
||||
'fetchOrder': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchClosedOrders': true,
|
||||
'fetchMyTrades': true,
|
||||
'fetchOHLCV': true,
|
||||
'createMarketOrder': false, // or they will return https://github.com/ccxt/ccxt/issues/2338
|
||||
'withdraw': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': '1min',
|
||||
'5m': '5min',
|
||||
'15m': '15min',
|
||||
'30m': '30min',
|
||||
'1h': '1hour',
|
||||
'2h': '2hour',
|
||||
'4h': '4hour',
|
||||
'6h': '6hour',
|
||||
'12h': '12hour',
|
||||
'1d': 'day',
|
||||
'1w': 'week',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/34902611-2be8bf1a-f830-11e7-91a2-11b2f292e750.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',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'post': [
|
||||
// TODO: rework for full endpoint/cmd paths here
|
||||
'mdata',
|
||||
],
|
||||
'get': [
|
||||
'cquery',
|
||||
'mdata',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'cquery',
|
||||
'ctrade',
|
||||
'user',
|
||||
'orderpending',
|
||||
'transfer',
|
||||
],
|
||||
},
|
||||
'v2private': {
|
||||
'post': [
|
||||
'assets/transfer/spot',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'taker': 0.001,
|
||||
'maker': 0.001,
|
||||
},
|
||||
'funding': {
|
||||
'tierBased': false,
|
||||
'percentage': false,
|
||||
'withdraw': {},
|
||||
'deposit': {},
|
||||
},
|
||||
},
|
||||
'exceptions': {
|
||||
'2011': AccountSuspended, // Account is locked
|
||||
'2015': AuthenticationError, // Google authenticator is wrong
|
||||
'2021': InsufficientFunds, // Insufficient balance available for withdrawal
|
||||
'2027': InsufficientFunds, // Insufficient balance available (for trade)
|
||||
'2033': OrderNotFound, // operation failed! Orders have been completed or revoked
|
||||
'2067': InvalidOrder, // Does not support market orders
|
||||
'2068': InvalidOrder, // The number of orders can not be less than
|
||||
'2085': InvalidOrder, // Order quantity is too small
|
||||
'3012': AuthenticationError, // invalid apiKey
|
||||
'3024': PermissionDenied, // wrong apikey permissions
|
||||
'3025': AuthenticationError, // signature failed
|
||||
'4000': ExchangeNotAvailable, // current network is unstable
|
||||
'4003': DDoSProtection, // server busy please try again later
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'KEY': 'Bihu',
|
||||
'MTC': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025
|
||||
'PAI': 'PCHAIN',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const request = {
|
||||
'cmd': 'marketAll',
|
||||
};
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "result": [
|
||||
// {
|
||||
// "is_hide":0,
|
||||
// "high_cny":"1.9478",
|
||||
// "amount":"272.41",
|
||||
// "coin_symbol":"BIX",
|
||||
// "last":"0.00002487",
|
||||
// "currency_symbol":"BTC",
|
||||
// "change":"+0.00000073",
|
||||
// "low_cny":"1.7408",
|
||||
// "base_last_cny":"1.84538041",
|
||||
// "area_id":7,
|
||||
// "percent":"+3.02%",
|
||||
// "last_cny":"1.8454",
|
||||
// "high":"0.00002625",
|
||||
// "low":"0.00002346",
|
||||
// "pair_type":0,
|
||||
// "last_usd":"0.2686",
|
||||
// "vol24H":"10940613",
|
||||
// "id":1,
|
||||
// "high_usd":"0.2835",
|
||||
// "low_usd":"0.2534"
|
||||
// }
|
||||
// ],
|
||||
// "cmd":"marketAll",
|
||||
// "ver":"1.1"
|
||||
// }
|
||||
//
|
||||
const markets = this.safeValue (response, 'result');
|
||||
const result = [];
|
||||
for (let i = 0; i < markets.length; i++) {
|
||||
const market = markets[i];
|
||||
const numericId = this.safeInteger (market, 'id');
|
||||
const baseId = this.safeString (market, 'coin_symbol');
|
||||
const quoteId = this.safeString (market, 'currency_symbol');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
const symbol = base + '/' + quote;
|
||||
const id = baseId + '_' + quoteId;
|
||||
const precision = {
|
||||
'amount': 4,
|
||||
'price': 8,
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'numericId': numericId,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'active': true,
|
||||
'info': market,
|
||||
'precision': precision,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': Math.pow (10, -precision['amount']),
|
||||
'max': undefined,
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision['price']),
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
// we don't set values that are not defined by the exchange
|
||||
const timestamp = this.safeInteger (ticker, 'timestamp');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
} else {
|
||||
const baseId = this.safeString (ticker, 'coin_symbol');
|
||||
const quoteId = this.safeString (ticker, 'currency_symbol');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
symbol = base + '/' + quote;
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
const change = this.safeFloat (ticker, 'change');
|
||||
const baseVolume = this.safeFloat2 (ticker, 'vol', 'vol24H');
|
||||
let open = undefined;
|
||||
if ((last !== undefined) && (change !== undefined)) {
|
||||
open = last - change;
|
||||
}
|
||||
let percentage = this.safeString (ticker, 'percent');
|
||||
if (percentage !== undefined) {
|
||||
percentage = percentage.replace ('%', '');
|
||||
percentage = parseFloat (percentage);
|
||||
}
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'high'),
|
||||
'low': this.safeFloat (ticker, 'low'),
|
||||
'bid': this.safeFloat (ticker, 'buy'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'sell'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': open,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': change,
|
||||
'percentage': percentage,
|
||||
'average': undefined,
|
||||
'baseVolume': baseVolume,
|
||||
'quoteVolume': this.safeFloat (ticker, 'amount'),
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'cmd': 'ticker',
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
return this.parseTicker (response['result'], market);
|
||||
}
|
||||
|
||||
parseTickers (rawTickers, symbols = undefined) {
|
||||
const tickers = [];
|
||||
for (let i = 0; i < rawTickers.length; i++) {
|
||||
const ticker = this.parseTicker (rawTickers[i]);
|
||||
if ((symbols === undefined) || (this.inArray (ticker['symbol'], symbols))) {
|
||||
tickers.push (ticker);
|
||||
}
|
||||
}
|
||||
return tickers;
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
const request = {
|
||||
'cmd': 'marketAll',
|
||||
};
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
const tickers = this.parseTickers (response['result'], symbols);
|
||||
return this.indexBy (tickers, 'symbol');
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.safeInteger2 (trade, 'time', 'createdAt');
|
||||
let side = this.safeInteger2 (trade, 'side', 'order_side');
|
||||
side = (side === 1) ? 'buy' : 'sell';
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
let marketId = this.safeString (trade, 'pair');
|
||||
if (marketId === undefined) {
|
||||
const baseId = this.safeString (trade, 'coin_symbol');
|
||||
const quoteId = this.safeString (trade, 'currency_symbol');
|
||||
if ((baseId !== undefined) && (quoteId !== undefined)) {
|
||||
marketId = baseId + '_' + quoteId;
|
||||
}
|
||||
}
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
}
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
let fee = undefined;
|
||||
const feeCost = this.safeFloat (trade, 'fee');
|
||||
let feeCurrency = this.safeString (trade, 'fee_symbol');
|
||||
if (feeCurrency !== undefined) {
|
||||
if (feeCurrency in this.currencies_by_id) {
|
||||
feeCurrency = this.currencies_by_id[feeCurrency]['code'];
|
||||
} else {
|
||||
feeCurrency = this.safeCurrencyCode (feeCurrency);
|
||||
}
|
||||
}
|
||||
const feeRate = undefined; // todo: deduce from market if market is defined
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined && amount !== undefined) {
|
||||
cost = price * amount;
|
||||
}
|
||||
if (feeCost !== undefined) {
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': feeCurrency,
|
||||
'rate': feeRate,
|
||||
};
|
||||
}
|
||||
const id = this.safeString (trade, 'id');
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'order': undefined, // Bibox does not have it (documented) yet
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': 'limit',
|
||||
'takerOrMaker': undefined,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'cmd': 'deals',
|
||||
'pair': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit; // default = 200
|
||||
}
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
return this.parseTrades (response['result'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'cmd': 'depth',
|
||||
'pair': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit; // default = 200
|
||||
}
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
return this.parseOrderBook (response['result'], this.safeFloat (response['result'], 'update_time'), 'bids', 'asks', 'price', 'volume');
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
ohlcv['time'],
|
||||
this.safeFloat (ohlcv, 'open'),
|
||||
this.safeFloat (ohlcv, 'high'),
|
||||
this.safeFloat (ohlcv, 'low'),
|
||||
this.safeFloat (ohlcv, 'close'),
|
||||
this.safeFloat (ohlcv, 'vol'),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = 1000, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'cmd': 'kline',
|
||||
'pair': market['id'],
|
||||
'period': this.timeframes[timeframe],
|
||||
'size': limit,
|
||||
};
|
||||
const response = await this.publicGetMdata (this.extend (request, params));
|
||||
return this.parseOHLCVs (response['result'], market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
async fetchCurrencies (params = {}) {
|
||||
if (!this.apiKey || !this.secret) {
|
||||
throw new AuthenticationError (this.id + " fetchCurrencies is an authenticated endpoint, therefore it requires 'apiKey' and 'secret' credentials. If you don't need currency details, set exchange.has['fetchCurrencies'] = false before calling its methods.");
|
||||
}
|
||||
const request = {
|
||||
'cmd': 'transfer/coinList',
|
||||
'body': {},
|
||||
};
|
||||
const response = await this.privatePostTransfer (this.extend (request, params));
|
||||
const currencies = this.safeValue (response, 'result');
|
||||
const result = {};
|
||||
for (let i = 0; i < currencies.length; i++) {
|
||||
const currency = currencies[i];
|
||||
const id = this.safeString (currency, 'symbol');
|
||||
const name = currency['name']; // contains hieroglyphs causing python ASCII bug
|
||||
const code = this.safeCurrencyCode (id);
|
||||
const precision = 8;
|
||||
const deposit = this.safeValue (currency, 'enable_deposit');
|
||||
const withdraw = this.safeValue (currency, 'enable_withdraw');
|
||||
const active = (deposit && withdraw);
|
||||
result[code] = {
|
||||
'id': id,
|
||||
'code': code,
|
||||
'info': currency,
|
||||
'name': name,
|
||||
'active': active,
|
||||
'fee': undefined,
|
||||
'precision': precision,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': Math.pow (10, -precision),
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision),
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
'cost': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
'withdraw': {
|
||||
'min': undefined,
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const type = this.safeString (params, 'type', 'assets');
|
||||
params = this.omit (params, 'type');
|
||||
const request = {
|
||||
'cmd': 'transfer/' + type, // assets, mainAssets
|
||||
'body': this.extend ({
|
||||
'select': 1, // return full info
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostTransfer (request);
|
||||
const balances = this.safeValue (response, 'result');
|
||||
const result = { 'info': balances };
|
||||
let indexed = undefined;
|
||||
if ('assets_list' in balances) {
|
||||
indexed = this.indexBy (balances['assets_list'], 'coin_symbol');
|
||||
} else {
|
||||
indexed = balances;
|
||||
}
|
||||
const keys = Object.keys (indexed);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const id = keys[i];
|
||||
let code = id.toUpperCase ();
|
||||
if (code.indexOf ('TOTAL_') >= 0) {
|
||||
code = code.slice (6);
|
||||
}
|
||||
if (code in this.currencies_by_id) {
|
||||
code = this.currencies_by_id[code]['code'];
|
||||
}
|
||||
const account = this.account ();
|
||||
let balance = indexed[id];
|
||||
if (typeof balance === 'string') {
|
||||
balance = parseFloat (balance);
|
||||
account['free'] = balance;
|
||||
account['used'] = 0.0;
|
||||
account['total'] = balance;
|
||||
} else {
|
||||
account['free'] = this.safeFloat (balance, 'balance');
|
||||
account['used'] = this.safeFloat (balance, 'freeze');
|
||||
}
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let currency = undefined;
|
||||
const request = {
|
||||
'page': 1,
|
||||
};
|
||||
if (code !== undefined) {
|
||||
currency = this.currency (code);
|
||||
request['symbol'] = currency['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit;
|
||||
} else {
|
||||
request['size'] = 100;
|
||||
}
|
||||
const response = await this.privatePostTransfer ({
|
||||
'cmd': 'transfer/transferInList',
|
||||
'body': this.extend (request, params),
|
||||
});
|
||||
const deposits = this.safeValue (response['result'], 'items', []);
|
||||
for (let i = 0; i < deposits.length; i++) {
|
||||
deposits[i]['type'] = 'deposit';
|
||||
}
|
||||
return this.parseTransactions (deposits, currency, since, limit);
|
||||
}
|
||||
|
||||
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let currency = undefined;
|
||||
const request = {
|
||||
'page': 1,
|
||||
};
|
||||
if (code !== undefined) {
|
||||
currency = this.currency (code);
|
||||
request['symbol'] = currency['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit;
|
||||
} else {
|
||||
request['size'] = 100;
|
||||
}
|
||||
const response = await this.privatePostTransfer ({
|
||||
'cmd': 'transfer/transferOutList',
|
||||
'body': this.extend (request, params),
|
||||
});
|
||||
const withdrawals = this.safeValue (response['result'], 'items', []);
|
||||
for (let i = 0; i < withdrawals.length; i++) {
|
||||
withdrawals[i]['type'] = 'withdrawal';
|
||||
}
|
||||
return this.parseTransactions (withdrawals, currency, since, limit);
|
||||
}
|
||||
|
||||
parseTransaction (transaction, currency = undefined) {
|
||||
//
|
||||
// fetchDeposits
|
||||
//
|
||||
// {
|
||||
// 'id': 1023291,
|
||||
// 'coin_symbol': 'ETH',
|
||||
// 'to_address': '0x7263....',
|
||||
// 'amount': '0.49170000',
|
||||
// 'confirmCount': '16',
|
||||
// 'createdAt': 1553123867000,
|
||||
// 'status': 2
|
||||
// }
|
||||
//
|
||||
// fetchWithdrawals
|
||||
//
|
||||
// {
|
||||
// 'id': 521844,
|
||||
// 'coin_symbol': 'ETH',
|
||||
// 'to_address': '0xfd4e....',
|
||||
// 'addr_remark': '',
|
||||
// 'amount': '0.39452750',
|
||||
// 'fee': '0.00600000',
|
||||
// 'createdAt': 1553226906000,
|
||||
// 'memo': '',
|
||||
// 'status': 3
|
||||
// }
|
||||
//
|
||||
const id = this.safeString (transaction, 'id');
|
||||
const address = this.safeString (transaction, 'to_address');
|
||||
const currencyId = this.safeString (transaction, 'coin_symbol');
|
||||
const code = this.safeCurrencyCode (currencyId, currency);
|
||||
const timestamp = this.safeString (transaction, 'createdAt');
|
||||
let tag = this.safeString (transaction, 'addr_remark');
|
||||
const type = this.safeString (transaction, 'type');
|
||||
const status = this.parseTransactionStatusByType (this.safeString (transaction, 'status'), type);
|
||||
const amount = this.safeFloat (transaction, 'amount');
|
||||
let feeCost = this.safeFloat (transaction, 'fee');
|
||||
if (type === 'deposit') {
|
||||
feeCost = 0;
|
||||
tag = undefined;
|
||||
}
|
||||
const fee = {
|
||||
'cost': feeCost,
|
||||
'currency': code,
|
||||
};
|
||||
return {
|
||||
'info': transaction,
|
||||
'id': id,
|
||||
'txid': undefined,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'address': address,
|
||||
'tag': tag,
|
||||
'type': type,
|
||||
'amount': amount,
|
||||
'currency': code,
|
||||
'status': status,
|
||||
'updated': undefined,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
parseTransactionStatusByType (status, type = undefined) {
|
||||
const statuses = {
|
||||
'deposit': {
|
||||
'1': 'pending',
|
||||
'2': 'ok',
|
||||
},
|
||||
'withdrawal': {
|
||||
'0': 'pending',
|
||||
'3': 'ok',
|
||||
},
|
||||
};
|
||||
return this.safeString (this.safeValue (statuses, type, {}), status, status);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const orderType = (type === 'limit') ? 2 : 1;
|
||||
const orderSide = (side === 'buy') ? 1 : 2;
|
||||
const request = {
|
||||
'cmd': 'orderpending/trade',
|
||||
'body': this.extend ({
|
||||
'pair': market['id'],
|
||||
'account_type': 0,
|
||||
'order_type': orderType,
|
||||
'order_side': orderSide,
|
||||
'pay_bix': 0,
|
||||
'amount': amount,
|
||||
'price': price,
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
return {
|
||||
'info': response,
|
||||
'id': this.safeString (response, 'result'),
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
const request = {
|
||||
'cmd': 'orderpending/cancelTrade',
|
||||
'body': this.extend ({
|
||||
'orders_id': id,
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'cmd': 'orderpending/order',
|
||||
'body': this.extend ({
|
||||
'id': id.toString (),
|
||||
'account_type': 0, // 0 = spot account
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
const order = this.safeValue (response, 'result');
|
||||
if (this.isEmpty (order)) {
|
||||
throw new OrderNotFound (this.id + ' order ' + id + ' not found');
|
||||
}
|
||||
return this.parseOrder (order);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
let marketId = undefined;
|
||||
const baseId = this.safeString (order, 'coin_symbol');
|
||||
const quoteId = this.safeString (order, 'currency_symbol');
|
||||
if ((baseId !== undefined) && (quoteId !== undefined)) {
|
||||
marketId = baseId + '_' + quoteId;
|
||||
}
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
}
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const type = (order['order_type'] === 1) ? 'market' : 'limit';
|
||||
const timestamp = order['createdAt'];
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const average = this.safeFloat (order, 'deal_price');
|
||||
const filled = this.safeFloat (order, 'deal_amount');
|
||||
const amount = this.safeFloat (order, 'amount');
|
||||
let cost = this.safeFloat2 (order, 'deal_money', 'money');
|
||||
let remaining = undefined;
|
||||
if (filled !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
remaining = amount - filled;
|
||||
}
|
||||
if (cost === undefined) {
|
||||
cost = price * filled;
|
||||
}
|
||||
}
|
||||
const side = (order['order_side'] === 1) ? 'buy' : 'sell';
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
const id = this.safeString (order, 'id');
|
||||
const feeCost = this.safeFloat (order, 'fee');
|
||||
let fee = undefined;
|
||||
if (feeCost !== undefined) {
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': undefined,
|
||||
};
|
||||
}
|
||||
cost = cost ? cost : (parseFloat (price) * filled);
|
||||
return {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'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': fee,
|
||||
};
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
// original comments from bibox:
|
||||
'1': 'open', // pending
|
||||
'2': 'open', // part completed
|
||||
'3': 'closed', // completed
|
||||
'4': 'canceled', // part canceled
|
||||
'5': 'canceled', // canceled
|
||||
'6': 'canceled', // canceling
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let market = undefined;
|
||||
let pair = undefined;
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
pair = market['id'];
|
||||
}
|
||||
const size = limit ? limit : 200;
|
||||
const request = {
|
||||
'cmd': 'orderpending/orderPendingList',
|
||||
'body': this.extend ({
|
||||
'pair': pair,
|
||||
'account_type': 0, // 0 - regular, 1 - margin
|
||||
'page': 1,
|
||||
'size': size,
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
const orders = this.safeValue (response['result'], 'items', []);
|
||||
return this.parseOrders (orders, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = 200, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'cmd': 'orderpending/pendingHistoryList',
|
||||
'body': this.extend ({
|
||||
'pair': market['id'],
|
||||
'account_type': 0, // 0 - regular, 1 - margin
|
||||
'page': 1,
|
||||
'size': limit,
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
const orders = this.safeValue (response['result'], 'items', []);
|
||||
return this.parseOrders (orders, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const size = limit ? limit : 200;
|
||||
const request = {
|
||||
'cmd': 'orderpending/orderHistoryList',
|
||||
'body': this.extend ({
|
||||
'pair': market['id'],
|
||||
'account_type': 0, // 0 - regular, 1 - margin
|
||||
'page': 1,
|
||||
'size': size,
|
||||
'coin_symbol': market['baseId'],
|
||||
'currency_symbol': market['quoteId'],
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostOrderpending (request);
|
||||
const trades = this.safeValue (response['result'], 'items', []);
|
||||
return this.parseTrades (trades, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchDepositAddress (code, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'cmd': 'transfer/transferIn',
|
||||
'body': this.extend ({
|
||||
'coin_symbol': currency['id'],
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostTransfer (request);
|
||||
//
|
||||
// {
|
||||
// "result":"3Jx6RZ9TNMsAoy9NUzBwZf68QBppDruSKW","cmd":"transfer/transferIn"
|
||||
// }
|
||||
//
|
||||
// {
|
||||
// "result":"{\"account\":\"PERSONALLY OMITTED\",\"memo\":\"PERSONALLY OMITTED\"}","cmd":"transfer/transferIn"
|
||||
// }
|
||||
//
|
||||
const result = this.safeString (response, 'result');
|
||||
let address = result;
|
||||
let tag = undefined;
|
||||
if (this.isJsonEncodedObject (result)) {
|
||||
const parsed = JSON.parse (result);
|
||||
address = this.safeString (parsed, 'account');
|
||||
tag = this.safeString (parsed, 'memo');
|
||||
}
|
||||
return {
|
||||
'currency': code,
|
||||
'address': address,
|
||||
'tag': tag,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
if (this.password === undefined) {
|
||||
if (!('trade_pwd' in params)) {
|
||||
throw new ExchangeError (this.id + ' withdraw() requires this.password set on the exchange instance or a trade_pwd parameter');
|
||||
}
|
||||
}
|
||||
if (!('totp_code' in params)) {
|
||||
throw new ExchangeError (this.id + ' withdraw() requires a totp_code parameter for 2FA authentication');
|
||||
}
|
||||
const request = {
|
||||
'trade_pwd': this.password,
|
||||
'coin_symbol': currency['id'],
|
||||
'amount': amount,
|
||||
'addr': address,
|
||||
};
|
||||
if (tag !== undefined) {
|
||||
request['address_remark'] = tag;
|
||||
}
|
||||
const response = await this.privatePostTransfer ({
|
||||
'cmd': 'transfer/transferOut',
|
||||
'body': this.extend (request, params),
|
||||
});
|
||||
return {
|
||||
'info': response,
|
||||
'id': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchFundingFees (codes = undefined, params = {}) {
|
||||
// by default it will try load withdrawal fees of all currencies (with separate requests)
|
||||
// however if you define codes = [ 'ETH', 'BTC' ] in args it will only load those
|
||||
await this.loadMarkets ();
|
||||
const withdrawFees = {};
|
||||
const info = {};
|
||||
if (codes === undefined) {
|
||||
codes = Object.keys (this.currencies);
|
||||
}
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'cmd': 'transfer/coinConfig',
|
||||
'body': this.extend ({
|
||||
'coin_symbol': currency['id'],
|
||||
}, params),
|
||||
};
|
||||
const response = await this.privatePostTransfer (request);
|
||||
info[code] = response;
|
||||
withdrawFees[code] = this.safeFloat (response['result'], 'withdraw_fee');
|
||||
}
|
||||
return {
|
||||
'info': info,
|
||||
'withdraw': withdrawFees,
|
||||
'deposit': {},
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + this.version + '/' + path;
|
||||
const cmds = this.json ([ params ]);
|
||||
if (api === 'public') {
|
||||
if (method !== 'GET') {
|
||||
body = { 'cmds': cmds };
|
||||
} else if (Object.keys (params).length) {
|
||||
url += '?' + this.urlencode (params);
|
||||
}
|
||||
} else if (api === 'v2private') {
|
||||
this.checkRequiredCredentials ();
|
||||
url = this.urls['api'] + '/v2/' + path;
|
||||
const json_params = this.json (params);
|
||||
body = {
|
||||
'body': json_params,
|
||||
'apikey': this.apiKey,
|
||||
'sign': this.hmac (this.encode (json_params), this.encode (this.secret), 'md5'),
|
||||
};
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
body = {
|
||||
'cmds': cmds,
|
||||
'apikey': this.apiKey,
|
||||
'sign': this.hmac (this.encode (cmds), this.encode (this.secret), 'md5'),
|
||||
};
|
||||
}
|
||||
if (body !== undefined) {
|
||||
body = this.json (body, { 'convertArraysToObjects': true });
|
||||
}
|
||||
headers = { 'Content-Type': 'application/json' };
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
|
||||
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if ('error' in response) {
|
||||
if ('code' in response['error']) {
|
||||
const code = this.safeString (response['error'], 'code');
|
||||
const feedback = this.id + ' ' + body;
|
||||
this.throwExactlyMatchedException (this.exceptions, code, feedback);
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
throw new ExchangeError (this.id + ' ' + body);
|
||||
}
|
||||
if (!('result' in response)) {
|
||||
throw new ExchangeError (this.id + ' ' + body);
|
||||
}
|
||||
}
|
||||
|
||||
async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const response = await this.fetch2 (path, api, method, params, headers, body);
|
||||
if (method === 'GET') {
|
||||
return response;
|
||||
} else {
|
||||
return response['result'][0];
|
||||
}
|
||||
}
|
||||
};
|
||||
+1255
File diff suppressed because it is too large.
Load diff
+1908
File diff suppressed because it is too large.
Load diff
+53
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const binance = require ('./binance.js');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class binanceje extends binance {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'binanceje',
|
||||
'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': {
|
||||
'web': 'https://www.binance.je',
|
||||
'wapi': 'https://api.binance.je/wapi/v3',
|
||||
'public': 'https://api.binance.je/api/v1',
|
||||
'private': 'https://api.binance.je/api/v3',
|
||||
'v3': 'https://api.binance.je/api/v3',
|
||||
'v1': 'https://api.binance.je/api/v1',
|
||||
},
|
||||
'www': 'https://www.binance.je',
|
||||
'referral': 'https://www.binance.je/?ref=35047921',
|
||||
'doc': 'https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md',
|
||||
'fees': 'https://www.binance.je/fees.html',
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'taker': 0.0005,
|
||||
'maker': 0.0005,
|
||||
},
|
||||
// should be deleted, these are outdated and inaccurate
|
||||
'funding': {
|
||||
'tierBased': false,
|
||||
'percentage': false,
|
||||
'withdraw': {
|
||||
'BTC': 0.0005,
|
||||
'ETH': 0.01,
|
||||
},
|
||||
'deposit': {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const binance = require ('./binance.js');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class binanceus extends binance {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'binanceus',
|
||||
'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': {
|
||||
'web': 'https://www.binance.us',
|
||||
'wapi': 'https://api.binance.us/wapi/v3',
|
||||
'public': 'https://api.binance.us/api/v1',
|
||||
'private': 'https://api.binance.us/api/v3',
|
||||
'v3': 'https://api.binance.us/api/v3',
|
||||
'v1': 'https://api.binance.us/api/v1',
|
||||
},
|
||||
'www': 'https://www.binance.us',
|
||||
'referral': 'https://www.binance.us/?ref=35005074',
|
||||
'doc': 'https://github.com/binance-us/binance-official-api-docs',
|
||||
'fees': 'https://www.binance.us/en/fee/schedule',
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'taker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1
|
||||
'maker': 0.0, // 0.1% trading fee, zero fees for all trading pairs before November 1
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ArgumentsRequired, ExchangeError } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bit2c extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bit2c',
|
||||
'name': 'Bit2C',
|
||||
'countries': [ 'IL' ], // Israel
|
||||
'rateLimit': 3000,
|
||||
'has': {
|
||||
'CORS': false,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchMyTrades': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/27766119-3593220e-5ece-11e7-8b3a-5a041f6bcc3f.jpg',
|
||||
'api': 'https://bit2c.co.il',
|
||||
'www': 'https://www.bit2c.co.il',
|
||||
'referral': 'https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf',
|
||||
'doc': [
|
||||
'https://www.bit2c.co.il/home/api',
|
||||
'https://github.com/OferE/bit2c',
|
||||
],
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'Exchanges/{pair}/Ticker',
|
||||
'Exchanges/{pair}/orderbook',
|
||||
'Exchanges/{pair}/trades',
|
||||
'Exchanges/{pair}/lasttrades',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'Merchant/CreateCheckout',
|
||||
'Order/AddCoinFundsRequest',
|
||||
'Order/AddFund',
|
||||
'Order/AddOrder',
|
||||
'Order/AddOrderMarketPriceBuy',
|
||||
'Order/AddOrderMarketPriceSell',
|
||||
'Order/CancelOrder',
|
||||
'Order/AddCoinFundsRequest',
|
||||
'Order/AddStopOrder',
|
||||
'Payment/GetMyId',
|
||||
'Payment/Send',
|
||||
'Payment/Pay',
|
||||
],
|
||||
'get': [
|
||||
'Account/Balance',
|
||||
'Account/Balance/v2',
|
||||
'Order/MyOrders',
|
||||
'Order/GetById',
|
||||
'Order/AccountHistory',
|
||||
'Order/OrderHistory',
|
||||
],
|
||||
},
|
||||
},
|
||||
'markets': {
|
||||
'BTC/NIS': { 'id': 'BtcNis', 'symbol': 'BTC/NIS', 'base': 'BTC', 'quote': 'NIS', 'baseId': 'Btc', 'quoteId': 'Nis' },
|
||||
'ETH/NIS': { 'id': 'EthNis', 'symbol': 'ETH/NIS', 'base': 'ETH', 'quote': 'NIS', 'baseId': 'Eth', 'quoteId': 'Nis' },
|
||||
'BCH/NIS': { 'id': 'BchabcNis', 'symbol': 'BCH/NIS', 'base': 'BCH', 'quote': 'NIS', 'baseId': 'Bchabc', 'quoteId': 'Nis' },
|
||||
'LTC/NIS': { 'id': 'LtcNis', 'symbol': 'LTC/NIS', 'base': 'LTC', 'quote': 'NIS', 'baseId': 'Ltc', 'quoteId': 'Nis' },
|
||||
'ETC/NIS': { 'id': 'EtcNis', 'symbol': 'ETC/NIS', 'base': 'ETC', 'quote': 'NIS', 'baseId': 'Etc', 'quoteId': 'Nis' },
|
||||
'BTG/NIS': { 'id': 'BtgNis', 'symbol': 'BTG/NIS', 'base': 'BTG', 'quote': 'NIS', 'baseId': 'Btg', 'quoteId': 'Nis' },
|
||||
'BSV/NIS': { 'id': 'BchsvNis', 'symbol': 'BSV/NIS', 'base': 'BSV', 'quote': 'NIS', 'baseId': 'Bchsv', 'quoteId': 'Nis' },
|
||||
'GRIN/NIS': { 'id': 'GrinNis', 'symbol': 'GRIN/NIS', 'base': 'GRIN', 'quote': 'NIS', 'baseId': 'Grin', 'quoteId': 'Nis' },
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': 0.5 / 100,
|
||||
'taker': 0.5 / 100,
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'fetchTradesMethod': 'public_get_exchanges_pair_lasttrades',
|
||||
},
|
||||
'exceptions': {
|
||||
// { "error" : "Please provide valid APIkey" }
|
||||
// { "error" : "Please provide valid nonce in Request UInt64.TryParse failed for nonce :" }
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const balance = await this.privateGetAccountBalanceV2 (params);
|
||||
//
|
||||
// {
|
||||
// "AVAILABLE_NIS": 0.0,
|
||||
// "NIS": 0.0,
|
||||
// "LOCKED_NIS": 0.0,
|
||||
// "AVAILABLE_BTC": 0.0,
|
||||
// "BTC": 0.0,
|
||||
// "LOCKED_BTC": 0.0,
|
||||
// "AVAILABLE_ETH": 0.0,
|
||||
// "ETH": 0.0,
|
||||
// "LOCKED_ETH": 0.0,
|
||||
// "AVAILABLE_BCHSV": 0.0,
|
||||
// "BCHSV": 0.0,
|
||||
// "LOCKED_BCHSV": 0.0,
|
||||
// "AVAILABLE_BCHABC": 0.0,
|
||||
// "BCHABC": 0.0,
|
||||
// "LOCKED_BCHABC": 0.0,
|
||||
// "AVAILABLE_LTC": 0.0,
|
||||
// "LTC": 0.0,
|
||||
// "LOCKED_LTC": 0.0,
|
||||
// "AVAILABLE_ETC": 0.0,
|
||||
// "ETC": 0.0,
|
||||
// "LOCKED_ETC": 0.0,
|
||||
// "AVAILABLE_BTG": 0.0,
|
||||
// "BTG": 0.0,
|
||||
// "LOCKED_BTG": 0.0,
|
||||
// "AVAILABLE_GRIN": 0.0,
|
||||
// "GRIN": 0.0,
|
||||
// "LOCKED_GRIN": 0.0,
|
||||
// "Fees": {
|
||||
// "BtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "EthNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "BchabcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "LtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "EtcNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "BtgNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "LtcBtc": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "BchsvNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 },
|
||||
// "GrinNis": { "FeeMaker": 1.0, "FeeTaker": 1.0 }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
const result = { 'info': balance };
|
||||
const codes = Object.keys (this.currencies);
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const account = this.account ();
|
||||
const currencyId = this.currencyId (code);
|
||||
const uppercase = currencyId.toUpperCase ();
|
||||
if (uppercase in balance) {
|
||||
account['free'] = this.safeFloat (balance, 'AVAILABLE_' + uppercase);
|
||||
account['total'] = this.safeFloat (balance, uppercase);
|
||||
}
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'pair': this.marketId (symbol),
|
||||
};
|
||||
const orderbook = await this.publicGetExchangesPairOrderbook (this.extend (request, params));
|
||||
return this.parseOrderBook (orderbook);
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'pair': this.marketId (symbol),
|
||||
};
|
||||
const ticker = await this.publicGetExchangesPairTicker (this.extend (request, params));
|
||||
const timestamp = this.milliseconds ();
|
||||
const averagePrice = this.safeFloat (ticker, 'av');
|
||||
const baseVolume = this.safeFloat (ticker, 'a');
|
||||
let quoteVolume = undefined;
|
||||
if (baseVolume !== undefined && averagePrice !== undefined) {
|
||||
quoteVolume = baseVolume * averagePrice;
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'll');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': undefined,
|
||||
'low': undefined,
|
||||
'bid': this.safeFloat (ticker, 'h'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'l'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': averagePrice,
|
||||
'baseVolume': baseVolume,
|
||||
'quoteVolume': quoteVolume,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const method = this.options['fetchTradesMethod'];
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this[method] (this.extend (request, params));
|
||||
if (typeof response === 'string') {
|
||||
throw new ExchangeError (response);
|
||||
}
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let method = 'privatePostOrderAddOrder';
|
||||
const request = {
|
||||
'Amount': amount,
|
||||
'Pair': this.marketId (symbol),
|
||||
};
|
||||
if (type === 'market') {
|
||||
method += 'MarketPrice' + this.capitalize (side);
|
||||
} else {
|
||||
request['Price'] = price;
|
||||
request['Total'] = amount * price;
|
||||
request['IsBid'] = (side === 'buy');
|
||||
}
|
||||
const response = await this[method] (this.extend (request, params));
|
||||
return {
|
||||
'info': response,
|
||||
'id': response['NewOrder']['id'],
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
return await this.privatePostOrderCancelOrder (this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.privateGetOrderMyOrders (this.extend (request, params));
|
||||
const orders = this.safeValue (response, market['id'], {});
|
||||
const asks = this.safeValue (orders, 'ask', []);
|
||||
const bids = this.safeValue (orders, 'bid', []);
|
||||
return this.parseOrders (this.arrayConcat (asks, bids), market, since, limit);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const timestamp = this.safeInteger (order, 'created');
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const amount = this.safeFloat (order, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = price * amount;
|
||||
}
|
||||
}
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
let side = this.safeValue (order, 'type');
|
||||
if (side === 0) {
|
||||
side = 'buy';
|
||||
} else if (side === 1) {
|
||||
side = 'sell';
|
||||
}
|
||||
const id = this.safeString (order, 'id');
|
||||
const status = this.safeString (order, 'status');
|
||||
return {
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'filled': undefined,
|
||||
'remaining': undefined,
|
||||
'cost': cost,
|
||||
'trades': undefined,
|
||||
'fee': undefined,
|
||||
'info': order,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let market = undefined;
|
||||
const request = {};
|
||||
if (limit !== undefined) {
|
||||
request['take'] = limit;
|
||||
}
|
||||
request['take'] = limit;
|
||||
if (since !== undefined) {
|
||||
request['toTime'] = this.ymd (this.milliseconds (), '.');
|
||||
request['fromTime'] = this.ymd (since, '.');
|
||||
}
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
request['pair'] = market['id'];
|
||||
}
|
||||
const response = await this.privateGetOrderOrderHistory (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
let timestamp = undefined;
|
||||
let id = undefined;
|
||||
let price = undefined;
|
||||
let amount = undefined;
|
||||
let orderId = undefined;
|
||||
let feeCost = undefined;
|
||||
let side = undefined;
|
||||
const reference = this.safeString (trade, 'reference');
|
||||
if (reference !== undefined) {
|
||||
timestamp = this.safeTimestamp (trade, 'ticks');
|
||||
price = this.safeFloat (trade, 'price');
|
||||
amount = this.safeFloat (trade, 'firstAmount');
|
||||
const reference_parts = reference.split ('|'); // reference contains 'pair|orderId|tradeId'
|
||||
if (market === undefined) {
|
||||
const marketId = this.safeString (trade, 'pair');
|
||||
if (marketId in this.markets_by_id[marketId]) {
|
||||
market = this.markets_by_id[marketId];
|
||||
} else if (reference_parts[0] in this.markets_by_id) {
|
||||
market = this.markets_by_id[reference_parts[0]];
|
||||
}
|
||||
}
|
||||
orderId = reference_parts[1];
|
||||
id = reference_parts[2];
|
||||
side = this.safeInteger (trade, 'action');
|
||||
if (side === 0) {
|
||||
side = 'buy';
|
||||
} else if (side === 1) {
|
||||
side = 'sell';
|
||||
}
|
||||
feeCost = this.safeFloat (trade, 'feeAmount');
|
||||
} else {
|
||||
timestamp = this.safeTimestamp (trade, 'date');
|
||||
id = this.safeString (trade, 'tid');
|
||||
price = this.safeFloat (trade, 'price');
|
||||
amount = this.safeFloat (trade, 'amount');
|
||||
side = this.safeValue (trade, 'isBid');
|
||||
if (side !== undefined) {
|
||||
if (side) {
|
||||
side = 'buy';
|
||||
} else {
|
||||
side = 'sell';
|
||||
}
|
||||
}
|
||||
}
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': orderId,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'takerOrMaker': undefined,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': price * amount,
|
||||
'fee': {
|
||||
'cost': feeCost,
|
||||
'currency': 'NIS',
|
||||
'rate': undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + this.implodeParams (path, params);
|
||||
if (api === 'public') {
|
||||
url += '.json';
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ();
|
||||
const query = this.extend ({
|
||||
'nonce': nonce,
|
||||
}, params);
|
||||
const auth = this.urlencode (query);
|
||||
if (method === 'GET') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + auth;
|
||||
}
|
||||
} else {
|
||||
body = auth;
|
||||
}
|
||||
const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512', 'base64');
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'key': this.apiKey,
|
||||
'sign': this.decode (signature),
|
||||
};
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
};
|
||||
|
||||
+566
@@ -0,0 +1,566 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, AuthenticationError, InvalidNonce, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitbank extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitbank',
|
||||
'name': 'bitbank',
|
||||
'countries': [ 'JP' ],
|
||||
'version': 'v1',
|
||||
'has': {
|
||||
'fetchOHLCV': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchMyTrades': true,
|
||||
'fetchDepositAddress': true,
|
||||
'withdraw': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': '1min',
|
||||
'5m': '5min',
|
||||
'15m': '15min',
|
||||
'30m': '30min',
|
||||
'1h': '1hour',
|
||||
'4h': '4hour',
|
||||
'8h': '8hour',
|
||||
'12h': '12hour',
|
||||
'1d': '1day',
|
||||
'1w': '1week',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg',
|
||||
'api': {
|
||||
'public': 'https://public.bitbank.cc',
|
||||
'private': 'https://api.bitbank.cc',
|
||||
},
|
||||
'www': 'https://bitbank.cc/',
|
||||
'doc': 'https://docs.bitbank.cc/',
|
||||
'fees': 'https://bitbank.cc/docs/fees/',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'{pair}/ticker',
|
||||
'{pair}/depth',
|
||||
'{pair}/transactions',
|
||||
'{pair}/transactions/{yyyymmdd}',
|
||||
'{pair}/candlestick/{candletype}/{yyyymmdd}',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'user/assets',
|
||||
'user/spot/order',
|
||||
'user/spot/active_orders',
|
||||
'user/spot/trade_history',
|
||||
'user/withdrawal_account',
|
||||
],
|
||||
'post': [
|
||||
'user/spot/order',
|
||||
'user/spot/cancel_order',
|
||||
'user/spot/cancel_orders',
|
||||
'user/spot/orders_info',
|
||||
'user/request_withdrawal',
|
||||
],
|
||||
},
|
||||
},
|
||||
'markets': {
|
||||
'BCH/BTC': { 'id': 'bcc_btc', 'symbol': 'BCH/BTC', 'base': 'BCH', 'quote': 'BTC', 'baseId': 'bcc', 'quoteId': 'btc' },
|
||||
'BCH/JPY': { 'id': 'bcc_jpy', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bcc', 'quoteId': 'jpy' },
|
||||
'MONA/BTC': { 'id': 'mona_btc', 'symbol': 'MONA/BTC', 'base': 'MONA', 'quote': 'BTC', 'baseId': 'mona', 'quoteId': 'btc' },
|
||||
'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' },
|
||||
'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' },
|
||||
'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' },
|
||||
'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' },
|
||||
'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' },
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': -0.02 / 100,
|
||||
'taker': 0.12 / 100,
|
||||
},
|
||||
'funding': {
|
||||
'withdraw': {
|
||||
// 'JPY': (amount > 30000) ? 756 : 540,
|
||||
'BTC': 0.001,
|
||||
'LTC': 0.001,
|
||||
'XRP': 0.15,
|
||||
'ETH': 0.0005,
|
||||
'MONA': 0.001,
|
||||
'BCC': 0.001,
|
||||
},
|
||||
},
|
||||
},
|
||||
'precision': {
|
||||
'price': 8,
|
||||
'amount': 8,
|
||||
},
|
||||
'exceptions': {
|
||||
'20001': AuthenticationError,
|
||||
'20002': AuthenticationError,
|
||||
'20003': AuthenticationError,
|
||||
'20005': AuthenticationError,
|
||||
'20004': InvalidNonce,
|
||||
'40020': InvalidOrder,
|
||||
'40021': InvalidOrder,
|
||||
'40025': ExchangeError,
|
||||
'40013': OrderNotFound,
|
||||
'40014': OrderNotFound,
|
||||
'50008': PermissionDenied,
|
||||
'50009': OrderNotFound,
|
||||
'50010': OrderNotFound,
|
||||
'60001': InsufficientFunds,
|
||||
'60005': InvalidOrder,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeInteger (ticker, 'timestamp');
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'high'),
|
||||
'low': this.safeFloat (ticker, 'low'),
|
||||
'bid': this.safeFloat (ticker, 'buy'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'sell'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (ticker, 'vol'),
|
||||
'quoteVolume': undefined,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.publicGetPairTicker (this.extend (request, params));
|
||||
return this.parseTicker (response['data'], market);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'pair': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetPairDepth (this.extend (request, params));
|
||||
const orderbook = this.safeValue (response, 'data', {});
|
||||
const timestamp = this.safeInteger (orderbook, 'timestamp');
|
||||
return this.parseOrderBook (orderbook, timestamp);
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.safeInteger (trade, 'executed_at');
|
||||
let symbol = undefined;
|
||||
let feeCurrency = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
feeCurrency = market['quote'];
|
||||
}
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = parseFloat (this.costToPrecision (symbol, price * amount));
|
||||
}
|
||||
}
|
||||
const id = this.safeString2 (trade, 'transaction_id', 'trade_id');
|
||||
const takerOrMaker = this.safeString (trade, 'maker_taker');
|
||||
let fee = undefined;
|
||||
const feeCost = this.safeFloat (trade, 'fee_amount_quote');
|
||||
if (feeCost !== undefined) {
|
||||
fee = {
|
||||
'currency': market['quote'],
|
||||
'cost': feeCurrency,
|
||||
};
|
||||
}
|
||||
const orderId = this.safeString (trade, 'order_id');
|
||||
const type = this.safeString (trade, 'type');
|
||||
const side = this.safeString (trade, 'side');
|
||||
return {
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'id': id,
|
||||
'order': orderId,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'takerOrMaker': takerOrMaker,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': fee,
|
||||
'info': trade,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.publicGetPairTransactions (this.extend (request, params));
|
||||
return this.parseTrades (response['data']['transactions'], market, since, limit);
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
ohlcv[5],
|
||||
parseFloat (ohlcv[0]),
|
||||
parseFloat (ohlcv[1]),
|
||||
parseFloat (ohlcv[2]),
|
||||
parseFloat (ohlcv[3]),
|
||||
parseFloat (ohlcv[4]),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
let date = this.milliseconds ();
|
||||
date = this.ymd (date);
|
||||
date = date.split ('-');
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
'candletype': this.timeframes[timeframe],
|
||||
'yyyymmdd': date.join (''),
|
||||
};
|
||||
const response = await this.publicGetPairCandlestickCandletypeYyyymmdd (this.extend (request, params));
|
||||
const ohlcv = this.safeValue (response['data']['candlestick'][0], 'ohlcv');
|
||||
return this.parseOHLCVs (ohlcv, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetUserAssets (params);
|
||||
const result = { 'info': response };
|
||||
const balances = response['data']['assets'];
|
||||
for (let i = 0; i < balances.length; i++) {
|
||||
const balance = balances[i];
|
||||
const currencyId = this.safeString (balance, 'asset');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = {
|
||||
'free': this.safeFloat (balance, 'free_amount'),
|
||||
'used': this.safeFloat (balance, 'locked_amount'),
|
||||
'total': this.safeFloat (balance, 'onhand_amount'),
|
||||
};
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'UNFILLED': 'open',
|
||||
'PARTIALLY_FILLED': 'open',
|
||||
'FULLY_FILLED': 'closed',
|
||||
'CANCELED_UNFILLED': 'canceled',
|
||||
'CANCELED_PARTIALLY_FILLED': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const id = this.safeString (order, 'order_id');
|
||||
const marketId = this.safeString (order, 'pair');
|
||||
let symbol = undefined;
|
||||
if (marketId && !market && (marketId in this.marketsById)) {
|
||||
market = this.marketsById[marketId];
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeInteger (order, 'ordered_at');
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const amount = this.safeFloat (order, 'start_amount');
|
||||
const filled = this.safeFloat (order, 'executed_amount');
|
||||
const remaining = this.safeFloat (order, 'remaining_amount');
|
||||
const average = this.safeFloat (order, 'average_price');
|
||||
let cost = undefined;
|
||||
if (filled !== undefined) {
|
||||
if (average !== undefined) {
|
||||
cost = filled * average;
|
||||
}
|
||||
}
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
const type = this.safeStringLower (order, 'type');
|
||||
const side = this.safeStringLower (order, 'side');
|
||||
return {
|
||||
'id': id,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'timestamp': timestamp,
|
||||
'lastTradeTimestamp': undefined,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'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);
|
||||
if (price === undefined) {
|
||||
throw new InvalidOrder (this.id + ' createOrder requires a price argument for both market and limit orders');
|
||||
}
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
'amount': this.amountToPrecision (symbol, amount),
|
||||
'price': this.priceToPrecision (symbol, price),
|
||||
'side': side,
|
||||
'type': type,
|
||||
};
|
||||
const response = await this.privatePostUserSpotOrder (this.extend (request, params));
|
||||
const order = this.parseOrder (response['data'], market);
|
||||
const id = order['id'];
|
||||
this.orders[id] = order;
|
||||
return order;
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'order_id': id,
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.privatePostUserSpotCancelOrder (this.extend (request, params));
|
||||
return response['data'];
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'order_id': id,
|
||||
'pair': market['id'],
|
||||
};
|
||||
const response = await this.privateGetUserSpotOrder (this.extend (request, params));
|
||||
return this.parseOrder (response['data']);
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['count'] = limit;
|
||||
}
|
||||
if (since !== undefined) {
|
||||
request['since'] = parseInt (since / 1000);
|
||||
}
|
||||
const response = await this.privateGetUserSpotActiveOrders (this.extend (request, params));
|
||||
return this.parseOrders (response['data']['orders'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let market = undefined;
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
}
|
||||
const request = {};
|
||||
if (market !== undefined) {
|
||||
request['pair'] = market['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['count'] = limit;
|
||||
}
|
||||
if (since !== undefined) {
|
||||
request['since'] = parseInt (since / 1000);
|
||||
}
|
||||
const response = await this.privateGetUserSpotTradeHistory (this.extend (request, params));
|
||||
return this.parseTrades (response['data']['trades'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchDepositAddress (code, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'asset': currency['id'],
|
||||
};
|
||||
const response = await this.privateGetUserWithdrawalAccount (this.extend (request, params));
|
||||
// Not sure about this if there could be more than one account...
|
||||
const accounts = response['data']['accounts'];
|
||||
const address = this.safeString (accounts[0], 'address');
|
||||
return {
|
||||
'currency': currency,
|
||||
'address': address,
|
||||
'tag': undefined,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
if (!('uuid' in params)) {
|
||||
throw new ExchangeError (this.id + ' uuid is required for withdrawal');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'asset': currency['id'],
|
||||
'amount': amount,
|
||||
};
|
||||
const response = await this.privatePostUserRequestWithdrawal (this.extend (request, params));
|
||||
const txid = this.safeString (response['data'], 'txid');
|
||||
return {
|
||||
'info': response,
|
||||
'id': txid,
|
||||
};
|
||||
}
|
||||
|
||||
nonce () {
|
||||
return this.milliseconds ();
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let query = this.omit (params, this.extractParams (path));
|
||||
let url = this.urls['api'][api] + '/';
|
||||
if (api === 'public') {
|
||||
url += this.implodeParams (path, params);
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ().toString ();
|
||||
let auth = nonce;
|
||||
url += this.version + '/' + this.implodeParams (path, params);
|
||||
if (method === 'POST') {
|
||||
body = this.json (query);
|
||||
auth += body;
|
||||
} else {
|
||||
auth += '/' + this.version + '/' + path;
|
||||
if (Object.keys (query).length) {
|
||||
query = this.urlencode (query);
|
||||
url += '?' + query;
|
||||
auth += '?' + query;
|
||||
}
|
||||
}
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'ACCESS-KEY': this.apiKey,
|
||||
'ACCESS-NONCE': nonce,
|
||||
'ACCESS-SIGNATURE': this.hmac (this.encode (auth), this.encode (this.secret)),
|
||||
};
|
||||
}
|
||||
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);
|
||||
const success = this.safeInteger (response, 'success');
|
||||
const data = this.safeValue (response, 'data');
|
||||
if (!success || !data) {
|
||||
const errorMessages = {
|
||||
'10000': 'URL does not exist',
|
||||
'10001': 'A system error occurred. Please contact support',
|
||||
'10002': 'Invalid JSON format. Please check the contents of transmission',
|
||||
'10003': 'A system error occurred. Please contact support',
|
||||
'10005': 'A timeout error occurred. Please wait for a while and try again',
|
||||
'20001': 'API authentication failed',
|
||||
'20002': 'Illegal API key',
|
||||
'20003': 'API key does not exist',
|
||||
'20004': 'API Nonce does not exist',
|
||||
'20005': 'API signature does not exist',
|
||||
'20011': 'Two-step verification failed',
|
||||
'20014': 'SMS authentication failed',
|
||||
'30001': 'Please specify the order quantity',
|
||||
'30006': 'Please specify the order ID',
|
||||
'30007': 'Please specify the order ID array',
|
||||
'30009': 'Please specify the stock',
|
||||
'30012': 'Please specify the order price',
|
||||
'30013': 'Trade Please specify either',
|
||||
'30015': 'Please specify the order type',
|
||||
'30016': 'Please specify asset name',
|
||||
'30019': 'Please specify uuid',
|
||||
'30039': 'Please specify the amount to be withdrawn',
|
||||
'40001': 'The order quantity is invalid',
|
||||
'40006': 'Count value is invalid',
|
||||
'40007': 'End time is invalid',
|
||||
'40008': 'end_id Value is invalid',
|
||||
'40009': 'The from_id value is invalid',
|
||||
'40013': 'The order ID is invalid',
|
||||
'40014': 'The order ID array is invalid',
|
||||
'40015': 'Too many specified orders',
|
||||
'40017': 'Incorrect issue name',
|
||||
'40020': 'The order price is invalid',
|
||||
'40021': 'The trading classification is invalid',
|
||||
'40022': 'Start date is invalid',
|
||||
'40024': 'The order type is invalid',
|
||||
'40025': 'Incorrect asset name',
|
||||
'40028': 'uuid is invalid',
|
||||
'40048': 'The amount of withdrawal is illegal',
|
||||
'50003': 'Currently, this account is in a state where you can not perform the operation you specified. Please contact support',
|
||||
'50004': 'Currently, this account is temporarily registered. Please try again after registering your account',
|
||||
'50005': 'Currently, this account is locked. Please contact support',
|
||||
'50006': 'Currently, this account is locked. Please contact support',
|
||||
'50008': 'User identification has not been completed',
|
||||
'50009': 'Your order does not exist',
|
||||
'50010': 'Can not cancel specified order',
|
||||
'50011': 'API not found',
|
||||
'60001': 'The number of possessions is insufficient',
|
||||
'60002': 'It exceeds the quantity upper limit of the tender buying order',
|
||||
'60003': 'The specified quantity exceeds the limit',
|
||||
'60004': 'The specified quantity is below the threshold',
|
||||
'60005': 'The specified price is above the limit',
|
||||
'60006': 'The specified price is below the lower limit',
|
||||
'70001': 'A system error occurred. Please contact support',
|
||||
'70002': 'A system error occurred. Please contact support',
|
||||
'70003': 'A system error occurred. Please contact support',
|
||||
'70004': 'We are unable to accept orders as the transaction is currently suspended',
|
||||
'70005': 'Order can not be accepted because purchase order is currently suspended',
|
||||
'70006': 'We can not accept orders because we are currently unsubscribed ',
|
||||
'70009': 'We are currently temporarily restricting orders to be carried out. Please use the limit order.',
|
||||
'70010': 'We are temporarily raising the minimum order quantity as the system load is now rising.',
|
||||
};
|
||||
const errorClasses = this.exceptions;
|
||||
const code = this.safeString (data, 'code');
|
||||
const message = this.safeString (errorMessages, code, 'Error');
|
||||
const ErrorClass = this.safeValue (errorClasses, code);
|
||||
if (ErrorClass !== undefined) {
|
||||
throw new ErrorClass (message);
|
||||
} else {
|
||||
throw new ExchangeError (this.id + ' ' + this.json (response));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
+1210
File diff suppressed because it is too large.
Load diff
+1180
File diff suppressed because it is too large.
Load diff
+1157
File diff suppressed because it is too large.
Load diff
+487
@@ -0,0 +1,487 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, ArgumentsRequired, OrderNotFound } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitflyer extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitflyer',
|
||||
'name': 'bitFlyer',
|
||||
'countries': [ 'JP' ],
|
||||
'version': 'v1',
|
||||
'rateLimit': 1000, // their nonce-timestamp is in seconds...
|
||||
'has': {
|
||||
'CORS': false,
|
||||
'withdraw': true,
|
||||
'fetchMyTrades': true,
|
||||
'fetchOrders': true,
|
||||
'fetchOrder': 'emulated',
|
||||
'fetchOpenOrders': 'emulated',
|
||||
'fetchClosedOrders': 'emulated',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg',
|
||||
'api': 'https://api.bitflyer.jp',
|
||||
'www': 'https://bitflyer.jp',
|
||||
'doc': 'https://lightning.bitflyer.com/docs?lang=en',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'getmarkets/usa', // new (wip)
|
||||
'getmarkets/eu', // new (wip)
|
||||
'getmarkets', // or 'markets'
|
||||
'getboard', // ...
|
||||
'getticker',
|
||||
'getexecutions',
|
||||
'gethealth',
|
||||
'getboardstate',
|
||||
'getchats',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'getpermissions',
|
||||
'getbalance',
|
||||
'getbalancehistory',
|
||||
'getcollateral',
|
||||
'getcollateralhistory',
|
||||
'getcollateralaccounts',
|
||||
'getaddresses',
|
||||
'getcoinins',
|
||||
'getcoinouts',
|
||||
'getbankaccounts',
|
||||
'getdeposits',
|
||||
'getwithdrawals',
|
||||
'getchildorders',
|
||||
'getparentorders',
|
||||
'getparentorder',
|
||||
'getexecutions',
|
||||
'getpositions',
|
||||
'gettradingcommission',
|
||||
],
|
||||
'post': [
|
||||
'sendcoin',
|
||||
'withdraw',
|
||||
'sendchildorder',
|
||||
'cancelchildorder',
|
||||
'sendparentorder',
|
||||
'cancelparentorder',
|
||||
'cancelallchildorders',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': 0.2 / 100,
|
||||
'taker': 0.2 / 100,
|
||||
},
|
||||
'BTC/JPY': {
|
||||
'maker': 0.15 / 100,
|
||||
'taker': 0.15 / 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const jp_markets = await this.publicGetGetmarkets (params);
|
||||
const us_markets = await this.publicGetGetmarketsUsa (params);
|
||||
const eu_markets = await this.publicGetGetmarketsEu (params);
|
||||
let markets = this.arrayConcat (jp_markets, us_markets);
|
||||
markets = this.arrayConcat (markets, eu_markets);
|
||||
const result = [];
|
||||
for (let i = 0; i < markets.length; i++) {
|
||||
const market = markets[i];
|
||||
const id = this.safeString (market, 'product_code');
|
||||
const currencies = id.split ('_');
|
||||
let baseId = undefined;
|
||||
let quoteId = undefined;
|
||||
let base = undefined;
|
||||
let quote = undefined;
|
||||
const numCurrencies = currencies.length;
|
||||
if (numCurrencies === 1) {
|
||||
baseId = id.slice (0, 3);
|
||||
quoteId = id.slice (3, 6);
|
||||
} else if (numCurrencies === 2) {
|
||||
baseId = currencies[0];
|
||||
quoteId = currencies[1];
|
||||
} else {
|
||||
baseId = currencies[1];
|
||||
quoteId = currencies[2];
|
||||
}
|
||||
base = this.safeCurrencyCode (baseId);
|
||||
quote = this.safeCurrencyCode (quoteId);
|
||||
const symbol = (numCurrencies === 2) ? (base + '/' + quote) : id;
|
||||
const fees = this.safeValue (this.fees, symbol, this.fees['trading']);
|
||||
let maker = this.safeValue (fees, 'maker', this.fees['trading']['maker']);
|
||||
let taker = this.safeValue (fees, 'taker', this.fees['trading']['taker']);
|
||||
let spot = true;
|
||||
let future = false;
|
||||
let type = 'spot';
|
||||
if (('alias' in market) || (currencies[0] === 'FX')) {
|
||||
type = 'future';
|
||||
future = true;
|
||||
spot = false;
|
||||
maker = 0.0;
|
||||
taker = 0.0;
|
||||
}
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'maker': maker,
|
||||
'taker': taker,
|
||||
'type': type,
|
||||
'spot': spot,
|
||||
'future': future,
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetGetbalance (params);
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "currency_code": "JPY",
|
||||
// "amount": 1024078,
|
||||
// "available": 508000
|
||||
// },
|
||||
// {
|
||||
// "currency_code": "BTC",
|
||||
// "amount": 10.24,
|
||||
// "available": 4.12
|
||||
// },
|
||||
// {
|
||||
// "currency_code": "ETH",
|
||||
// "amount": 20.48,
|
||||
// "available": 16.38
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
const result = { 'info': response };
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
const balance = response[i];
|
||||
const currencyId = this.safeString (balance, 'currency_code');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = this.account ();
|
||||
account['total'] = this.safeFloat (balance, 'amount');
|
||||
account['free'] = this.safeFloat (balance, 'available');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'product_code': this.marketId (symbol),
|
||||
};
|
||||
const orderbook = await this.publicGetGetboard (this.extend (request, params));
|
||||
return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'size');
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'product_code': this.marketId (symbol),
|
||||
};
|
||||
const ticker = await this.publicGetGetticker (this.extend (request, params));
|
||||
const timestamp = this.parse8601 (this.safeString (ticker, 'timestamp'));
|
||||
const last = this.safeFloat (ticker, 'ltp');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': undefined,
|
||||
'low': undefined,
|
||||
'bid': this.safeFloat (ticker, 'best_bid'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'best_ask'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (ticker, 'volume_by_product'),
|
||||
'quoteVolume': undefined,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
let side = this.safeStringLower (trade, 'side');
|
||||
if (side !== undefined) {
|
||||
if (side.length < 1) {
|
||||
side = undefined;
|
||||
}
|
||||
}
|
||||
let order = undefined;
|
||||
if (side !== undefined) {
|
||||
const id = side + '_child_order_acceptance_id';
|
||||
if (id in trade) {
|
||||
order = trade[id];
|
||||
}
|
||||
}
|
||||
if (order === undefined) {
|
||||
order = this.safeString (trade, 'child_order_acceptance_id');
|
||||
}
|
||||
const timestamp = this.parse8601 (this.safeString (trade, 'exec_date'));
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'size');
|
||||
let cost = undefined;
|
||||
if (amount !== undefined) {
|
||||
if (price !== undefined) {
|
||||
cost = price * amount;
|
||||
}
|
||||
}
|
||||
const id = this.safeString (trade, 'id');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': order,
|
||||
'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 request = {
|
||||
'product_code': market['id'],
|
||||
};
|
||||
const response = await this.publicGetGetexecutions (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'product_code': this.marketId (symbol),
|
||||
'child_order_type': type.toUpperCase (),
|
||||
'side': side.toUpperCase (),
|
||||
'price': price,
|
||||
'size': amount,
|
||||
};
|
||||
const result = await this.privatePostSendchildorder (this.extend (request, params));
|
||||
// { "status": - 200, "error_message": "Insufficient funds", "data": null }
|
||||
const id = this.safeString (result, 'child_order_acceptance_id');
|
||||
return {
|
||||
'info': result,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'product_code': this.marketId (symbol),
|
||||
'child_order_acceptance_id': id,
|
||||
};
|
||||
return await this.privatePostCancelchildorder (this.extend (request, params));
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'ACTIVE': 'open',
|
||||
'COMPLETED': 'closed',
|
||||
'CANCELED': 'canceled',
|
||||
'EXPIRED': 'canceled',
|
||||
'REJECTED': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const timestamp = this.parse8601 (this.safeString (order, 'child_order_date'));
|
||||
const amount = this.safeFloat (order, 'size');
|
||||
const remaining = this.safeFloat (order, 'outstanding_size');
|
||||
const filled = this.safeFloat (order, 'executed_size');
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const cost = price * filled;
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'child_order_state'));
|
||||
const type = this.safeStringLower (order, 'child_order_type');
|
||||
const side = this.safeStringLower (order, 'side');
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
const marketId = this.safeString (order, 'product_code');
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
}
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
let fee = undefined;
|
||||
const feeCost = this.safeFloat (order, 'total_commission');
|
||||
if (feeCost !== undefined) {
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': undefined,
|
||||
'rate': undefined,
|
||||
};
|
||||
}
|
||||
const id = this.safeString (order, 'child_order_acceptance_id');
|
||||
return {
|
||||
'id': id,
|
||||
'info': order,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'cost': cost,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchOrders (symbol = undefined, since = undefined, limit = 100, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'product_code': market['id'],
|
||||
'count': limit,
|
||||
};
|
||||
const response = await this.privateGetGetchildorders (this.extend (request, params));
|
||||
let orders = this.parseOrders (response, market, since, limit);
|
||||
if (symbol !== undefined) {
|
||||
orders = this.filterBy (orders, 'symbol', symbol);
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = 100, params = {}) {
|
||||
const request = {
|
||||
'child_order_state': 'ACTIVE',
|
||||
};
|
||||
return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = 100, params = {}) {
|
||||
const request = {
|
||||
'child_order_state': 'COMPLETED',
|
||||
};
|
||||
return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a `symbol` argument');
|
||||
}
|
||||
const orders = await this.fetchOrders (symbol);
|
||||
const ordersById = this.indexBy (orders, 'id');
|
||||
if (id in ordersById) {
|
||||
return ordersById[id];
|
||||
}
|
||||
throw new OrderNotFound (this.id + ' No order found with id ' + id);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'product_code': market['id'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['count'] = limit;
|
||||
}
|
||||
const response = await this.privateGetGetexecutions (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
if (code !== 'JPY' && code !== 'USD' && code !== 'EUR') {
|
||||
throw new ExchangeError (this.id + ' allows withdrawing JPY, USD, EUR only, ' + code + ' is not supported');
|
||||
}
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'currency_code': currency['id'],
|
||||
'amount': amount,
|
||||
// 'bank_account_id': 1234,
|
||||
};
|
||||
const response = await this.privatePostWithdraw (this.extend (request, params));
|
||||
const id = this.safeString (response, 'message_id');
|
||||
return {
|
||||
'info': response,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let request = '/' + this.version + '/';
|
||||
if (api === 'private') {
|
||||
request += 'me/';
|
||||
}
|
||||
request += path;
|
||||
if (method === 'GET') {
|
||||
if (Object.keys (params).length) {
|
||||
request += '?' + this.urlencode (params);
|
||||
}
|
||||
}
|
||||
const url = this.urls['api'] + request;
|
||||
if (api === 'private') {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ().toString ();
|
||||
let auth = [ nonce, method, request ].join ('');
|
||||
if (Object.keys (params).length) {
|
||||
if (method !== 'GET') {
|
||||
body = this.json (params);
|
||||
auth += body;
|
||||
}
|
||||
}
|
||||
headers = {
|
||||
'ACCESS-KEY': this.apiKey,
|
||||
'ACCESS-TIMESTAMP': nonce,
|
||||
'ACCESS-SIGN': this.hmac (this.encode (auth), this.encode (this.secret)),
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
};
|
||||
+597
@@ -0,0 +1,597 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitforex extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitforex',
|
||||
'name': 'Bitforex',
|
||||
'countries': [ 'CN' ],
|
||||
'version': 'v1',
|
||||
'has': {
|
||||
'fetchBalance': true,
|
||||
'fetchMarkets': true,
|
||||
'createOrder': true,
|
||||
'cancelOrder': true,
|
||||
'fetchTicker': true,
|
||||
'fetchTickers': false,
|
||||
'fetchMyTrades': false,
|
||||
'fetchTrades': true,
|
||||
'fetchOrder': true,
|
||||
'fetchOrders': false,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchClosedOrders': true,
|
||||
'fetchOHLCV': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': '1min',
|
||||
'5m': '5min',
|
||||
'15m': '15min',
|
||||
'30m': '30min',
|
||||
'1h': '1hour',
|
||||
'2h': '2hour',
|
||||
'4h': '4hour',
|
||||
'12h': '12hour',
|
||||
'1d': '1day',
|
||||
'1w': '1week',
|
||||
'1M': '1month',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/44310033-69e9e600-a3d8-11e8-873d-54d74d1bc4e4.jpg',
|
||||
'api': 'https://api.bitforex.com',
|
||||
'www': 'https://www.bitforex.com',
|
||||
'doc': 'https://github.com/githubdev2020/API_Doc_en/wiki',
|
||||
'fees': 'https://help.bitforex.com/en_us/?cat=13',
|
||||
'referral': 'https://www.bitforex.com/en/invitationRegister?inviterId=1867438',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'api/v1/market/symbols',
|
||||
'api/v1/market/ticker',
|
||||
'api/v1/market/depth',
|
||||
'api/v1/market/trades',
|
||||
'api/v1/market/kline',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'api/v1/fund/mainAccount',
|
||||
'api/v1/fund/allAccount',
|
||||
'api/v1/trade/placeOrder',
|
||||
'api/v1/trade/placeMultiOrder',
|
||||
'api/v1/trade/cancelOrder',
|
||||
'api/v1/trade/orderInfo',
|
||||
'api/v1/trade/orderInfos',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'maker': 0.1 / 100,
|
||||
'taker': 0.1 / 100,
|
||||
},
|
||||
'funding': {
|
||||
'tierBased': false,
|
||||
'percentage': true,
|
||||
'deposit': {},
|
||||
'withdraw': {
|
||||
'BTC': 0.0005,
|
||||
'ETH': 0.01,
|
||||
'BCH': 0.0001,
|
||||
'LTC': 0.001,
|
||||
'ETC': 0.005,
|
||||
'USDT': 5,
|
||||
'CMCT': 30,
|
||||
'AION': 3,
|
||||
'LVT': 0,
|
||||
'DATA': 40,
|
||||
'RHP': 50,
|
||||
'NEO': 0,
|
||||
'AIDOC': 10,
|
||||
'BQT': 2,
|
||||
'R': 2,
|
||||
'DPY': 0.8,
|
||||
'GTC': 40,
|
||||
'AGI': 30,
|
||||
'DENT': 100,
|
||||
'SAN': 1,
|
||||
'SPANK': 8,
|
||||
'AID': 5,
|
||||
'OMG': 0.1,
|
||||
'BFT': 5,
|
||||
'SHOW': 150,
|
||||
'TRX': 20,
|
||||
'ABYSS': 10,
|
||||
'THM': 25,
|
||||
'ZIL': 20,
|
||||
'PPT': 0.2,
|
||||
'WTC': 0.4,
|
||||
'LRC': 7,
|
||||
'BNT': 1,
|
||||
'CTXC': 1,
|
||||
'MITH': 20,
|
||||
'TRUE': 4,
|
||||
'LYM': 10,
|
||||
'VEE': 100,
|
||||
'AUTO': 200,
|
||||
'REN': 50,
|
||||
'TIO': 2.5,
|
||||
'NGC': 1.5,
|
||||
'PST': 10,
|
||||
'CRE': 200,
|
||||
'IPC': 5,
|
||||
'PTT': 1000,
|
||||
'XMCT': 20,
|
||||
'ATMI': 40,
|
||||
'TERN': 40,
|
||||
'XLM': 0.01,
|
||||
'ODE': 15,
|
||||
'FTM': 100,
|
||||
'RTE': 100,
|
||||
'DCC': 100,
|
||||
'IMT': 500,
|
||||
'GOT': 3,
|
||||
'EGT': 500,
|
||||
'DACC': 1000,
|
||||
'UBEX': 500,
|
||||
'ABL': 100,
|
||||
'OLT': 100,
|
||||
'DAV': 40,
|
||||
'THRT': 10,
|
||||
'RMESH': 3,
|
||||
'UPP': 20,
|
||||
'SDT': 0,
|
||||
'SHR': 10,
|
||||
'MTV': 3,
|
||||
'ESS': 100,
|
||||
'MET': 3,
|
||||
'TTC': 20,
|
||||
'LXT': 10,
|
||||
'XCLP': 100,
|
||||
'LUK': 100,
|
||||
'UBC': 100,
|
||||
'DTX': 10,
|
||||
'BEAT': 20,
|
||||
'DEED': 2,
|
||||
'BGX': 3000,
|
||||
'PRL': 20,
|
||||
'ELY': 50,
|
||||
'CARD': 300,
|
||||
'SQR': 15,
|
||||
'VRA': 400,
|
||||
'BWX': 3500,
|
||||
'MAS': 75,
|
||||
'FLP': 0.6,
|
||||
'UNC': 300,
|
||||
'CRNC': 15,
|
||||
'MFG': 70,
|
||||
'ZXC': 70,
|
||||
'TRT': 30,
|
||||
'ZIX': 35,
|
||||
'XRA': 10,
|
||||
'AMO': 1600,
|
||||
'IPG': 3,
|
||||
'uDoo': 50,
|
||||
'URB': 30,
|
||||
'ARCONA': 3,
|
||||
'CRAD': 5,
|
||||
'NOBS': 1000,
|
||||
'ADF': 2,
|
||||
'ELF': 5,
|
||||
'LX': 20,
|
||||
'PATH': 15,
|
||||
'SILK': 120,
|
||||
'SKYFT': 50,
|
||||
'EDN': 50,
|
||||
'ADE': 50,
|
||||
'EDR': 10,
|
||||
'TIME': 0.25,
|
||||
'SPRK': 20,
|
||||
'QTUM': 0.01,
|
||||
'BF': 5,
|
||||
'ZPR': 100,
|
||||
'HYB': 10,
|
||||
'CAN': 30,
|
||||
'CEL': 10,
|
||||
'ATS': 50,
|
||||
'KCASH': 1,
|
||||
'ACT': 0.01,
|
||||
'MT': 300,
|
||||
'DXT': 30,
|
||||
'WAB': 4000,
|
||||
'HYDRO': 400,
|
||||
'LQD': 5,
|
||||
'OPTC': 200,
|
||||
'EQUAD': 80,
|
||||
'LATX': 50,
|
||||
'LEDU': 100,
|
||||
'RIT': 70,
|
||||
'ACDC': 500,
|
||||
'FSN': 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'UOS': 'UOS Network',
|
||||
},
|
||||
'exceptions': {
|
||||
'4004': OrderNotFound,
|
||||
'1013': AuthenticationError,
|
||||
'1016': AuthenticationError,
|
||||
'3002': InsufficientFunds,
|
||||
'10204': DDoSProtection,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const response = await this.publicGetApiV1MarketSymbols (params);
|
||||
const data = response['data'];
|
||||
const result = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const market = data[i];
|
||||
const id = this.safeString (market, 'symbol');
|
||||
const symbolParts = id.split ('-');
|
||||
const baseId = symbolParts[2];
|
||||
const quoteId = symbolParts[1];
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
const symbol = base + '/' + quote;
|
||||
const active = true;
|
||||
const precision = {
|
||||
'amount': this.safeInteger (market, 'amountPrecision'),
|
||||
'price': this.safeInteger (market, 'pricePrecision'),
|
||||
};
|
||||
const limits = {
|
||||
'amount': {
|
||||
'min': this.safeFloat (market, 'minOrderAmount'),
|
||||
'max': undefined,
|
||||
},
|
||||
'price': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
'cost': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'active': active,
|
||||
'precision': precision,
|
||||
'limits': limits,
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeInteger (trade, 'time');
|
||||
const id = this.safeString (trade, 'tid');
|
||||
const orderId = undefined;
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = amount * price;
|
||||
}
|
||||
}
|
||||
const sideId = this.safeInteger (trade, 'direction');
|
||||
const side = this.parseSide (sideId);
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'order': orderId,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit;
|
||||
}
|
||||
const market = this.market (symbol);
|
||||
const response = await this.publicGetApiV1MarketTrades (this.extend (request, params));
|
||||
return this.parseTrades (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privatePostApiV1FundAllAccount (params);
|
||||
const data = response['data'];
|
||||
const result = { 'info': response };
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const balance = data[i];
|
||||
const currencyId = this.safeString (balance, 'currency');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = this.account ();
|
||||
account['used'] = this.safeFloat (balance, 'frozen');
|
||||
account['free'] = this.safeFloat (balance, 'active');
|
||||
account['total'] = this.safeFloat (balance, 'fix');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.markets[symbol];
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
};
|
||||
const response = await this.publicGetApiV1MarketTicker (this.extend (request, params));
|
||||
const data = response['data'];
|
||||
const timestamp = this.safeInteger (data, 'date');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (data, 'high'),
|
||||
'low': this.safeFloat (data, 'low'),
|
||||
'bid': this.safeFloat (data, 'buy'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (data, 'sell'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': this.safeFloat (data, 'last'),
|
||||
'last': this.safeFloat (data, 'last'),
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (data, 'vol'),
|
||||
'quoteVolume': undefined,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
this.safeInteger (ohlcv, 'time'),
|
||||
this.safeFloat (ohlcv, 'open'),
|
||||
this.safeFloat (ohlcv, 'high'),
|
||||
this.safeFloat (ohlcv, 'low'),
|
||||
this.safeFloat (ohlcv, 'close'),
|
||||
this.safeFloat (ohlcv, 'vol'),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'ktype': this.timeframes[timeframe],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit; // default 1, max 600
|
||||
}
|
||||
const response = await this.publicGetApiV1MarketKline (this.extend (request, params));
|
||||
const ohlcvs = this.safeValue (response, 'data', []);
|
||||
return this.parseOHLCVs (ohlcvs, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const marketId = this.marketId (symbol);
|
||||
const request = {
|
||||
'symbol': marketId,
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['size'] = limit;
|
||||
}
|
||||
const response = await this.publicGetApiV1MarketDepth (this.extend (request, params));
|
||||
const data = this.safeValue (response, 'data');
|
||||
const timestamp = this.safeInteger (response, 'time');
|
||||
return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'amount');
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'0': 'open',
|
||||
'1': 'open',
|
||||
'2': 'closed',
|
||||
'3': 'canceled',
|
||||
'4': 'canceled',
|
||||
};
|
||||
return (status in statuses) ? statuses[status] : status;
|
||||
}
|
||||
|
||||
parseSide (sideId) {
|
||||
if (sideId === 1) {
|
||||
return 'buy';
|
||||
} else if (sideId === 2) {
|
||||
return 'sell';
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const id = this.safeString (order, 'orderId');
|
||||
const timestamp = this.safeFloat (order, 'createTime');
|
||||
const lastTradeTimestamp = this.safeFloat (order, 'lastTime');
|
||||
const symbol = market['symbol'];
|
||||
const sideId = this.safeInteger (order, 'tradeType');
|
||||
const side = this.parseSide (sideId);
|
||||
const type = undefined;
|
||||
const price = this.safeFloat (order, 'orderPrice');
|
||||
const average = this.safeFloat (order, 'avgPrice');
|
||||
const amount = this.safeFloat (order, 'orderAmount');
|
||||
const filled = this.safeFloat (order, 'dealAmount');
|
||||
const remaining = amount - filled;
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'orderState'));
|
||||
const cost = filled * price;
|
||||
const feeSide = (side === 'buy') ? 'base' : 'quote';
|
||||
const feeCurrency = market[feeSide];
|
||||
const fee = {
|
||||
'cost': this.safeFloat (order, 'tradeFee'),
|
||||
'currency': feeCurrency,
|
||||
};
|
||||
const result = {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': lastTradeTimestamp,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'cost': cost,
|
||||
'average': average,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'status': status,
|
||||
'fee': fee,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'orderId': id,
|
||||
};
|
||||
const response = await this.privatePostApiV1TradeOrderInfo (this.extend (request, params));
|
||||
const order = this.parseOrder (response['data'], market);
|
||||
return order;
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'state': 0,
|
||||
};
|
||||
const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params));
|
||||
return this.parseOrders (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'state': 1,
|
||||
};
|
||||
const response = await this.privatePostApiV1TradeOrderInfos (this.extend (request, params));
|
||||
return this.parseOrders (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let sideId = undefined;
|
||||
if (side === 'buy') {
|
||||
sideId = 1;
|
||||
} else if (side === 'sell') {
|
||||
sideId = 2;
|
||||
}
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'tradeType': sideId,
|
||||
};
|
||||
const response = await this.privatePostApiV1TradePlaceOrder (this.extend (request, params));
|
||||
const data = response['data'];
|
||||
return {
|
||||
'info': response,
|
||||
'id': this.safeString (data, 'orderId'),
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'orderId': id,
|
||||
};
|
||||
if (symbol !== undefined) {
|
||||
request['symbol'] = this.marketId (symbol);
|
||||
}
|
||||
const results = await this.privatePostApiV1TradeCancelOrder (this.extend (request, params));
|
||||
const success = results['success'];
|
||||
const returnVal = { 'info': results, 'success': success };
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + this.implodeParams (path, params);
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
let payload = this.urlencode ({ 'accessKey': this.apiKey });
|
||||
query['nonce'] = this.milliseconds ();
|
||||
if (Object.keys (query).length) {
|
||||
payload += '&' + this.urlencode (this.keysort (query));
|
||||
}
|
||||
// let message = '/' + 'api/' + this.version + '/' + path + '?' + payload;
|
||||
const message = '/' + path + '?' + payload;
|
||||
const signature = this.hmac (this.encode (message), this.encode (this.secret));
|
||||
body = payload + '&signData=' + signature;
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
|
||||
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
||||
if (typeof body !== 'string') {
|
||||
return; // fallback to default error handler
|
||||
}
|
||||
if ((body[0] === '{') || (body[0] === '[')) {
|
||||
const feedback = this.id + ' ' + body;
|
||||
const success = this.safeValue (response, 'success');
|
||||
if (success !== undefined) {
|
||||
if (!success) {
|
||||
const code = this.safeString (response, 'code');
|
||||
this.throwExactlyMatchedException (this.exceptions, code, feedback);
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bithumb extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bithumb',
|
||||
'name': 'Bithumb',
|
||||
'countries': [ 'KR' ], // South Korea
|
||||
'rateLimit': 500,
|
||||
'has': {
|
||||
'CORS': true,
|
||||
'fetchTickers': true,
|
||||
'withdraw': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg',
|
||||
'api': {
|
||||
'public': 'https://api.bithumb.com/public',
|
||||
'private': 'https://api.bithumb.com',
|
||||
},
|
||||
'www': 'https://www.bithumb.com',
|
||||
'doc': 'https://apidocs.bithumb.com',
|
||||
'fees': 'https://en.bithumb.com/customer_support/info_fee',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'ticker/{currency}',
|
||||
'ticker/all',
|
||||
'orderbook/{currency}',
|
||||
'orderbook/all',
|
||||
'transaction_history/{currency}',
|
||||
'transaction_history/all',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'info/account',
|
||||
'info/balance',
|
||||
'info/wallet_address',
|
||||
'info/ticker',
|
||||
'info/orders',
|
||||
'info/user_transactions',
|
||||
'trade/place',
|
||||
'info/order_detail',
|
||||
'trade/cancel',
|
||||
'trade/btc_withdrawal',
|
||||
'trade/krw_deposit',
|
||||
'trade/krw_withdrawal',
|
||||
'trade/market_buy',
|
||||
'trade/market_sell',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': 0.25 / 100,
|
||||
'taker': 0.25 / 100,
|
||||
},
|
||||
},
|
||||
'exceptions': {
|
||||
'Bad Request(SSL)': BadRequest,
|
||||
'Bad Request(Bad Method)': BadRequest,
|
||||
'Bad Request.(Auth Data)': AuthenticationError, // { "status": "5100", "message": "Bad Request.(Auth Data)" }
|
||||
'Not Member': AuthenticationError,
|
||||
'Invalid Apikey': AuthenticationError, // {"status":"5300","message":"Invalid Apikey"}
|
||||
'Method Not Allowed.(Access IP)': PermissionDenied,
|
||||
'Method Not Allowed.(BTC Adress)': InvalidAddress,
|
||||
'Method Not Allowed.(Access)': PermissionDenied,
|
||||
'Database Fail': ExchangeNotAvailable,
|
||||
'Invalid Parameter': BadRequest,
|
||||
'5600': ExchangeError,
|
||||
'Unknown Error': ExchangeError,
|
||||
'After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions': ExchangeError, // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const response = await this.publicGetTickerAll (params);
|
||||
const data = this.safeValue (response, 'data');
|
||||
const currencyIds = Object.keys (data);
|
||||
const result = [];
|
||||
for (let i = 0; i < currencyIds.length; i++) {
|
||||
const currencyId = currencyIds[i];
|
||||
if (currencyId === 'date') {
|
||||
continue;
|
||||
}
|
||||
const market = data[currencyId];
|
||||
const base = currencyId;
|
||||
const quote = 'KRW';
|
||||
const symbol = currencyId + '/' + quote;
|
||||
let active = true;
|
||||
if (Array.isArray (market)) {
|
||||
const numElements = market.length;
|
||||
if (numElements === 0) {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
result.push ({
|
||||
'id': currencyId,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'info': market,
|
||||
'active': active,
|
||||
'precision': {
|
||||
'amount': undefined,
|
||||
'price': undefined,
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
'price': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
'cost': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'currency': 'ALL',
|
||||
};
|
||||
const response = await this.privatePostInfoBalance (this.extend (request, params));
|
||||
const result = { 'info': response };
|
||||
const balances = this.safeValue (response, 'data');
|
||||
const codes = Object.keys (this.currencies);
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const account = this.account ();
|
||||
const currency = this.currency (code);
|
||||
const lowerCurrencyId = this.safeStringLower (currency, 'id');
|
||||
account['total'] = this.safeFloat (balances, 'total_' + lowerCurrencyId);
|
||||
account['used'] = this.safeFloat (balances, 'in_use_' + lowerCurrencyId);
|
||||
account['free'] = this.safeFloat (balances, 'available_' + lowerCurrencyId);
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'currency': market['base'],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['count'] = limit; // max = 50
|
||||
}
|
||||
const response = await this.publicGetOrderbookCurrency (this.extend (request, params));
|
||||
const orderbook = this.safeValue (response, 'data');
|
||||
const timestamp = this.safeInteger (orderbook, 'timestamp');
|
||||
return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'quantity');
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
const timestamp = this.safeInteger (ticker, 'date');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const open = this.safeFloat (ticker, 'opening_price');
|
||||
const close = this.safeFloat (ticker, 'closing_price');
|
||||
let change = undefined;
|
||||
let percentage = undefined;
|
||||
let average = undefined;
|
||||
if ((close !== undefined) && (open !== undefined)) {
|
||||
change = close - open;
|
||||
if (open > 0) {
|
||||
percentage = change / open * 100;
|
||||
}
|
||||
average = this.sum (open, close) / 2;
|
||||
}
|
||||
const baseVolume = this.safeFloat (ticker, 'units_traded_24H');
|
||||
const quoteVolume = this.safeFloat (ticker, 'acc_trade_value_24H');
|
||||
let vwap = undefined;
|
||||
if (quoteVolume !== undefined && baseVolume !== undefined) {
|
||||
vwap = quoteVolume / baseVolume;
|
||||
}
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'max_price'),
|
||||
'low': this.safeFloat (ticker, 'min_price'),
|
||||
'bid': this.safeFloat (ticker, 'buy_price'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'sell_price'),
|
||||
'askVolume': undefined,
|
||||
'vwap': vwap,
|
||||
'open': open,
|
||||
'close': close,
|
||||
'last': close,
|
||||
'previousClose': undefined,
|
||||
'change': change,
|
||||
'percentage': percentage,
|
||||
'average': average,
|
||||
'baseVolume': baseVolume,
|
||||
'quoteVolume': quoteVolume,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.publicGetTickerAll (params);
|
||||
const result = {};
|
||||
const timestamp = this.safeInteger (response['data'], 'date');
|
||||
const tickers = this.omit (response['data'], 'date');
|
||||
const ids = Object.keys (tickers);
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
let symbol = id;
|
||||
let market = undefined;
|
||||
if (id in this.markets_by_id) {
|
||||
market = this.markets_by_id[id];
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const ticker = tickers[id];
|
||||
const isArray = Array.isArray (ticker);
|
||||
if (!isArray) {
|
||||
ticker['date'] = timestamp;
|
||||
result[symbol] = this.parseTicker (ticker, market);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'currency': market['base'],
|
||||
};
|
||||
const response = await this.publicGetTickerCurrency (this.extend (request, params));
|
||||
return this.parseTicker (response['data'], market);
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
// a workaround for their bug in date format, hours are not 0-padded
|
||||
const parts = trade['transaction_date'].split (' ');
|
||||
const transaction_date = parts[0];
|
||||
let transaction_time = parts[1];
|
||||
if (transaction_time.length < 8) {
|
||||
transaction_time = '0' + transaction_time;
|
||||
}
|
||||
let timestamp = this.parse8601 (transaction_date + ' ' + transaction_time);
|
||||
timestamp -= 9 * 3600000; // they report UTC + 9 hours (server in Korean timezone)
|
||||
const type = undefined;
|
||||
let side = this.safeString (trade, 'type');
|
||||
side = (side === 'ask') ? 'sell' : 'buy';
|
||||
const id = this.safeString (trade, 'cont_no');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'units_traded');
|
||||
let cost = undefined;
|
||||
if (amount !== undefined) {
|
||||
if (price !== undefined) {
|
||||
cost = price * amount;
|
||||
}
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': undefined,
|
||||
'type': type,
|
||||
'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 request = {
|
||||
'currency': market['base'],
|
||||
};
|
||||
if (limit === undefined) {
|
||||
request['count'] = limit; // default 20, max 100
|
||||
}
|
||||
const response = await this.publicGetTransactionHistoryCurrency (this.extend (request, params));
|
||||
return this.parseTrades (response['data'], market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
let request = undefined;
|
||||
let method = 'privatePostTrade';
|
||||
if (type === 'limit') {
|
||||
request = {
|
||||
'order_currency': market['id'],
|
||||
'Payment_currency': market['quote'],
|
||||
'units': amount,
|
||||
'price': price,
|
||||
'type': (side === 'buy') ? 'bid' : 'ask',
|
||||
};
|
||||
method += 'Place';
|
||||
} else if (type === 'market') {
|
||||
request = {
|
||||
'currency': market['id'],
|
||||
'units': amount,
|
||||
};
|
||||
method += 'Market' + this.capitalize (side);
|
||||
}
|
||||
const response = await this[method] (this.extend (request, params));
|
||||
const id = this.safeString (response, 'order_id');
|
||||
return {
|
||||
'info': response,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
const side_in_params = ('side' in params);
|
||||
if (!side_in_params) {
|
||||
throw new ExchangeError (this.id + ' cancelOrder requires a `side` parameter (sell or buy) and a `currency` parameter');
|
||||
}
|
||||
const currency = this.safeString (params, 'currency');
|
||||
if (currency === undefined) {
|
||||
throw new ExchangeError (this.id + ' cancelOrder requires a `currency` parameter (a currency id)');
|
||||
}
|
||||
const side = (params['side'] === 'buy') ? 'bid' : 'ask';
|
||||
params = this.omit (params, [ 'side', 'currency' ]);
|
||||
const request = {
|
||||
'order_id': id,
|
||||
'type': side,
|
||||
'currency': currency,
|
||||
};
|
||||
return await this.privatePostTradeCancel (this.extend (request, params));
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'units': amount,
|
||||
'address': address,
|
||||
'currency': currency['id'],
|
||||
};
|
||||
if (currency === 'XRP' || currency === 'XMR') {
|
||||
const destination = this.safeString (params, 'destination');
|
||||
if ((tag === undefined) && (destination === undefined)) {
|
||||
throw new ExchangeError (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param');
|
||||
} else if (tag !== undefined) {
|
||||
request['destination'] = tag;
|
||||
}
|
||||
}
|
||||
const response = await this.privatePostTradeBtcWithdrawal (this.extend (request, params));
|
||||
return {
|
||||
'info': response,
|
||||
'id': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
nonce () {
|
||||
return this.milliseconds ();
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const endpoint = '/' + this.implodeParams (path, params);
|
||||
let url = this.urls['api'][api] + endpoint;
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
body = this.urlencode (this.extend ({
|
||||
'endpoint': endpoint,
|
||||
}, query));
|
||||
const nonce = this.nonce ().toString ();
|
||||
const auth = endpoint + "\0" + body + "\0" + nonce; // eslint-disable-line quotes
|
||||
const signature = this.hmac (this.encode (auth), this.encode (this.secret), 'sha512');
|
||||
const signature64 = this.decode (this.stringToBase64 (this.encode (signature)));
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Api-Key': this.apiKey,
|
||||
'Api-Sign': signature64.toString (),
|
||||
'Api-Nonce': nonce,
|
||||
};
|
||||
}
|
||||
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":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"}
|
||||
//
|
||||
const status = this.safeString (response, 'status');
|
||||
const message = this.safeString (response, 'message');
|
||||
if (status !== undefined) {
|
||||
if (status === '0000') {
|
||||
return; // no error
|
||||
}
|
||||
const feedback = this.id + ' ' + body;
|
||||
this.throwExactlyMatchedException (this.exceptions, status, feedback);
|
||||
this.throwExactlyMatchedException (this.exceptions, message, feedback);
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const response = await this.fetch2 (path, api, method, params, headers, body);
|
||||
if ('status' in response) {
|
||||
if (response['status'] === '0000') {
|
||||
return response;
|
||||
}
|
||||
throw new ExchangeError (this.id + ' ' + this.json (response));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const zb = require ('./zb.js');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitkk extends zb {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitkk',
|
||||
'name': 'bitkk',
|
||||
'comment': 'a Chinese ZB clone',
|
||||
'urls': {
|
||||
'api': {
|
||||
'public': 'http://api.bitkk.com/data', // no https for public API
|
||||
'private': 'https://trade.bitkk.com/api',
|
||||
},
|
||||
'www': 'https://www.bitkk.com',
|
||||
'doc': 'https://www.bitkk.com/i/developer',
|
||||
'fees': 'https://www.bitkk.com/i/rate',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
'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 };
|
||||
}
|
||||
};
|
||||
+924
@@ -0,0 +1,924 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { AuthenticationError, ArgumentsRequired, ExchangeError, InvalidOrder, BadRequest, OrderNotFound, DDoSProtection, BadSymbol } = require ('./base/errors');
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitmart extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitmart',
|
||||
'name': 'BitMart',
|
||||
'countries': [ 'US', 'CN', 'HK', 'KR' ],
|
||||
'rateLimit': 1000,
|
||||
'version': 'v2',
|
||||
'has': {
|
||||
'CORS': true,
|
||||
'fetchMarkets': true,
|
||||
'fetchTicker': true,
|
||||
'fetchTickers': true,
|
||||
'fetchTime': true,
|
||||
'fetchCurrencies': true,
|
||||
'fetchOrderBook': true,
|
||||
'fetchTrades': true,
|
||||
'fetchMyTrades': true,
|
||||
'fetchOHLCV': true,
|
||||
'fetchBalance': true,
|
||||
'createOrder': true,
|
||||
'cancelOrder': true,
|
||||
'cancelAllOrders': true,
|
||||
'fetchOrders': false,
|
||||
'fetchOrderTrades': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchClosedOrders': true,
|
||||
'fetchCanceledOrders': true,
|
||||
'fetchOrder': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg',
|
||||
'api': 'https://openapi.bitmart.com',
|
||||
'www': 'https://www.bitmart.com/',
|
||||
'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs',
|
||||
'referral': 'http://www.bitmart.com/?r=rQCFLh',
|
||||
},
|
||||
'requiredCredentials': {
|
||||
'apiKey': true,
|
||||
'secret': true,
|
||||
'uid': true,
|
||||
},
|
||||
'api': {
|
||||
'token': {
|
||||
'post': [
|
||||
'authentication',
|
||||
],
|
||||
},
|
||||
'public': {
|
||||
'get': [
|
||||
'currencies',
|
||||
'ping',
|
||||
'steps',
|
||||
'symbols',
|
||||
'symbols_details',
|
||||
'symbols/{symbol}/kline',
|
||||
'symbols/{symbol}/orders',
|
||||
'symbols/{symbol}/trades',
|
||||
'ticker',
|
||||
'time',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'orders',
|
||||
'orders/{id}',
|
||||
'trades',
|
||||
'wallet',
|
||||
],
|
||||
'post': [
|
||||
'orders',
|
||||
],
|
||||
'delete': [
|
||||
'orders',
|
||||
'orders/{id}',
|
||||
],
|
||||
},
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': 1,
|
||||
'3m': 3,
|
||||
'5m': 5,
|
||||
'15m': 15,
|
||||
'30m': 30,
|
||||
'45m': 45,
|
||||
'1h': 60,
|
||||
'2h': 120,
|
||||
'3h': 180,
|
||||
'4h': 240,
|
||||
'1d': 1440,
|
||||
'1w': 10080,
|
||||
'1M': 43200,
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'tierBased': true,
|
||||
'percentage': true,
|
||||
'taker': 0.002,
|
||||
'maker': 0.001,
|
||||
'tiers': {
|
||||
'taker': [
|
||||
[0, 0.20 / 100],
|
||||
[10, 0.18 / 100],
|
||||
[50, 0.16 / 100],
|
||||
[250, 0.14 / 100],
|
||||
[1000, 0.12 / 100],
|
||||
[5000, 0.10 / 100],
|
||||
[25000, 0.08 / 100],
|
||||
[50000, 0.06 / 100],
|
||||
],
|
||||
'maker': [
|
||||
[0, 0.1 / 100],
|
||||
[10, 0.09 / 100],
|
||||
[50, 0.08 / 100],
|
||||
[250, 0.07 / 100],
|
||||
[1000, 0.06 / 100],
|
||||
[5000, 0.05 / 100],
|
||||
[25000, 0.04 / 100],
|
||||
[50000, 0.03 / 100],
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
'exceptions': {
|
||||
'exact': {
|
||||
'Place order error': InvalidOrder, // {"message":"Place order error"}
|
||||
'Not found': OrderNotFound, // {"message":"Not found"}
|
||||
'Visit too often, please try again later': DDoSProtection, // {"code":-30,"msg":"Visit too often, please try again later","subMsg":"","data":{}}
|
||||
'Unknown symbol': BadSymbol, // {"message":"Unknown symbol"}
|
||||
},
|
||||
'broad': {
|
||||
'Invalid limit. limit must be in the range': InvalidOrder,
|
||||
'Maximum price is': InvalidOrder, // {"message":"Maximum price is 0.112695"}
|
||||
// {"message":"Required Integer parameter 'status' is not present"}
|
||||
// {"message":"Required String parameter 'symbol' is not present"}
|
||||
// {"message":"Required Integer parameter 'offset' is not present"}
|
||||
// {"message":"Required Integer parameter 'limit' is not present"}
|
||||
// {"message":"Required Long parameter 'from' is not present"}
|
||||
// {"message":"Required Long parameter 'to' is not present"}
|
||||
'is not present': BadRequest,
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'ONE': 'Menlo One',
|
||||
'PLA': 'Plair',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchTime (params = {}) {
|
||||
const response = await this.publicGetTime (params);
|
||||
//
|
||||
// {
|
||||
// "server_time": 1527777538000
|
||||
// }
|
||||
//
|
||||
return this.safeInteger (response, 'server_time');
|
||||
}
|
||||
|
||||
async signIn (params = {}) {
|
||||
const message = this.apiKey + ':' + this.secret + ':' + this.uid;
|
||||
const data = {
|
||||
'grant_type': 'client_credentials',
|
||||
'client_id': this.apiKey,
|
||||
'client_secret': this.hmac (this.encode (message), this.encode (this.secret), 'sha256'),
|
||||
};
|
||||
const response = await this.tokenPostAuthentication (this.extend (data, params));
|
||||
const accessToken = this.safeString (response, 'access_token');
|
||||
if (!accessToken) {
|
||||
throw new AuthenticationError (this.id + ' signIn() failed to authenticate. Access token missing from response.');
|
||||
}
|
||||
const expiresIn = this.safeInteger (response, 'expires_in');
|
||||
this.options['expires'] = this.sum (this.nonce (), expiresIn * 1000);
|
||||
this.options['accessToken'] = accessToken;
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const markets = await this.publicGetSymbolsDetails (params);
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "id":"1SG_BTC",
|
||||
// "base_currency":"1SG",
|
||||
// "quote_currency":"BTC",
|
||||
// "quote_increment":"0.1",
|
||||
// "base_min_size":"0.1000000000",
|
||||
// "base_max_size":"10000000.0000000000",
|
||||
// "price_min_precision":4,
|
||||
// "price_max_precision":6,
|
||||
// "expiration":"NA"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
const result = [];
|
||||
for (let i = 0; i < markets.length; i++) {
|
||||
const market = markets[i];
|
||||
const id = this.safeString (market, 'id');
|
||||
const baseId = this.safeString (market, 'base_currency');
|
||||
const quoteId = this.safeString (market, 'quote_currency');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
const symbol = base + '/' + quote;
|
||||
//
|
||||
// https://github.com/bitmartexchange/bitmart-official-api-docs/blob/master/rest/public/symbols_details.md#response-details
|
||||
// from the above API doc:
|
||||
// quote_increment Minimum order price as well as the price increment
|
||||
// price_min_precision Minimum price precision (digit) used to query price and kline
|
||||
// price_max_precision Maximum price precision (digit) used to query price and kline
|
||||
//
|
||||
// the docs are wrong: https://github.com/ccxt/ccxt/issues/5612
|
||||
//
|
||||
const quoteIncrement = this.safeString (market, 'quote_increment');
|
||||
const amountPrecision = this.precisionFromString (quoteIncrement);
|
||||
const pricePrecision = this.safeInteger (market, 'price_max_precision');
|
||||
const precision = {
|
||||
'amount': amountPrecision,
|
||||
'price': pricePrecision,
|
||||
};
|
||||
const 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,
|
||||
},
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'precision': precision,
|
||||
'limits': limits,
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
const timestamp = this.milliseconds ();
|
||||
const marketId = this.safeString (ticker, 'symbol_id');
|
||||
let symbol = undefined;
|
||||
if (marketId !== undefined) {
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
symbol = market['symbol'];
|
||||
} else if (marketId !== undefined) {
|
||||
const [ baseId, quoteId ] = marketId.split ('_');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
symbol = base + '/' + quote;
|
||||
}
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'current_price');
|
||||
const percentage = this.safeFloat (ticker, 'fluctuation');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'highest_price'),
|
||||
'low': this.safeFloat (ticker, 'lowest_price'),
|
||||
'bid': this.safeFloat (ticker, 'bid_1'),
|
||||
'bidVolume': this.safeFloat (ticker, 'bid_1_amount'),
|
||||
'ask': this.safeFloat (ticker, 'ask_1'),
|
||||
'askVolume': this.safeFloat (ticker, 'ask_1_amount'),
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': percentage * 100,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (ticker, 'volume'),
|
||||
'quoteVolume': this.safeFloat (ticker, 'base_volume'),
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetTicker (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "volume":"97487.38",
|
||||
// "ask_1":"0.00148668",
|
||||
// "base_volume":"144.59",
|
||||
// "lowest_price":"0.00144362",
|
||||
// "bid_1":"0.00148017",
|
||||
// "highest_price":"0.00151000",
|
||||
// "ask_1_amount":"92.03",
|
||||
// "current_price":"0.00148230",
|
||||
// "fluctuation":"+0.0227",
|
||||
// "symbol_id":"XRP_ETH",
|
||||
// "url":"https://www.bitmart.com/trade?symbol=XRP_ETH",
|
||||
// "bid_1_amount":"134.78"
|
||||
// }
|
||||
//
|
||||
return this.parseTicker (response);
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const tickers = await this.publicGetTicker (params);
|
||||
const result = {};
|
||||
for (let i = 0; i < tickers.length; i++) {
|
||||
const ticker = this.parseTicker (tickers[i]);
|
||||
const symbol = ticker['symbol'];
|
||||
result[symbol] = ticker;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchCurrencies (params = {}) {
|
||||
const currencies = await this.publicGetCurrencies (params);
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "name":"CNY1",
|
||||
// "withdraw_enabled":false,
|
||||
// "id":"CNY1",
|
||||
// "deposit_enabled":false
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
const result = {};
|
||||
for (let i = 0; i < currencies.length; i++) {
|
||||
const currency = currencies[i];
|
||||
const currencyId = this.safeString (currency, 'id');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const name = this.safeString (currency, 'name');
|
||||
const withdrawEnabled = this.safeValue (currency, 'withdraw_enabled');
|
||||
const depositEnabled = this.safeValue (currency, 'deposit_enabled');
|
||||
const active = withdrawEnabled && depositEnabled;
|
||||
result[code] = {
|
||||
'id': currencyId,
|
||||
'code': code,
|
||||
'name': name,
|
||||
'info': currency, // the original payload
|
||||
'active': active,
|
||||
'fee': undefined,
|
||||
'precision': undefined,
|
||||
'limits': {
|
||||
'amount': { 'min': undefined, 'max': undefined },
|
||||
'price': { 'min': undefined, 'max': undefined },
|
||||
'cost': { 'min': undefined, 'max': undefined },
|
||||
'withdraw': { 'min': undefined, 'max': undefined },
|
||||
},
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'symbol': this.marketId (symbol),
|
||||
// 'precision': 4, // optional price precision / depth level whose range is defined in symbol details
|
||||
};
|
||||
const response = await this.publicGetSymbolsSymbolOrders (this.extend (request, params));
|
||||
return this.parseOrderBook (response, undefined, 'buys', 'sells', 'price', 'amount');
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
//
|
||||
// fetchTrades (public)
|
||||
//
|
||||
// {
|
||||
// "amount":"2.29275119",
|
||||
// "price":"0.021858",
|
||||
// "count":"104.8930",
|
||||
// "order_time":1563997286061,
|
||||
// "type":"sell"
|
||||
// }
|
||||
//
|
||||
// fetchMyTrades (private)
|
||||
//
|
||||
// {
|
||||
// active: true,
|
||||
// amount: '0.2000',
|
||||
// entrustType: 1,
|
||||
// entrust_id: 979648824,
|
||||
// fees: '0.0000085532',
|
||||
// price: '0.021383',
|
||||
// symbol: 'ETH_BTC',
|
||||
// timestamp: 1574343514000,
|
||||
// trade_id: 329418828
|
||||
// },
|
||||
//
|
||||
const id = this.safeString (trade, 'trade_id');
|
||||
const timestamp = this.safeInteger2 (trade, 'timestamp', 'order_time');
|
||||
const type = undefined;
|
||||
let side = this.safeStringLower (trade, 'type');
|
||||
if ((side === undefined) && ('entrustType' in trade)) {
|
||||
side = trade['entrustType'] ? 'sell' : 'buy';
|
||||
}
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = amount * price;
|
||||
}
|
||||
}
|
||||
const orderId = this.safeInteger (trade, 'entrust_id');
|
||||
const marketId = this.safeString (trade, 'symbol');
|
||||
let symbol = undefined;
|
||||
if (marketId !== undefined) {
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
symbol = market['symbol'];
|
||||
} else {
|
||||
const [ baseId, quoteId ] = marketId.split ('_');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
symbol = base + '/' + quote;
|
||||
}
|
||||
}
|
||||
if (symbol === undefined) {
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
}
|
||||
const feeCost = this.safeFloat (trade, 'fees');
|
||||
let fee = undefined;
|
||||
if (feeCost !== undefined) {
|
||||
let feeCurrencyCode = undefined;
|
||||
if (market !== undefined) {
|
||||
feeCurrencyCode = (side === 'buy') ? market['base'] : market['quote'];
|
||||
}
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': feeCurrencyCode,
|
||||
};
|
||||
}
|
||||
return {
|
||||
'info': trade,
|
||||
'id': id,
|
||||
'order': orderId,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'takerOrMaker': undefined,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
};
|
||||
const response = await this.publicGetSymbolsSymbolTrades (this.extend (request, params));
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "amount":"2.29275119",
|
||||
// "price":"0.021858",
|
||||
// "count":"104.8930",
|
||||
// "order_time":1563997286061,
|
||||
// "type":"sell"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
// limit is required, must be in the range (0, 50)
|
||||
const maxLimit = 50;
|
||||
limit = (limit === undefined) ? maxLimit : Math.min (limit, maxLimit);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'offset': 0, // current page, starts from 0
|
||||
'limit': limit, // required
|
||||
};
|
||||
const response = await this.privateGetTrades (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "total_trades": 216,
|
||||
// "total_pages": 22,
|
||||
// "current_page": 0,
|
||||
// "trades": [
|
||||
// {
|
||||
// "symbol": "BMX_ETH",
|
||||
// "amount": "1.0",
|
||||
// "fees": "0.0005000000",
|
||||
// "trade_id": 2734956,
|
||||
// "price": "0.00013737",
|
||||
// "active": true,
|
||||
// "entrust_id": 5576623,
|
||||
// "timestamp": 1545292334000
|
||||
// },
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
const trades = this.safeValue (response, 'trades', []);
|
||||
return this.parseTrades (trades, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'entrust_id': id,
|
||||
};
|
||||
return await this.fetchMyTrades (symbol, since, limit, this.extend (request, params));
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
this.safeInteger (ohlcv, 'timestamp'),
|
||||
this.safeFloat (ohlcv, 'open_price'),
|
||||
this.safeFloat (ohlcv, 'highest_price'),
|
||||
this.safeFloat (ohlcv, 'lowest_price'),
|
||||
this.safeFloat (ohlcv, 'current_price'),
|
||||
this.safeFloat (ohlcv, 'volume'),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
||||
if (since === undefined && limit === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires either a `since` argument or a `limit` argument (or both)');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const periodInSeconds = this.parseTimeframe (timeframe);
|
||||
const duration = periodInSeconds * limit * 1000;
|
||||
let to = this.milliseconds ();
|
||||
if (since === undefined) {
|
||||
since = to - duration;
|
||||
} else {
|
||||
to = this.sum (since, duration);
|
||||
}
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'from': since, // start time of k-line data (in milliseconds, required)
|
||||
'to': to, // end time of k-line data (in milliseconds, required)
|
||||
'step': this.timeframes[timeframe], // steps of sampling (in minutes, default 1 minute, optional)
|
||||
};
|
||||
const response = await this.publicGetSymbolsSymbolKline (this.extend (request, params));
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "timestamp":1525761000000,
|
||||
// "open_price":"0.010130",
|
||||
// "highest_price":"0.010130",
|
||||
// "lowest_price":"0.010130",
|
||||
// "current_price":"0.010130",
|
||||
// "volume":"0.000000"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
return this.parseOHLCVs (response, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const balances = await this.privateGetWallet (params);
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "name":"Bitcoin",
|
||||
// "available":"0.0000000000",
|
||||
// "frozen":"0.0000000000",
|
||||
// "id":"BTC"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
const result = { 'info': balances };
|
||||
for (let i = 0; i < balances.length; i++) {
|
||||
const balance = balances[i];
|
||||
const currencyId = this.safeString (balance, 'id');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = this.account ();
|
||||
account['free'] = this.safeFloat (balance, 'available');
|
||||
account['used'] = this.safeFloat (balance, 'frozen');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
//
|
||||
// createOrder
|
||||
//
|
||||
// {
|
||||
// "entrust_id":1223181
|
||||
// }
|
||||
//
|
||||
// cancelOrder
|
||||
//
|
||||
// {}
|
||||
//
|
||||
// fetchOrder, fetchOrdersByStatus, fetchOpenOrders, fetchClosedOrders
|
||||
//
|
||||
// {
|
||||
// "entrust_id":1223181,
|
||||
// "symbol":"BMX_ETH",
|
||||
// "timestamp":1528060666000,
|
||||
// "side":"buy",
|
||||
// "price":"1.000000",
|
||||
// "fees":"0.1",
|
||||
// "original_amount":"1",
|
||||
// "executed_amount":"1",
|
||||
// "remaining_amount":"0",
|
||||
// "status":3
|
||||
// }
|
||||
//
|
||||
const id = this.safeString (order, 'entrust_id');
|
||||
const timestamp = this.milliseconds ();
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
let symbol = undefined;
|
||||
const marketId = this.safeString (order, 'symbol');
|
||||
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 price = this.safeFloat (order, 'price');
|
||||
const amount = this.safeFloat (order, 'original_amount');
|
||||
let cost = undefined;
|
||||
let filled = this.safeFloat (order, 'executed_amount');
|
||||
let remaining = this.safeFloat (order, 'remaining_amount');
|
||||
if (amount !== undefined) {
|
||||
if (remaining !== undefined) {
|
||||
if (filled === undefined) {
|
||||
filled = amount - remaining;
|
||||
}
|
||||
}
|
||||
if (filled !== undefined) {
|
||||
if (remaining === undefined) {
|
||||
remaining = amount - filled;
|
||||
}
|
||||
if (cost === undefined) {
|
||||
if (price !== undefined) {
|
||||
cost = price * filled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const side = this.safeString (order, 'side');
|
||||
const type = undefined;
|
||||
return {
|
||||
'id': id,
|
||||
'info': order,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'symbol': symbol,
|
||||
'type': type,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': undefined,
|
||||
'average': undefined,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'status': status,
|
||||
'fee': undefined,
|
||||
'trades': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'0': 'all',
|
||||
'1': 'open',
|
||||
'2': 'open',
|
||||
'3': 'closed',
|
||||
'4': 'canceled',
|
||||
'5': 'open',
|
||||
'6': 'closed',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
if (type !== 'limit') {
|
||||
throw new ExchangeError (this.id + ' allows limit orders only');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'side': side.toLowerCase (),
|
||||
'amount': this.amountToPrecision (symbol, amount),
|
||||
'price': this.priceToPrecision (symbol, price),
|
||||
};
|
||||
const response = await this.privatePostOrders (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "entrust_id":1223181
|
||||
// }
|
||||
//
|
||||
return this.parseOrder (response, market);
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const intId = parseInt (id);
|
||||
const request = {
|
||||
'id': intId,
|
||||
'entrust_id': intId,
|
||||
};
|
||||
const response = await this.privateDeleteOrdersId (this.extend (request, params));
|
||||
//
|
||||
// responds with an empty object {}
|
||||
//
|
||||
return this.parseOrder (response);
|
||||
}
|
||||
|
||||
async cancelAllOrders (symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument');
|
||||
}
|
||||
const side = this.safeString (params, 'side');
|
||||
if (side === undefined) {
|
||||
throw new ArgumentsRequired (this.id + " cancelAllOrders requires a `side` parameter ('buy' or 'sell')");
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'side': side, // 'buy' or 'sell'
|
||||
};
|
||||
const response = await this.privateDeleteOrders (this.extend (request, params));
|
||||
//
|
||||
// responds with an empty object {}
|
||||
//
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus requires a symbol argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
if (limit === undefined) {
|
||||
limit = 500; // default 500, max 1000
|
||||
}
|
||||
const request = {
|
||||
'symbol': market['id'],
|
||||
'status': status,
|
||||
'offset': 0, // current page, starts from 0
|
||||
'limit': limit,
|
||||
};
|
||||
const response = await this.privateGetOrders (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "orders":[
|
||||
// {
|
||||
// "entrust_id":1223181,
|
||||
// "symbol":"BMX_ETH",
|
||||
// "timestamp":1528060666000,
|
||||
// "side":"buy",
|
||||
// "price":"1.000000",
|
||||
// "fees":"0.1",
|
||||
// "original_amount":"1",
|
||||
// "executed_amount":"1",
|
||||
// "remaining_amount":"0",
|
||||
// "status":3
|
||||
// }
|
||||
// ],
|
||||
// "total_pages":1,
|
||||
// "total_orders":1,
|
||||
// "current_page":0,
|
||||
// }
|
||||
//
|
||||
const orders = this.safeValue (response, 'orders', []);
|
||||
return this.parseOrders (orders, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
// 5 = pending & partially filled orders
|
||||
return await this.fetchOrdersByStatus (5, symbol, since, limit, params);
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
// 3 = closed orders
|
||||
return await this.fetchOrdersByStatus (3, symbol, since, limit, params);
|
||||
}
|
||||
|
||||
async fetchCanceledOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
// 4 = canceled orders
|
||||
return await this.fetchOrdersByStatus (4, symbol, since, limit, params);
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
const response = await this.privateGetOrdersId (this.extend (request, params));
|
||||
//
|
||||
// {
|
||||
// "entrust_id":1223181,
|
||||
// "symbol":"BMX_ETH",
|
||||
// "timestamp":1528060666000,
|
||||
// "side":"buy",
|
||||
// "price":"1.000000",
|
||||
// "fees":"0.1",
|
||||
// "original_amount":"1",
|
||||
// "executed_amount":"1",
|
||||
// "remaining_amount":"0",
|
||||
// "status":3
|
||||
// }
|
||||
//
|
||||
return this.parseOrder (response);
|
||||
}
|
||||
|
||||
nonce () {
|
||||
return this.milliseconds ();
|
||||
}
|
||||
|
||||
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));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else if (api === 'token') {
|
||||
this.checkRequiredCredentials ();
|
||||
body = this.urlencode (query);
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
} else {
|
||||
const nonce = this.nonce ();
|
||||
this.checkRequiredCredentials ();
|
||||
const token = this.safeString (this.options, 'accessToken');
|
||||
if (token === undefined) {
|
||||
throw new AuthenticationError (this.id + ' ' + path + ' endpoint requires an accessToken option or a prior call to signIn() method');
|
||||
}
|
||||
const expires = this.safeInteger (this.options, 'expires');
|
||||
if (expires !== undefined) {
|
||||
if (nonce >= expires) {
|
||||
throw new AuthenticationError (this.id + ' accessToken expired, supply a new accessToken or call the signIn() method');
|
||||
}
|
||||
}
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-BM-TIMESTAMP': nonce.toString (),
|
||||
'X-BM-AUTHORIZATION': 'Bearer ' + token,
|
||||
};
|
||||
if (method !== 'GET') {
|
||||
query = this.keysort (query);
|
||||
body = this.json (query);
|
||||
const message = this.urlencode (query);
|
||||
headers['X-BM-SIGNATURE'] = this.hmac (this.encode (message), this.encode (this.secret), 'sha256');
|
||||
}
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
|
||||
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
// {"message":"Maximum price is 0.112695"}
|
||||
// {"message":"Required Integer parameter 'status' is not present"}
|
||||
// {"message":"Required String parameter 'symbol' is not present"}
|
||||
// {"message":"Required Integer parameter 'offset' is not present"}
|
||||
// {"message":"Required Integer parameter 'limit' is not present"}
|
||||
// {"message":"Required Long parameter 'from' is not present"}
|
||||
// {"message":"Required Long parameter 'to' is not present"}
|
||||
// {"message":"Invalid status. status=6 not support any more, please use 3:deal_success orders, 4:cancelled orders"}
|
||||
// {"message":"Not found"}
|
||||
// {"message":"Place order error"}
|
||||
//
|
||||
const feedback = this.id + ' ' + body;
|
||||
const message = this.safeString2 (response, 'message', 'msg');
|
||||
if (message !== undefined) {
|
||||
this.throwExactlyMatchedException (this.exceptions['exact'], message, feedback);
|
||||
this.throwBroadlyMatchedException (this.exceptions['broad'], message, feedback);
|
||||
throw new ExchangeError (feedback); // unknown message
|
||||
}
|
||||
}
|
||||
};
|
||||
+1181
File diff suppressed because it is too large.
Load diff
+1326
File diff suppressed because it is too large.
Load diff
+562
@@ -0,0 +1,562 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, InvalidNonce, AuthenticationError, OrderNotFound } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitso extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitso',
|
||||
'name': 'Bitso',
|
||||
'countries': [ 'MX' ], // Mexico
|
||||
'rateLimit': 2000, // 30 requests per minute
|
||||
'version': 'v3',
|
||||
'has': {
|
||||
'CORS': false,
|
||||
'fetchMyTrades': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchOrder': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/27766335-715ce7aa-5ed5-11e7-88a8-173a27bb30fe.jpg',
|
||||
'api': 'https://api.bitso.com',
|
||||
'www': 'https://bitso.com',
|
||||
'doc': 'https://bitso.com/api_info',
|
||||
'fees': 'https://bitso.com/fees?l=es',
|
||||
'referral': 'https://bitso.com/?ref=itej',
|
||||
},
|
||||
'options': {
|
||||
'precision': {
|
||||
'XRP': 6,
|
||||
'MXN': 2,
|
||||
'TUSD': 2,
|
||||
},
|
||||
'defaultPrecision': 8,
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'available_books',
|
||||
'ticker',
|
||||
'order_book',
|
||||
'trades',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'account_status',
|
||||
'balance',
|
||||
'fees',
|
||||
'fundings',
|
||||
'fundings/{fid}',
|
||||
'funding_destination',
|
||||
'kyc_documents',
|
||||
'ledger',
|
||||
'ledger/trades',
|
||||
'ledger/fees',
|
||||
'ledger/fundings',
|
||||
'ledger/withdrawals',
|
||||
'mx_bank_codes',
|
||||
'open_orders',
|
||||
'order_trades/{oid}',
|
||||
'orders/{oid}',
|
||||
'user_trades',
|
||||
'user_trades/{tid}',
|
||||
'withdrawals/',
|
||||
'withdrawals/{wid}',
|
||||
],
|
||||
'post': [
|
||||
'bitcoin_withdrawal',
|
||||
'debit_card_withdrawal',
|
||||
'ether_withdrawal',
|
||||
'ripple_withdrawal',
|
||||
'bcash_withdrawal',
|
||||
'litecoin_withdrawal',
|
||||
'orders',
|
||||
'phone_number',
|
||||
'phone_verification',
|
||||
'phone_withdrawal',
|
||||
'spei_withdrawal',
|
||||
'ripple_withdrawal',
|
||||
'bcash_withdrawal',
|
||||
'litecoin_withdrawal',
|
||||
],
|
||||
'delete': [
|
||||
'orders/{oid}',
|
||||
'orders/all',
|
||||
],
|
||||
},
|
||||
},
|
||||
'exceptions': {
|
||||
'0201': AuthenticationError, // Invalid Nonce or Invalid Credentials
|
||||
'104': InvalidNonce, // Cannot perform request - nonce must be higher than 1520307203724237
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const response = await this.publicGetAvailableBooks (params);
|
||||
const markets = this.safeValue (response, 'payload');
|
||||
const result = [];
|
||||
for (let i = 0; i < markets.length; i++) {
|
||||
const market = markets[i];
|
||||
const id = this.safeString (market, 'book');
|
||||
const [ baseId, quoteId ] = id.split ('_');
|
||||
let base = baseId.toUpperCase ();
|
||||
let quote = quoteId.toUpperCase ();
|
||||
base = this.safeCurrencyCode (base);
|
||||
quote = this.safeCurrencyCode (quote);
|
||||
const symbol = base + '/' + quote;
|
||||
const limits = {
|
||||
'amount': {
|
||||
'min': this.safeFloat (market, 'minimum_amount'),
|
||||
'max': this.safeFloat (market, 'maximum_amount'),
|
||||
},
|
||||
'price': {
|
||||
'min': this.safeFloat (market, 'minimum_price'),
|
||||
'max': this.safeFloat (market, 'maximum_price'),
|
||||
},
|
||||
'cost': {
|
||||
'min': this.safeFloat (market, 'minimum_value'),
|
||||
'max': this.safeFloat (market, 'maximum_value'),
|
||||
},
|
||||
};
|
||||
const precision = {
|
||||
'amount': this.safeInteger (this.options['precision'], base, this.options['defaultPrecision']),
|
||||
'price': this.safeInteger (this.options['precision'], quote, this.options['defaultPrecision']),
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'info': market,
|
||||
'limits': limits,
|
||||
'precision': precision,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetBalance (params);
|
||||
const balances = this.safeValue (response['payload'], 'balances');
|
||||
const result = { 'info': response };
|
||||
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 = {
|
||||
'free': this.safeFloat (balance, 'available'),
|
||||
'used': this.safeFloat (balance, 'locked'),
|
||||
'total': this.safeFloat (balance, 'total'),
|
||||
};
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'book': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetOrderBook (this.extend (request, params));
|
||||
const orderbook = this.safeValue (response, 'payload');
|
||||
const timestamp = this.parse8601 (this.safeString (orderbook, 'updated_at'));
|
||||
return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'amount');
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'book': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetTicker (this.extend (request, params));
|
||||
const ticker = this.safeValue (response, 'payload');
|
||||
const timestamp = this.parse8601 (this.safeString (ticker, 'created_at'));
|
||||
const vwap = this.safeFloat (ticker, 'vwap');
|
||||
const baseVolume = this.safeFloat (ticker, 'volume');
|
||||
let quoteVolume = undefined;
|
||||
if (baseVolume !== undefined && vwap !== undefined) {
|
||||
quoteVolume = baseVolume * vwap;
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
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': vwap,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': baseVolume,
|
||||
'quoteVolume': quoteVolume,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.parse8601 (this.safeString (trade, 'created_at'));
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
const marketId = this.safeString (trade, 'book');
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
}
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const side = this.safeString2 (trade, 'side', 'maker_side');
|
||||
let amount = this.safeFloat2 (trade, 'amount', 'major');
|
||||
if (amount !== undefined) {
|
||||
amount = Math.abs (amount);
|
||||
}
|
||||
let fee = undefined;
|
||||
const feeCost = this.safeFloat (trade, 'fees_amount');
|
||||
if (feeCost !== undefined) {
|
||||
const feeCurrencyId = this.safeString (trade, 'fees_currency');
|
||||
const feeCurrency = this.safeCurrencyCode (feeCurrencyId);
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': feeCurrency,
|
||||
};
|
||||
}
|
||||
let cost = this.safeFloat (trade, 'minor');
|
||||
if (cost !== undefined) {
|
||||
cost = Math.abs (cost);
|
||||
}
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const orderId = this.safeString (trade, 'oid');
|
||||
const id = this.safeString (trade, 'tid');
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': orderId,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'takerOrMaker': undefined,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'book': market['id'],
|
||||
};
|
||||
const response = await this.publicGetTrades (this.extend (request, params));
|
||||
return this.parseTrades (response['payload'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = 25, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
// the don't support fetching trades starting from a date yet
|
||||
// use the `marker` extra param for that
|
||||
// this is not a typo, the variable name is 'marker' (don't confuse with 'market')
|
||||
const markerInParams = ('marker' in params);
|
||||
// warn the user with an exception if the user wants to filter
|
||||
// starting from since timestamp, but does not set the trade id with an extra 'marker' param
|
||||
if ((since !== undefined) && !markerInParams) {
|
||||
throw new ExchangeError (this.id + ' fetchMyTrades does not support fetching trades starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id');
|
||||
}
|
||||
// convert it to an integer unconditionally
|
||||
if (markerInParams) {
|
||||
params = this.extend (params, {
|
||||
'marker': parseInt (params['marker']),
|
||||
});
|
||||
}
|
||||
const request = {
|
||||
'book': market['id'],
|
||||
'limit': limit, // default = 25, max = 100
|
||||
// 'sort': 'desc', // default = desc
|
||||
// 'marker': id, // integer id to start from
|
||||
};
|
||||
const response = await this.privateGetUserTrades (this.extend (request, params));
|
||||
return this.parseTrades (response['payload'], market, since, limit);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'book': this.marketId (symbol),
|
||||
'side': side,
|
||||
'type': type,
|
||||
'major': this.amountToPrecision (symbol, amount),
|
||||
};
|
||||
if (type === 'limit') {
|
||||
request['price'] = this.priceToPrecision (symbol, price);
|
||||
}
|
||||
const response = await this.privatePostOrders (this.extend (request, params));
|
||||
const id = this.safeString (response['payload'], 'oid');
|
||||
return {
|
||||
'info': response,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'oid': id,
|
||||
};
|
||||
return await this.privateDeleteOrdersOid (this.extend (request, params));
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'partial-fill': 'open', // this is a common substitution in ccxt
|
||||
'completed': 'closed',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
const id = this.safeString (order, 'oid');
|
||||
const side = this.safeString (order, 'side');
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
let symbol = undefined;
|
||||
const marketId = this.safeString (order, 'book');
|
||||
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) {
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
}
|
||||
const orderType = this.safeString (order, 'type');
|
||||
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const amount = this.safeFloat (order, 'original_amount');
|
||||
const remaining = this.safeFloat (order, 'unfilled_amount');
|
||||
let filled = undefined;
|
||||
if (amount !== undefined) {
|
||||
if (remaining !== undefined) {
|
||||
filled = amount - remaining;
|
||||
}
|
||||
}
|
||||
return {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'symbol': symbol,
|
||||
'type': orderType,
|
||||
'side': side,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': undefined,
|
||||
'remaining': remaining,
|
||||
'filled': filled,
|
||||
'status': status,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = 25, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
// the don't support fetching trades starting from a date yet
|
||||
// use the `marker` extra param for that
|
||||
// this is not a typo, the variable name is 'marker' (don't confuse with 'market')
|
||||
const markerInParams = ('marker' in params);
|
||||
// warn the user with an exception if the user wants to filter
|
||||
// starting from since timestamp, but does not set the trade id with an extra 'marker' param
|
||||
if ((since !== undefined) && !markerInParams) {
|
||||
throw new ExchangeError (this.id + ' fetchOpenOrders does not support fetching orders starting from a timestamp with the `since` argument, use the `marker` extra param to filter starting from an integer trade id');
|
||||
}
|
||||
// convert it to an integer unconditionally
|
||||
if (markerInParams) {
|
||||
params = this.extend (params, {
|
||||
'marker': parseInt (params['marker']),
|
||||
});
|
||||
}
|
||||
const request = {
|
||||
'book': market['id'],
|
||||
'limit': limit, // default = 25, max = 100
|
||||
// 'sort': 'desc', // default = desc
|
||||
// 'marker': id, // integer id to start from
|
||||
};
|
||||
const response = await this.privateGetOpenOrders (this.extend (request, params));
|
||||
const orders = this.parseOrders (response['payload'], market, since, limit);
|
||||
return orders;
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetOrdersOid ({
|
||||
'oid': id,
|
||||
});
|
||||
const payload = this.safeValue (response, 'payload');
|
||||
if (Array.isArray (payload)) {
|
||||
const numOrders = response['payload'].length;
|
||||
if (numOrders === 1) {
|
||||
return this.parseOrder (payload[0]);
|
||||
}
|
||||
}
|
||||
throw new OrderNotFound (this.id + ': The order ' + id + ' not found.');
|
||||
}
|
||||
|
||||
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'oid': id,
|
||||
};
|
||||
const response = await this.privateGetOrderTradesOid (this.extend (request, params));
|
||||
return this.parseTrades (response['payload'], market);
|
||||
}
|
||||
|
||||
async fetchDepositAddress (code, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'fund_currency': currency['id'],
|
||||
};
|
||||
const response = await this.privateGetFundingDestination (this.extend (request, params));
|
||||
let address = this.safeString (response['payload'], 'account_identifier');
|
||||
let tag = undefined;
|
||||
if (code === 'XRP') {
|
||||
const parts = address.split ('?dt=');
|
||||
address = parts[0];
|
||||
tag = parts[1];
|
||||
}
|
||||
this.checkAddress (address);
|
||||
return {
|
||||
'currency': code,
|
||||
'address': address,
|
||||
'tag': tag,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
async withdraw (code, amount, address, tag = undefined, params = {}) {
|
||||
this.checkAddress (address);
|
||||
await this.loadMarkets ();
|
||||
const methods = {
|
||||
'BTC': 'Bitcoin',
|
||||
'ETH': 'Ether',
|
||||
'XRP': 'Ripple',
|
||||
'BCH': 'Bcash',
|
||||
'LTC': 'Litecoin',
|
||||
};
|
||||
const method = (code in methods) ? methods[code] : undefined;
|
||||
if (method === undefined) {
|
||||
throw new ExchangeError (this.id + ' not valid withdraw coin: ' + code);
|
||||
}
|
||||
const request = {
|
||||
'amount': amount,
|
||||
'address': address,
|
||||
'destination_tag': tag,
|
||||
};
|
||||
const classMethod = 'privatePost' + method + 'Withdrawal';
|
||||
const response = await this[classMethod] (this.extend (request, params));
|
||||
return {
|
||||
'info': response,
|
||||
'id': this.safeString (response['payload'], 'wid'),
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let endpoint = '/' + this.version + '/' + this.implodeParams (path, params);
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
if (method === 'GET') {
|
||||
if (Object.keys (query).length) {
|
||||
endpoint += '?' + this.urlencode (query);
|
||||
}
|
||||
}
|
||||
const url = this.urls['api'] + endpoint;
|
||||
if (api === 'private') {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ().toString ();
|
||||
let request = [ nonce, method, endpoint ].join ('');
|
||||
if (method !== 'GET') {
|
||||
if (Object.keys (query).length) {
|
||||
body = this.json (query);
|
||||
request += body;
|
||||
}
|
||||
}
|
||||
const signature = this.hmac (this.encode (request), this.encode (this.secret));
|
||||
const auth = this.apiKey + ':' + nonce + ':' + signature;
|
||||
headers = {
|
||||
'Authorization': 'Bitso ' + auth,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
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 ('success' in response) {
|
||||
//
|
||||
// {"success":false,"error":{"code":104,"message":"Cannot perform request - nonce must be higher than 1520307203724237"}}
|
||||
//
|
||||
let success = this.safeValue (response, 'success', false);
|
||||
if (typeof success === 'string') {
|
||||
if ((success === 'true') || (success === '1')) {
|
||||
success = true;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
const feedback = this.id + ' ' + this.json (response);
|
||||
const error = this.safeValue (response, 'error');
|
||||
if (error === undefined) {
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
const code = this.safeString (error, 'code');
|
||||
this.throwExactlyMatchedException (this.exceptions, code, feedback);
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const response = await this.fetch2 (path, api, method, params, headers, body);
|
||||
if ('success' in response) {
|
||||
if (response['success']) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
throw new ExchangeError (this.id + ' ' + this.json (response));
|
||||
}
|
||||
};
|
||||
+1148
File diff suppressed because it is too large.
Load diff
+291
@@ -0,0 +1,291 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { BadSymbol, ExchangeError, NotSupported } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bitstamp1 extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bitstamp1',
|
||||
'name': 'Bitstamp',
|
||||
'countries': [ 'GB' ],
|
||||
'rateLimit': 1000,
|
||||
'version': 'v1',
|
||||
'has': {
|
||||
'CORS': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg',
|
||||
'api': 'https://www.bitstamp.net/api',
|
||||
'www': 'https://www.bitstamp.net',
|
||||
'doc': 'https://www.bitstamp.net/api',
|
||||
},
|
||||
'requiredCredentials': {
|
||||
'apiKey': true,
|
||||
'secret': true,
|
||||
'uid': true,
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'ticker',
|
||||
'ticker_hour',
|
||||
'order_book',
|
||||
'transactions',
|
||||
'eur_usd',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'balance',
|
||||
'user_transactions',
|
||||
'open_orders',
|
||||
'order_status',
|
||||
'cancel_order',
|
||||
'cancel_all_orders',
|
||||
'buy',
|
||||
'sell',
|
||||
'bitcoin_deposit_address',
|
||||
'unconfirmed_btc',
|
||||
'ripple_withdrawal',
|
||||
'ripple_address',
|
||||
'withdrawal_requests',
|
||||
'bitcoin_withdrawal',
|
||||
],
|
||||
},
|
||||
},
|
||||
'markets': {
|
||||
'BTC/USD': { 'id': 'btcusd', 'symbol': 'BTC/USD', 'base': 'BTC', 'quote': 'USD', 'baseId': 'btc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'BTC/EUR': { 'id': 'btceur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'EUR/USD': { 'id': 'eurusd', 'symbol': 'EUR/USD', 'base': 'EUR', 'quote': 'USD', 'baseId': 'eur', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'XRP/USD': { 'id': 'xrpusd', 'symbol': 'XRP/USD', 'base': 'XRP', 'quote': 'USD', 'baseId': 'xrp', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'XRP/EUR': { 'id': 'xrpeur', 'symbol': 'XRP/EUR', 'base': 'XRP', 'quote': 'EUR', 'baseId': 'xrp', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'XRP/BTC': { 'id': 'xrpbtc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'LTC/USD': { 'id': 'ltcusd', 'symbol': 'LTC/USD', 'base': 'LTC', 'quote': 'USD', 'baseId': 'ltc', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'LTC/EUR': { 'id': 'ltceur', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'ltc', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'LTC/BTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'ETH/USD': { 'id': 'ethusd', 'symbol': 'ETH/USD', 'base': 'ETH', 'quote': 'USD', 'baseId': 'eth', 'quoteId': 'usd', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'ETH/EUR': { 'id': 'etheur', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR', 'baseId': 'eth', 'quoteId': 'eur', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'ETH/BTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
if (symbol !== 'BTC/USD') {
|
||||
throw new ExchangeError (this.id + ' ' + this.version + " fetchOrderBook doesn't support " + symbol + ', use it for BTC/USD only');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const orderbook = await this.publicGetOrderBook (params);
|
||||
const timestamp = this.safeTimestamp (orderbook, 'timestamp');
|
||||
return this.parseOrderBook (orderbook, timestamp);
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
if (symbol !== 'BTC/USD') {
|
||||
throw new ExchangeError (this.id + ' ' + this.version + " fetchTicker doesn't support " + symbol + ', use it for BTC/USD only');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const ticker = await this.publicGetTicker (params);
|
||||
const timestamp = this.safeTimestamp (ticker, 'timestamp');
|
||||
const vwap = this.safeFloat (ticker, 'vwap');
|
||||
const baseVolume = this.safeFloat (ticker, 'volume');
|
||||
let quoteVolume = undefined;
|
||||
if (baseVolume !== undefined && vwap !== undefined) {
|
||||
quoteVolume = baseVolume * vwap;
|
||||
}
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
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': vwap,
|
||||
'open': this.safeFloat (ticker, 'open'),
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': baseVolume,
|
||||
'quoteVolume': quoteVolume,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.safeTimestamp2 (trade, 'date', 'datetime');
|
||||
const side = (trade['type'] === 0) ? 'buy' : 'sell';
|
||||
const orderId = this.safeString (trade, 'order_id');
|
||||
if ('currency_pair' in trade) {
|
||||
if (trade['currency_pair'] in this.markets_by_id) {
|
||||
market = this.markets_by_id[trade['currency_pair']];
|
||||
}
|
||||
}
|
||||
const id = this.safeString (trade, 'tid');
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = price * amount;
|
||||
}
|
||||
}
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': orderId,
|
||||
'type': undefined,
|
||||
'side': side,
|
||||
'takerOrMaker': undefined,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
if (symbol !== 'BTC/USD') {
|
||||
throw new BadSymbol (this.id + ' ' + this.version + " fetchTrades doesn't support " + symbol + ', use it for BTC/USD only');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'time': 'minute',
|
||||
};
|
||||
const response = await this.publicGetTransactions (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
const balance = await this.privatePostBalance (params);
|
||||
const result = { 'info': balance };
|
||||
const codes = Object.keys (this.currencies);
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const currency = this.currency (code);
|
||||
const currencyId = currency['id'];
|
||||
const account = this.account ();
|
||||
account['free'] = this.safeFloat (balance, currencyId + '_available');
|
||||
account['used'] = this.safeFloat (balance, currencyId + '_reserved');
|
||||
account['total'] = this.safeFloat (balance, currencyId + '_balance');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
if (type !== 'limit') {
|
||||
throw new ExchangeError (this.id + ' ' + this.version + ' accepts limit orders only');
|
||||
}
|
||||
if (symbol !== 'BTC/USD') {
|
||||
throw new ExchangeError (this.id + ' v1 supports BTC/USD orders only');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const method = 'privatePost' + this.capitalize (side);
|
||||
const request = {
|
||||
'amount': amount,
|
||||
'price': price,
|
||||
};
|
||||
const response = await this[method] (this.extend (request, params));
|
||||
const id = this.safeString (response, 'id');
|
||||
return {
|
||||
'info': response,
|
||||
'id': id,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
return await this.privatePostCancelOrder ({ 'id': id });
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'In Queue': 'open',
|
||||
'Open': 'open',
|
||||
'Finished': 'closed',
|
||||
'Canceled': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
async fetchOrderStatus (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
const response = await this.privatePostOrderStatus (this.extend (request, params));
|
||||
return this.parseOrderStatus (response);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let market = undefined;
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
}
|
||||
const pair = market ? market['id'] : 'all';
|
||||
const request = {
|
||||
'id': pair,
|
||||
};
|
||||
const response = await this.privatePostOpenOrdersId (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
throw new NotSupported (this.id + ' fetchOrder is not implemented yet');
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + this.implodeParams (path, params);
|
||||
let query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} 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',
|
||||
};
|
||||
}
|
||||
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 ('status' in response) {
|
||||
if (response['status'] === 'error') {
|
||||
throw new ExchangeError (this.id + ' ' + this.json (response));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
+1444
File diff suppressed because it is too large.
Load diff
+1211
File diff suppressed because it is too large.
Load diff
+233
@@ -0,0 +1,233 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bl3p extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bl3p',
|
||||
'name': 'BL3P',
|
||||
'countries': [ 'NL', 'EU' ], // Netherlands, EU
|
||||
'rateLimit': 1000,
|
||||
'version': '1',
|
||||
'comment': 'An exchange market by BitonicNL',
|
||||
'has': {
|
||||
'CORS': false,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg',
|
||||
'api': 'https://api.bl3p.eu',
|
||||
'www': [
|
||||
'https://bl3p.eu',
|
||||
'https://bitonic.nl',
|
||||
],
|
||||
'doc': [
|
||||
'https://github.com/BitonicNL/bl3p-api/tree/master/docs',
|
||||
'https://bl3p.eu/api',
|
||||
'https://bitonic.nl/en/api',
|
||||
],
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'{market}/ticker',
|
||||
'{market}/orderbook',
|
||||
'{market}/trades',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'{market}/money/depth/full',
|
||||
'{market}/money/order/add',
|
||||
'{market}/money/order/cancel',
|
||||
'{market}/money/order/result',
|
||||
'{market}/money/orders',
|
||||
'{market}/money/orders/history',
|
||||
'{market}/money/trades/fetch',
|
||||
'GENMKT/money/info',
|
||||
'GENMKT/money/deposit_address',
|
||||
'GENMKT/money/new_deposit_address',
|
||||
'GENMKT/money/wallet/history',
|
||||
'GENMKT/money/withdraw',
|
||||
],
|
||||
},
|
||||
},
|
||||
'markets': {
|
||||
'BTC/EUR': { 'id': 'BTCEUR', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'BTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
'LTC/EUR': { 'id': 'LTCEUR', 'symbol': 'LTC/EUR', 'base': 'LTC', 'quote': 'EUR', 'baseId': 'LTC', 'quoteId': 'EUR', 'maker': 0.0025, 'taker': 0.0025 },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privatePostGENMKTMoneyInfo (params);
|
||||
const data = this.safeValue (response, 'data', {});
|
||||
const wallets = this.safeValue (data, 'wallets');
|
||||
const result = { 'info': data };
|
||||
const codes = Object.keys (this.currencies);
|
||||
for (let i = 0; i < codes.length; i++) {
|
||||
const code = codes[i];
|
||||
const currency = this.currency (code);
|
||||
const currencyId = currency['id'];
|
||||
const wallet = this.safeValue (wallets, currencyId, {});
|
||||
const available = this.safeValue (wallet, 'available', {});
|
||||
const balance = this.safeValue (wallet, 'balance', {});
|
||||
const account = this.account ();
|
||||
account['free'] = this.safeFloat (available, 'value');
|
||||
account['total'] = this.safeFloat (balance, 'value');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
parseBidAsk (bidask, priceKey = 0, amountKey = 1) {
|
||||
return [
|
||||
bidask[priceKey] / 100000.0,
|
||||
bidask[amountKey] / 100000000.0,
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.publicGetMarketOrderbook (this.extend (request, params));
|
||||
const orderbook = this.safeValue (response, 'data');
|
||||
return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price_int', 'amount_int');
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
const request = {
|
||||
'market': this.marketId (symbol),
|
||||
};
|
||||
const ticker = await this.publicGetMarketTicker (this.extend (request, params));
|
||||
const timestamp = this.safeTimestamp (ticker, 'timestamp');
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
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': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': undefined,
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (ticker['volume'], '24h'),
|
||||
'quoteVolume': undefined,
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const id = this.safeString (trade, 'trade_id');
|
||||
const timestamp = this.safeInteger (trade, 'date');
|
||||
let price = this.safeFloat (trade, 'price_int');
|
||||
if (price !== undefined) {
|
||||
price /= 100000.0;
|
||||
}
|
||||
let amount = this.safeFloat (trade, 'amount_int');
|
||||
if (amount !== undefined) {
|
||||
amount /= 100000000.0;
|
||||
}
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = amount * price;
|
||||
}
|
||||
}
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'type': undefined,
|
||||
'side': undefined,
|
||||
'order': undefined,
|
||||
'takerOrMaker': undefined,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
const market = this.market (symbol);
|
||||
const response = await this.publicGetMarketTrades (this.extend ({
|
||||
'market': market['id'],
|
||||
}, params));
|
||||
const result = this.parseTrades (response['data']['trades'], market, since, limit);
|
||||
return result;
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
const market = this.market (symbol);
|
||||
const order = {
|
||||
'market': market['id'],
|
||||
'amount_int': parseInt (amount * 100000000),
|
||||
'fee_currency': market['quote'],
|
||||
'type': (side === 'buy') ? 'bid' : 'ask',
|
||||
};
|
||||
if (type === 'limit') {
|
||||
order['price_int'] = parseInt (price * 100000.0);
|
||||
}
|
||||
const response = await this.privatePostMarketMoneyOrderAdd (this.extend (order, params));
|
||||
const orderId = this.safeString (response['data'], 'order_id');
|
||||
return {
|
||||
'info': response,
|
||||
'id': orderId,
|
||||
};
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
const request = {
|
||||
'order_id': id,
|
||||
};
|
||||
return await this.privatePostMarketMoneyOrderCancel (this.extend (request, params));
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const request = this.implodeParams (path, params);
|
||||
let url = this.urls['api'] + '/' + this.version + '/' + request;
|
||||
const query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
const nonce = this.nonce ();
|
||||
body = this.urlencode (this.extend ({ 'nonce': nonce }, query));
|
||||
const secret = this.base64ToBinary (this.secret);
|
||||
// eslint-disable-next-line quotes
|
||||
const auth = request + "\0" + body;
|
||||
const signature = this.hmac (this.encode (auth), secret, 'sha512', 'base64');
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Rest-Key': this.apiKey,
|
||||
'Rest-Sign': this.decode (signature),
|
||||
};
|
||||
}
|
||||
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
|
||||
}
|
||||
};
|
||||
+832
@@ -0,0 +1,832 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, InvalidOrder, AuthenticationError, InsufficientFunds, BadRequest } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class bleutrade extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'bleutrade',
|
||||
'name': 'Bleutrade',
|
||||
'countries': ['BR'], // Brazil
|
||||
'rateLimit': 1000,
|
||||
'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,
|
||||
'fetchTicker': true,
|
||||
'fetchOrders': false,
|
||||
'fetchClosedOrders': 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',
|
||||
},
|
||||
'hostname': 'bleutrade.com',
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg',
|
||||
'api': {
|
||||
'v3Private': 'https://{hostname}/api/v3/private',
|
||||
'v3Public': 'https://{hostname}/api/v3/public',
|
||||
},
|
||||
'www': ['https://bleutrade.com'],
|
||||
'doc': [
|
||||
'https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0',
|
||||
],
|
||||
'fees': 'https://bleutrade.com/fees/',
|
||||
},
|
||||
'api': {
|
||||
'v3Public': {
|
||||
'get': [
|
||||
'getassets',
|
||||
'getmarkets',
|
||||
'getticker',
|
||||
'getmarketsummary',
|
||||
'getmarketsummaries',
|
||||
'getorderbook',
|
||||
'getmarkethistory',
|
||||
'getcandles',
|
||||
],
|
||||
},
|
||||
'v3Private': {
|
||||
'post': [
|
||||
'getbalance',
|
||||
'getbalances',
|
||||
'buylimit',
|
||||
'selllimit',
|
||||
'buylimitami',
|
||||
'selllimitami',
|
||||
'buystoplimit',
|
||||
'sellstoplimit',
|
||||
'ordercancel',
|
||||
'getopenorders',
|
||||
'getcloseorders',
|
||||
'getdeposithistory',
|
||||
'getdepositaddress',
|
||||
'getmytransactions',
|
||||
'withdraw',
|
||||
'directtransfer',
|
||||
'getwithdrawhistory',
|
||||
'getlimits',
|
||||
],
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'EPC': 'Epacoin',
|
||||
},
|
||||
'exceptions': {
|
||||
'exact': {
|
||||
'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': {
|
||||
'parseOrderStatus': true,
|
||||
'symbolSeparator': '_',
|
||||
},
|
||||
});
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
// https://github.com/ccxt/ccxt/issues/5668
|
||||
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;
|
||||
const precision = {
|
||||
'amount': 8,
|
||||
'price': 8,
|
||||
};
|
||||
const active = this.safeValue (market, 'IsActive', false);
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'active': active,
|
||||
'info': market,
|
||||
'precision': precision,
|
||||
'maker': this.fees['trading']['maker'],
|
||||
'taker': this.fees['trading']['taker'],
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': this.safeFloat (market, 'MinTradeSize'),
|
||||
'max': undefined,
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision['price']),
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'market': this.marketId (symbol),
|
||||
'type': 'ALL',
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['depth'] = limit; // 50
|
||||
}
|
||||
const response = await this.v3PublicGetGetorderbook (this.extend (request, params));
|
||||
const orderbook = this.safeValue (response, 'result');
|
||||
if (!orderbook) {
|
||||
throw new ExchangeError (this.id + ' no orderbook data in ' + this.json (response));
|
||||
}
|
||||
return this.parseOrderBook (orderbook, undefined, 'buy', 'sell', 'Rate', 'Quantity');
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.v3PublicGetGetmarketsummary (this.extend (request, params));
|
||||
const ticker = response['result'][0];
|
||||
return this.parseTicker (ticker, market);
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
const timestamp = this.parse8601 (ohlcv['TimeStamp'] + '+00:00');
|
||||
return [
|
||||
timestamp,
|
||||
this.safeFloat (ohlcv, 'Open'),
|
||||
this.safeFloat (ohlcv, 'High'),
|
||||
this.safeFloat (ohlcv, 'Low'),
|
||||
this.safeFloat (ohlcv, 'Close'),
|
||||
this.safeFloat (ohlcv, 'Volume'),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '15m', since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'period': this.timeframes[timeframe],
|
||||
'market': market['id'],
|
||||
'count': limit,
|
||||
};
|
||||
const response = await this.v3PublicGetGetcandles (this.extend (request, params));
|
||||
return this.parseOHLCVs (response['result'], market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
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 {
|
||||
'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,
|
||||
};
|
||||
}
|
||||
|
||||
parseLedgerEntryType (type) {
|
||||
// deposits don't seem to appear in here
|
||||
const types = {
|
||||
'TRADE': 'trade',
|
||||
'WITHDRAW': 'transaction',
|
||||
};
|
||||
return this.safeString (types, type, type);
|
||||
}
|
||||
|
||||
parseLedgerEntry (item, currency = undefined) {
|
||||
//
|
||||
// trade (both sides)
|
||||
//
|
||||
// {
|
||||
// ID: 109660527,
|
||||
// TimeStamp: '2018-11-14 15:12:57.140776',
|
||||
// Asset: 'ETH',
|
||||
// AssetName: 'Ethereum',
|
||||
// Amount: 0.01,
|
||||
// Type: 'TRADE',
|
||||
// Description: 'Trade +, order id 133111123',
|
||||
// Comments: '',
|
||||
// CoinSymbol: 'ETH',
|
||||
// CoinName: 'Ethereum'
|
||||
// }
|
||||
//
|
||||
// {
|
||||
// ID: 109660526,
|
||||
// TimeStamp: '2018-11-14 15:12:57.140776',
|
||||
// Asset: 'BTC',
|
||||
// AssetName: 'Bitcoin',
|
||||
// Amount: -0.00031776,
|
||||
// Type: 'TRADE',
|
||||
// Description: 'Trade -, order id 133111123, fee -0.00000079',
|
||||
// Comments: '',
|
||||
// CoinSymbol: 'BTC',
|
||||
// CoinName: 'Bitcoin'
|
||||
// }
|
||||
//
|
||||
// withdrawal
|
||||
//
|
||||
// {
|
||||
// ID: 104672316,
|
||||
// TimeStamp: '2018-05-03 08:18:19.031831',
|
||||
// Asset: 'DOGE',
|
||||
// AssetName: 'Dogecoin',
|
||||
// Amount: -61893.87864686,
|
||||
// Type: 'WITHDRAW',
|
||||
// Description: 'Withdraw: 61883.87864686 to address DD8tgehNNyYB2iqVazi2W1paaztgcWXtF6; fee 10.00000000',
|
||||
// Comments: '',
|
||||
// CoinSymbol: 'DOGE',
|
||||
// CoinName: 'Dogecoin'
|
||||
// }
|
||||
//
|
||||
const code = this.safeCurrencyCode (this.safeString (item, 'CoinSymbol'), currency);
|
||||
const description = this.safeString (item, 'Description');
|
||||
const type = this.parseLedgerEntryType (this.safeString (item, 'Type'));
|
||||
let referenceId = undefined;
|
||||
let fee = undefined;
|
||||
const delimiter = (type === 'trade') ? ', ' : '; ';
|
||||
const parts = description.split (delimiter);
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
let part = parts[i];
|
||||
if (part.indexOf ('fee') === 0) {
|
||||
part = part.replace ('fee ', '');
|
||||
let feeCost = parseFloat (part);
|
||||
if (feeCost < 0) {
|
||||
feeCost = -feeCost;
|
||||
}
|
||||
fee = {
|
||||
'cost': feeCost,
|
||||
'currency': code,
|
||||
};
|
||||
} else if (part.indexOf ('order id') === 0) {
|
||||
referenceId = part.replace ('order id ', '');
|
||||
}
|
||||
//
|
||||
// does not belong to Ledger, related to parseTransaction
|
||||
//
|
||||
// if (part.indexOf ('Withdraw') === 0) {
|
||||
// const details = part.split (' to address ');
|
||||
// if (details.length > 1) {
|
||||
// address = details[1];
|
||||
// }
|
||||
//
|
||||
}
|
||||
const timestamp = this.parse8601 (this.safeString (item, 'TimeStamp'));
|
||||
let amount = this.safeFloat (item, 'Amount');
|
||||
let direction = undefined;
|
||||
if (amount !== undefined) {
|
||||
direction = 'in';
|
||||
if (amount < 0) {
|
||||
direction = 'out';
|
||||
amount = -amount;
|
||||
}
|
||||
}
|
||||
const id = this.safeString (item, 'ID');
|
||||
return {
|
||||
'id': id,
|
||||
'info': item,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'direction': direction,
|
||||
'account': undefined,
|
||||
'referenceId': referenceId,
|
||||
'referenceAccount': undefined,
|
||||
'type': type,
|
||||
'currency': code,
|
||||
'amount': amount,
|
||||
'before': undefined,
|
||||
'after': undefined,
|
||||
'status': 'ok',
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
// 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) {
|
||||
//
|
||||
// fetchClosedOrders
|
||||
//
|
||||
// { 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: '' }
|
||||
//
|
||||
// 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) {
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
} else {
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
symbol = market['symbol'];
|
||||
} else {
|
||||
symbol = this.parseSymbol (marketId);
|
||||
}
|
||||
}
|
||||
let timestamp = undefined;
|
||||
if ('Created' in order) {
|
||||
timestamp = this.parse8601 (order['Created'] + '+00:00');
|
||||
}
|
||||
let price = this.safeFloat (order, 'Price');
|
||||
let cost = undefined;
|
||||
const amount = this.safeFloat (order, 'Quantity');
|
||||
const remaining = this.safeFloat (order, 'QuantityRemaining');
|
||||
let filled = undefined;
|
||||
if (amount !== undefined && remaining !== undefined) {
|
||||
filled = amount - remaining;
|
||||
}
|
||||
if (!cost) {
|
||||
if (price && filled) {
|
||||
cost = price * filled;
|
||||
}
|
||||
}
|
||||
if (!price) {
|
||||
if (cost && filled) {
|
||||
price = cost / filled;
|
||||
}
|
||||
}
|
||||
const average = this.safeFloat (order, 'PricePerUnit');
|
||||
const id = this.safeString (order, 'OrderID');
|
||||
return {
|
||||
'info': order,
|
||||
'id': id,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'lastTradeTimestamp': undefined,
|
||||
'symbol': symbol,
|
||||
'type': 'limit',
|
||||
'side': side,
|
||||
'price': price,
|
||||
'cost': cost,
|
||||
'average': average,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'status': status,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'OK': 'closed',
|
||||
'OPEN': 'open',
|
||||
'CANCELED': 'canceled',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseTransaction (transaction, currency = undefined) {
|
||||
//
|
||||
// deposit:
|
||||
//
|
||||
// { ID: 118698752,
|
||||
// Timestamp: '2020-01-21 11:16:09',
|
||||
// Asset: 'ETH',
|
||||
// Amount: 1,
|
||||
// TransactionID: '',
|
||||
// Status: 'CONFIRMED',
|
||||
// Label: '0x748c5c8228d0c596f4d07f338blah',
|
||||
// Symbol: 'ETH' }
|
||||
//
|
||||
// withdrawal:
|
||||
//
|
||||
// { 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' }
|
||||
//
|
||||
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, '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');
|
||||
let address = undefined;
|
||||
let feeCost = undefined;
|
||||
const labelParts = label.split (';');
|
||||
if (labelParts.length === 3) {
|
||||
amount = parseFloat (labelParts[0]);
|
||||
address = labelParts[1];
|
||||
feeCost = parseFloat (labelParts[2]);
|
||||
} else {
|
||||
address = label;
|
||||
}
|
||||
let fee = undefined;
|
||||
if (feeCost !== undefined) {
|
||||
fee = {
|
||||
'currency': code,
|
||||
'cost': feeCost,
|
||||
};
|
||||
}
|
||||
let status = 'ok';
|
||||
if (txid === 'CANCELED') {
|
||||
txid = undefined;
|
||||
status = 'canceled';
|
||||
}
|
||||
return {
|
||||
'info': transaction,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'id': id,
|
||||
'currency': code,
|
||||
'amount': amount,
|
||||
'address': address,
|
||||
'tag': undefined,
|
||||
'status': status,
|
||||
'type': type,
|
||||
'updated': undefined,
|
||||
'txid': txid,
|
||||
'fee': fee,
|
||||
};
|
||||
}
|
||||
|
||||
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') {
|
||||
this.checkRequiredCredentials ();
|
||||
const request = {
|
||||
'apikey': this.apiKey,
|
||||
'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 += 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
+616
@@ -0,0 +1,616 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, InvalidOrder, AuthenticationError, ArgumentsRequired } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class braziliex extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'braziliex',
|
||||
'name': 'Braziliex',
|
||||
'countries': [ 'BR' ],
|
||||
'rateLimit': 1000,
|
||||
'has': {
|
||||
'fetchCurrencies': true,
|
||||
'fetchTickers': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchMyTrades': true,
|
||||
'fetchDepositAddress': true,
|
||||
'fetchOrder': true,
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg',
|
||||
'api': 'https://braziliex.com/api/v1',
|
||||
'www': 'https://braziliex.com/',
|
||||
'doc': 'https://braziliex.com/exchange/api.php',
|
||||
'fees': 'https://braziliex.com/exchange/fees.php',
|
||||
'referral': 'https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'currencies',
|
||||
'ticker',
|
||||
'ticker/{market}',
|
||||
'orderbook/{market}',
|
||||
'tradehistory/{market}',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'post': [
|
||||
'balance',
|
||||
'complete_balance',
|
||||
'open_orders',
|
||||
'trade_history',
|
||||
'deposit_address',
|
||||
'sell',
|
||||
'buy',
|
||||
'cancel_order',
|
||||
'order_status',
|
||||
],
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'EPC': 'Epacoin',
|
||||
'ABC': 'Anti Bureaucracy Coin',
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': 0.005,
|
||||
'taker': 0.005,
|
||||
},
|
||||
},
|
||||
'precision': {
|
||||
'amount': 8,
|
||||
'price': 8,
|
||||
},
|
||||
'options': {
|
||||
'fetchCurrencies': {
|
||||
'expires': 1000, // 1 second
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchCurrenciesFromCache (params = {}) {
|
||||
// this method is now redundant
|
||||
// currencies are now fetched before markets
|
||||
const options = this.safeValue (this.options, 'fetchCurrencies', {});
|
||||
const timestamp = this.safeInteger (options, 'timestamp');
|
||||
const expires = this.safeInteger (options, 'expires', 1000);
|
||||
const now = this.milliseconds ();
|
||||
if ((timestamp === undefined) || ((now - timestamp) > expires)) {
|
||||
const response = await this.publicGetCurrencies (params);
|
||||
this.options['fetchCurrencies'] = this.extend (options, {
|
||||
'response': response,
|
||||
'timestamp': now,
|
||||
});
|
||||
}
|
||||
return this.safeValue (this.options['fetchCurrencies'], 'response');
|
||||
}
|
||||
|
||||
async fetchCurrencies (params = {}) {
|
||||
const response = await this.fetchCurrenciesFromCache (params);
|
||||
//
|
||||
// {
|
||||
// brl: {
|
||||
// name: "Real",
|
||||
// withdrawal_txFee: 0.0075,
|
||||
// txWithdrawalFee: 9,
|
||||
// MinWithdrawal: 30,
|
||||
// minConf: 1,
|
||||
// minDeposit: 0,
|
||||
// txDepositFee: 0,
|
||||
// txDepositPercentageFee: 0,
|
||||
// minAmountTradeFIAT: 5,
|
||||
// minAmountTradeBTC: 0.0001,
|
||||
// minAmountTradeUSDT: 0.0001,
|
||||
// decimal: 8,
|
||||
// decimal_withdrawal: 8,
|
||||
// active: 1,
|
||||
// dev_active: 1,
|
||||
// under_maintenance: 0,
|
||||
// order: "010",
|
||||
// is_withdrawal_active: 1,
|
||||
// is_deposit_active: 1,
|
||||
// is_token_erc20: 0,
|
||||
// is_fiat: 1,
|
||||
// gateway: 0,
|
||||
// },
|
||||
// btc: {
|
||||
// name: "Bitcoin",
|
||||
// txWithdrawalMinFee: 0.000125,
|
||||
// txWithdrawalFee: 0.00015625,
|
||||
// MinWithdrawal: 0.0005,
|
||||
// minConf: 1,
|
||||
// minDeposit: 0,
|
||||
// txDepositFee: 0,
|
||||
// txDepositPercentageFee: 0,
|
||||
// minAmountTradeFIAT: 5,
|
||||
// minAmountTradeBTC: 0.0001,
|
||||
// minAmountTradeUSDT: 0.0001,
|
||||
// decimal: 8,
|
||||
// decimal_withdrawal: 8,
|
||||
// active: 1,
|
||||
// dev_active: 1,
|
||||
// under_maintenance: 0,
|
||||
// order: "011",
|
||||
// is_withdrawal_active: 1,
|
||||
// is_deposit_active: 1,
|
||||
// is_token_erc20: 0,
|
||||
// is_fiat: 0,
|
||||
// gateway: 1,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
this.options['currencies'] = {
|
||||
'timestamp': this.milliseconds (),
|
||||
'response': response,
|
||||
};
|
||||
const ids = Object.keys (response);
|
||||
const result = {};
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
const currency = response[id];
|
||||
const precision = this.safeInteger (currency, 'decimal');
|
||||
const code = this.safeCurrencyCode (id);
|
||||
let active = this.safeInteger (currency, 'active') === 1;
|
||||
const maintenance = this.safeInteger (currency, 'under_maintenance');
|
||||
if (maintenance !== 0) {
|
||||
active = false;
|
||||
}
|
||||
const canWithdraw = this.safeInteger (currency, 'is_withdrawal_active') === 1;
|
||||
const canDeposit = this.safeInteger (currency, 'is_deposit_active') === 1;
|
||||
if (!canWithdraw || !canDeposit) {
|
||||
active = false;
|
||||
}
|
||||
result[code] = {
|
||||
'id': id,
|
||||
'code': code,
|
||||
'name': currency['name'],
|
||||
'active': active,
|
||||
'precision': precision,
|
||||
'funding': {
|
||||
'withdraw': {
|
||||
'active': canWithdraw,
|
||||
'fee': this.safeFloat (currency, 'txWithdrawalFee'),
|
||||
},
|
||||
'deposit': {
|
||||
'active': canDeposit,
|
||||
'fee': this.safeFloat (currency, 'txDepositFee'),
|
||||
},
|
||||
},
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': Math.pow (10, -precision),
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision),
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
'cost': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
'withdraw': {
|
||||
'min': this.safeFloat (currency, 'MinWithdrawal'),
|
||||
'max': Math.pow (10, precision),
|
||||
},
|
||||
'deposit': {
|
||||
'min': this.safeFloat (currency, 'minDeposit'),
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
'info': currency,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const currencies = await this.fetchCurrenciesFromCache (params);
|
||||
const response = await this.publicGetTicker ();
|
||||
//
|
||||
// {
|
||||
// btc_brl: {
|
||||
// active: 1,
|
||||
// market: 'btc_brl',
|
||||
// last: 14648,
|
||||
// percentChange: -0.95,
|
||||
// baseVolume24: 27.856,
|
||||
// quoteVolume24: 409328.039,
|
||||
// baseVolume: 27.856,
|
||||
// quoteVolume: 409328.039,
|
||||
// highestBid24: 14790,
|
||||
// lowestAsk24: 14450.01,
|
||||
// highestBid: 14450.37,
|
||||
// lowestAsk: 14699.98
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
const ids = Object.keys (response);
|
||||
const result = [];
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
const market = response[id];
|
||||
const [ baseId, quoteId ] = id.split ('_');
|
||||
const uppercaseBaseId = baseId.toUpperCase ();
|
||||
const uppercaseQuoteId = quoteId.toUpperCase ();
|
||||
const base = this.safeCurrencyCode (uppercaseBaseId);
|
||||
const quote = this.safeCurrencyCode (uppercaseQuoteId);
|
||||
const symbol = base + '/' + quote;
|
||||
const baseCurrency = this.safeValue (currencies, baseId, {});
|
||||
const quoteCurrency = this.safeValue (currencies, quoteId, {});
|
||||
const quoteIsFiat = this.safeInteger (quoteCurrency, 'is_fiat', 0);
|
||||
let minCost = undefined;
|
||||
if (quoteIsFiat) {
|
||||
minCost = this.safeFloat (baseCurrency, 'minAmountTradeFIAT');
|
||||
} else {
|
||||
minCost = this.safeFloat (baseCurrency, 'minAmountTrade' + uppercaseQuoteId);
|
||||
}
|
||||
const isActive = this.safeInteger (market, 'active');
|
||||
const active = (isActive === 1);
|
||||
const precision = {
|
||||
'amount': 8,
|
||||
'price': 8,
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'baseId': baseId,
|
||||
'quoteId': quoteId,
|
||||
'active': active,
|
||||
'precision': precision,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': Math.pow (10, -precision['amount']),
|
||||
'max': Math.pow (10, precision['amount']),
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision['price']),
|
||||
'max': Math.pow (10, precision['price']),
|
||||
},
|
||||
'cost': {
|
||||
'min': minCost,
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTicker (ticker, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.milliseconds ();
|
||||
const last = this.safeFloat (ticker, 'last');
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'high': this.safeFloat (ticker, 'highestBid24'),
|
||||
'low': this.safeFloat (ticker, 'lowestAsk24'),
|
||||
'bid': this.safeFloat (ticker, 'highestBid'),
|
||||
'bidVolume': undefined,
|
||||
'ask': this.safeFloat (ticker, 'lowestAsk'),
|
||||
'askVolume': undefined,
|
||||
'vwap': undefined,
|
||||
'open': undefined,
|
||||
'close': last,
|
||||
'last': last,
|
||||
'previousClose': undefined,
|
||||
'change': this.safeFloat (ticker, 'percentChange'),
|
||||
'percentage': undefined,
|
||||
'average': undefined,
|
||||
'baseVolume': this.safeFloat (ticker, 'baseVolume24'),
|
||||
'quoteVolume': this.safeFloat (ticker, 'quoteVolume24'),
|
||||
'info': ticker,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTicker (symbol, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.publicGetTickerMarket (this.extend (request, params));
|
||||
return this.parseTicker (response, market);
|
||||
}
|
||||
|
||||
async fetchTickers (symbols = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.publicGetTicker (params);
|
||||
const result = {};
|
||||
const ids = Object.keys (response);
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const marketId = ids[i];
|
||||
const market = this.markets_by_id[marketId];
|
||||
const symbol = market['symbol'];
|
||||
result[symbol] = this.parseTicker (response[marketId], market);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'market': this.marketId (symbol),
|
||||
};
|
||||
const response = await this.publicGetOrderbookMarket (this.extend (request, params));
|
||||
return this.parseOrderBook (response, undefined, 'bids', 'asks', 'price', 'amount');
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
const timestamp = this.parse8601 (this.safeString2 (trade, 'date_exec', 'date'));
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let symbol = undefined;
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const cost = this.safeFloat (trade, 'total');
|
||||
const orderId = this.safeString (trade, 'order_number');
|
||||
const type = 'limit';
|
||||
const side = this.safeString (trade, 'type');
|
||||
const id = this.safeString (trade, '_id');
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': orderId,
|
||||
'type': type,
|
||||
'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 request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.publicGetTradehistoryMarket (this.extend (request, params));
|
||||
return this.parseTrades (response, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const balances = await this.privatePostCompleteBalance (params);
|
||||
const result = { 'info': balances };
|
||||
const currencyIds = Object.keys (balances);
|
||||
for (let i = 0; i < currencyIds.length; i++) {
|
||||
const currencyId = currencyIds[i];
|
||||
const balance = balances[currencyId];
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = this.account ();
|
||||
account['free'] = this.safeFloat (balance, 'available');
|
||||
account['total'] = this.safeFloat (balance, 'total');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
//
|
||||
// {
|
||||
// "order_number":"58ee441d05f8233fadabfb07",
|
||||
// "type":"buy",
|
||||
// "market":"ltc_btc",
|
||||
// "price":"0.01000000",
|
||||
// "amount":"0.00200000",
|
||||
// "total":"0.00002000",
|
||||
// "progress":"1.0000",
|
||||
// "date":"2017-03-12 15:13:33"
|
||||
// }
|
||||
//
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
const marketId = this.safeString (order, 'market');
|
||||
if (marketId in this.markets_by_id) {
|
||||
market = this.markets_by_id[marketId];
|
||||
}
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
let timestamp = this.safeInteger (order, 'timestamp');
|
||||
if (timestamp === undefined) {
|
||||
timestamp = this.parse8601 (this.safeString (order, 'date'));
|
||||
}
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const cost = this.safeFloat (order, 'total', 0.0);
|
||||
const amount = this.safeFloat (order, 'amount');
|
||||
const filledPercentage = this.safeFloat (order, 'progress');
|
||||
const filled = amount * filledPercentage;
|
||||
const remaining = parseFloat (this.amountToPrecision (symbol, amount - filled));
|
||||
let info = order;
|
||||
if ('info' in info) {
|
||||
info = order['info'];
|
||||
}
|
||||
const id = this.safeString (order, 'order_number');
|
||||
const fee = this.safeValue (order, 'fee'); // propagated from createOrder
|
||||
const status = (filledPercentage === 1.0) ? 'closed' : 'open';
|
||||
return {
|
||||
'id': id,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'timestamp': timestamp,
|
||||
'lastTradeTimestamp': undefined,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': 'limit',
|
||||
'side': order['type'],
|
||||
'price': price,
|
||||
'cost': cost,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'trades': undefined,
|
||||
'fee': fee,
|
||||
'info': info,
|
||||
};
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const method = 'privatePost' + this.capitalize (side);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
// 'price': this.priceToPrecision (symbol, price),
|
||||
// 'amount': this.amountToPrecision (symbol, amount),
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
};
|
||||
const response = await this[method] (this.extend (request, params));
|
||||
const success = this.safeInteger (response, 'success');
|
||||
if (success !== 1) {
|
||||
throw new InvalidOrder (this.id + ' ' + this.json (response));
|
||||
}
|
||||
let parts = response['message'].split (' / ');
|
||||
parts = parts.slice (1);
|
||||
const feeParts = parts[5].split (' ');
|
||||
const order = this.parseOrder ({
|
||||
'timestamp': this.milliseconds (),
|
||||
'order_number': response['order_number'],
|
||||
'type': parts[0].toLowerCase (),
|
||||
'market': parts[0].toLowerCase (),
|
||||
'amount': parts[2].split (' ')[1],
|
||||
'price': parts[3].split (' ')[1],
|
||||
'total': parts[4].split (' ')[1],
|
||||
'fee': {
|
||||
'cost': parseFloat (feeParts[1]),
|
||||
'currency': feeParts[2],
|
||||
},
|
||||
'progress': '0.0',
|
||||
'info': response,
|
||||
}, market);
|
||||
const id = order['id'];
|
||||
this.orders[id] = order;
|
||||
return order;
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'order_number': id,
|
||||
'market': market['id'],
|
||||
};
|
||||
return await this.privatePostCancelOrder (this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
if (symbol === undefined) {
|
||||
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
|
||||
}
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'order_number': id,
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.privatePostOrderStatus (this.extend (request, params));
|
||||
return this.parseOrder (response, market);
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.privatePostOpenOrders (this.extend (request, params));
|
||||
return this.parseOrders (response['order_open'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'market': market['id'],
|
||||
};
|
||||
const response = await this.privatePostTradeHistory (this.extend (request, params));
|
||||
return this.parseTrades (response['trade_history'], market, since, limit);
|
||||
}
|
||||
|
||||
async fetchDepositAddress (code, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const currency = this.currency (code);
|
||||
const request = {
|
||||
'currency': currency['id'],
|
||||
};
|
||||
const response = await this.privatePostDepositAddress (this.extend (request, params));
|
||||
const address = this.safeString (response, 'deposit_address');
|
||||
this.checkAddress (address);
|
||||
const tag = this.safeString (response, 'payment_id');
|
||||
return {
|
||||
'currency': code,
|
||||
'address': address,
|
||||
'tag': tag,
|
||||
'info': response,
|
||||
};
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
let url = this.urls['api'] + '/' + api;
|
||||
let query = this.omit (params, this.extractParams (path));
|
||||
if (api === 'public') {
|
||||
url += '/' + this.implodeParams (path, params);
|
||||
if (Object.keys (query).length) {
|
||||
url += '?' + this.urlencode (query);
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
query = this.extend ({
|
||||
'command': path,
|
||||
'nonce': this.nonce (),
|
||||
}, query);
|
||||
body = this.urlencode (query);
|
||||
const signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512');
|
||||
headers = {
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
'Key': this.apiKey,
|
||||
'Sign': this.decode (signature),
|
||||
};
|
||||
}
|
||||
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 ((typeof response === 'string') && (response.length < 1)) {
|
||||
throw new ExchangeError (this.id + ' returned empty response');
|
||||
}
|
||||
if ('success' in response) {
|
||||
const success = this.safeInteger (response, 'success');
|
||||
if (success === 0) {
|
||||
const message = this.safeString (response, 'message');
|
||||
if (message === 'Invalid APIKey') {
|
||||
throw new AuthenticationError (message);
|
||||
}
|
||||
throw new ExchangeError (message);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Exchange = require ('./base/Exchange');
|
||||
const { ExchangeError, AuthenticationError, DDoSProtection, InvalidOrder, InsufficientFunds } = require ('./base/errors');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module.exports = class btcalpha extends Exchange {
|
||||
describe () {
|
||||
return this.deepExtend (super.describe (), {
|
||||
'id': 'btcalpha',
|
||||
'name': 'BTC-Alpha',
|
||||
'countries': [ 'US' ],
|
||||
'version': 'v1',
|
||||
'has': {
|
||||
'fetchTicker': false,
|
||||
'fetchOHLCV': true,
|
||||
'fetchOrders': true,
|
||||
'fetchOpenOrders': true,
|
||||
'fetchClosedOrders': true,
|
||||
'fetchMyTrades': true,
|
||||
},
|
||||
'timeframes': {
|
||||
'1m': '1',
|
||||
'5m': '5',
|
||||
'15m': '15',
|
||||
'30m': '30',
|
||||
'1h': '60',
|
||||
'4h': '240',
|
||||
'1d': 'D',
|
||||
},
|
||||
'urls': {
|
||||
'logo': 'https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg',
|
||||
'api': 'https://btc-alpha.com/api',
|
||||
'www': 'https://btc-alpha.com',
|
||||
'doc': 'https://btc-alpha.github.io/api-docs',
|
||||
'fees': 'https://btc-alpha.com/fees/',
|
||||
'referral': 'https://btc-alpha.com/?r=123788',
|
||||
},
|
||||
'api': {
|
||||
'public': {
|
||||
'get': [
|
||||
'currencies/',
|
||||
'pairs/',
|
||||
'orderbook/{pair_name}/',
|
||||
'exchanges/',
|
||||
'charts/{pair}/{type}/chart/',
|
||||
],
|
||||
},
|
||||
'private': {
|
||||
'get': [
|
||||
'wallets/',
|
||||
'orders/own/',
|
||||
'order/{id}/',
|
||||
'exchanges/own/',
|
||||
'deposits/',
|
||||
'withdraws/',
|
||||
],
|
||||
'post': [
|
||||
'order/',
|
||||
'order-cancel/',
|
||||
],
|
||||
},
|
||||
},
|
||||
'fees': {
|
||||
'trading': {
|
||||
'maker': 0.2 / 100,
|
||||
'taker': 0.2 / 100,
|
||||
},
|
||||
'funding': {
|
||||
'withdraw': {
|
||||
'BTC': 0.00135,
|
||||
'LTC': 0.0035,
|
||||
'XMR': 0.018,
|
||||
'ZEC': 0.002,
|
||||
'ETH': 0.01,
|
||||
'ETC': 0.01,
|
||||
'SIB': 1.5,
|
||||
'CCRB': 4,
|
||||
'PZM': 0.05,
|
||||
'ITI': 0.05,
|
||||
'DCY': 5,
|
||||
'R': 5,
|
||||
'ATB': 0.05,
|
||||
'BRIA': 0.05,
|
||||
'KZC': 0.05,
|
||||
'HWC': 1,
|
||||
'SPA': 1,
|
||||
'SMS': 0.001,
|
||||
'REC': 0.01,
|
||||
'SUP': 1,
|
||||
'BQ': 100,
|
||||
'GDS': 0.1,
|
||||
'EVN': 300,
|
||||
'TRKC': 0.01,
|
||||
'UNI': 1,
|
||||
'STN': 1,
|
||||
'BCH': undefined,
|
||||
'QBIC': 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
'commonCurrencies': {
|
||||
'CBC': 'Cashbery',
|
||||
},
|
||||
'exceptions': {
|
||||
'exact': {},
|
||||
'broad': {
|
||||
'Out of balance': InsufficientFunds, // {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"}
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchMarkets (params = {}) {
|
||||
const response = await this.publicGetPairs (params);
|
||||
const result = [];
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
const market = response[i];
|
||||
const id = this.safeString (market, 'name');
|
||||
const baseId = this.safeString (market, 'currency1');
|
||||
const quoteId = this.safeString (market, 'currency2');
|
||||
const base = this.safeCurrencyCode (baseId);
|
||||
const quote = this.safeCurrencyCode (quoteId);
|
||||
const symbol = base + '/' + quote;
|
||||
const precision = {
|
||||
'amount': 8,
|
||||
'price': this.safeInteger (market, 'price_precision'),
|
||||
};
|
||||
result.push ({
|
||||
'id': id,
|
||||
'symbol': symbol,
|
||||
'base': base,
|
||||
'quote': quote,
|
||||
'active': true,
|
||||
'precision': precision,
|
||||
'limits': {
|
||||
'amount': {
|
||||
'min': this.safeFloat (market, 'minimum_order_size'),
|
||||
'max': this.safeFloat (market, 'maximum_order_size'),
|
||||
},
|
||||
'price': {
|
||||
'min': Math.pow (10, -precision['price']),
|
||||
'max': Math.pow (10, precision['price']),
|
||||
},
|
||||
'cost': {
|
||||
'min': undefined,
|
||||
'max': undefined,
|
||||
},
|
||||
},
|
||||
'info': market,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetchOrderBook (symbol, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'pair_name': this.marketId (symbol),
|
||||
};
|
||||
if (limit) {
|
||||
request['limit_sell'] = limit;
|
||||
request['limit_buy'] = limit;
|
||||
}
|
||||
const response = await this.publicGetOrderbookPairName (this.extend (request, params));
|
||||
return this.parseOrderBook (response, undefined, 'buy', 'sell', 'price', 'amount');
|
||||
}
|
||||
|
||||
parseBidsAsks (bidasks, priceKey = 0, amountKey = 1) {
|
||||
const result = [];
|
||||
for (let i = 0; i < bidasks.length; i++) {
|
||||
const bidask = bidasks[i];
|
||||
if (bidask) {
|
||||
result.push (this.parseBidAsk (bidask, priceKey, amountKey));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
parseTrade (trade, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
market = this.safeValue (this.marketsById, trade['pair']);
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeTimestamp (trade, 'timestamp');
|
||||
const price = this.safeFloat (trade, 'price');
|
||||
const amount = this.safeFloat (trade, 'amount');
|
||||
let cost = undefined;
|
||||
if (price !== undefined) {
|
||||
if (amount !== undefined) {
|
||||
cost = parseFloat (this.costToPrecision (symbol, price * amount));
|
||||
}
|
||||
}
|
||||
const id = this.safeString2 (trade, 'id', 'tid');
|
||||
const side = this.safeString2 (trade, 'my_side', 'side');
|
||||
const orderId = this.safeString (trade, 'o_id');
|
||||
return {
|
||||
'id': id,
|
||||
'info': trade,
|
||||
'timestamp': timestamp,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'symbol': symbol,
|
||||
'order': orderId,
|
||||
'type': 'limit',
|
||||
'side': side,
|
||||
'takerOrMaker': undefined,
|
||||
'price': price,
|
||||
'amount': amount,
|
||||
'cost': cost,
|
||||
'fee': undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
let market = undefined;
|
||||
const request = {};
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
request['pair'] = market['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
const trades = await this.publicGetExchanges (this.extend (request, params));
|
||||
return this.parseTrades (trades, market, since, limit);
|
||||
}
|
||||
|
||||
parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) {
|
||||
return [
|
||||
this.safeTimestamp (ohlcv, 'time'),
|
||||
this.safeFloat (ohlcv, 'open'),
|
||||
this.safeFloat (ohlcv, 'high'),
|
||||
this.safeFloat (ohlcv, 'low'),
|
||||
this.safeFloat (ohlcv, 'close'),
|
||||
this.safeFloat (ohlcv, 'volume'),
|
||||
];
|
||||
}
|
||||
|
||||
async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
'type': this.timeframes[timeframe],
|
||||
};
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
if (since !== undefined) {
|
||||
request['since'] = parseInt (since / 1000);
|
||||
}
|
||||
const response = await this.publicGetChartsPairTypeChart (this.extend (request, params));
|
||||
return this.parseOHLCVs (response, market, timeframe, since, limit);
|
||||
}
|
||||
|
||||
async fetchBalance (params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const response = await this.privateGetWallets (params);
|
||||
const result = { 'info': response };
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
const balance = response[i];
|
||||
const currencyId = this.safeString (balance, 'currency');
|
||||
const code = this.safeCurrencyCode (currencyId);
|
||||
const account = this.account ();
|
||||
account['used'] = this.safeFloat (balance, 'reserve');
|
||||
account['total'] = this.safeFloat (balance, 'balance');
|
||||
result[code] = account;
|
||||
}
|
||||
return this.parseBalance (result);
|
||||
}
|
||||
|
||||
parseOrderStatus (status) {
|
||||
const statuses = {
|
||||
'1': 'open',
|
||||
'2': 'canceled',
|
||||
'3': 'closed',
|
||||
};
|
||||
return this.safeString (statuses, status, status);
|
||||
}
|
||||
|
||||
parseOrder (order, market = undefined) {
|
||||
let symbol = undefined;
|
||||
if (market === undefined) {
|
||||
market = this.safeValue (this.marketsById, order['pair']);
|
||||
}
|
||||
if (market !== undefined) {
|
||||
symbol = market['symbol'];
|
||||
}
|
||||
const timestamp = this.safeTimestamp (order, 'date');
|
||||
const price = this.safeFloat (order, 'price');
|
||||
const amount = this.safeFloat (order, 'amount');
|
||||
const status = this.parseOrderStatus (this.safeString (order, 'status'));
|
||||
const id = this.safeString2 (order, 'oid', 'id');
|
||||
let trades = this.safeValue (order, 'trades', []);
|
||||
trades = this.parseTrades (trades, market);
|
||||
const side = this.safeString2 (order, 'my_side', 'type');
|
||||
let filled = undefined;
|
||||
const numTrades = trades.length;
|
||||
if (numTrades > 0) {
|
||||
filled = 0.0;
|
||||
for (let i = 0; i < numTrades; i++) {
|
||||
filled = this.sum (filled, trades[i]['amount']);
|
||||
}
|
||||
}
|
||||
let remaining = undefined;
|
||||
if ((amount !== undefined) && (amount > 0) && (filled !== undefined)) {
|
||||
remaining = Math.max (0, amount - filled);
|
||||
}
|
||||
return {
|
||||
'id': id,
|
||||
'datetime': this.iso8601 (timestamp),
|
||||
'timestamp': timestamp,
|
||||
'status': status,
|
||||
'symbol': symbol,
|
||||
'type': 'limit',
|
||||
'side': side,
|
||||
'price': price,
|
||||
'cost': undefined,
|
||||
'amount': amount,
|
||||
'filled': filled,
|
||||
'remaining': remaining,
|
||||
'trades': trades,
|
||||
'fee': undefined,
|
||||
'info': order,
|
||||
};
|
||||
}
|
||||
|
||||
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const market = this.market (symbol);
|
||||
const request = {
|
||||
'pair': market['id'],
|
||||
'type': side,
|
||||
'amount': amount,
|
||||
'price': this.priceToPrecision (symbol, price),
|
||||
};
|
||||
const response = await this.privatePostOrder (this.extend (request, params));
|
||||
if (!response['success']) {
|
||||
throw new InvalidOrder (this.id + ' ' + this.json (response));
|
||||
}
|
||||
const order = this.parseOrder (response, market);
|
||||
amount = (order['amount'] > 0) ? order['amount'] : amount;
|
||||
return this.extend (order, {
|
||||
'amount': amount,
|
||||
});
|
||||
}
|
||||
|
||||
async cancelOrder (id, symbol = undefined, params = {}) {
|
||||
const request = {
|
||||
'order': id,
|
||||
};
|
||||
const response = await this.privatePostOrderCancel (this.extend (request, params));
|
||||
return response;
|
||||
}
|
||||
|
||||
async fetchOrder (id, symbol = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {
|
||||
'id': id,
|
||||
};
|
||||
const order = await this.privateGetOrderId (this.extend (request, params));
|
||||
return this.parseOrder (order);
|
||||
}
|
||||
|
||||
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {};
|
||||
let market = undefined;
|
||||
if (symbol !== undefined) {
|
||||
market = this.market (symbol);
|
||||
request['pair'] = market['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
const orders = await this.privateGetOrdersOwn (this.extend (request, params));
|
||||
return this.parseOrders (orders, market, since, limit);
|
||||
}
|
||||
|
||||
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
const request = {
|
||||
'status': '1',
|
||||
};
|
||||
return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
const request = {
|
||||
'status': '3',
|
||||
};
|
||||
return await this.fetchOrders (symbol, since, limit, this.extend (request, params));
|
||||
}
|
||||
|
||||
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
||||
await this.loadMarkets ();
|
||||
const request = {};
|
||||
if (symbol !== undefined) {
|
||||
const market = this.market (symbol);
|
||||
request['pair'] = market['id'];
|
||||
}
|
||||
if (limit !== undefined) {
|
||||
request['limit'] = limit;
|
||||
}
|
||||
const trades = await this.privateGetExchangesOwn (this.extend (request, params));
|
||||
return this.parseTrades (trades, undefined, since, limit);
|
||||
}
|
||||
|
||||
nonce () {
|
||||
return this.milliseconds ();
|
||||
}
|
||||
|
||||
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
|
||||
const query = this.urlencode (this.keysort (this.omit (params, this.extractParams (path))));
|
||||
let url = this.urls['api'] + '/';
|
||||
if (path !== 'charts/{pair}/{type}/chart/') {
|
||||
url += 'v1/';
|
||||
}
|
||||
url += this.implodeParams (path, params);
|
||||
headers = { 'Accept': 'application/json' };
|
||||
if (api === 'public') {
|
||||
if (query.length) {
|
||||
url += '?' + query;
|
||||
}
|
||||
} else {
|
||||
this.checkRequiredCredentials ();
|
||||
let payload = this.apiKey;
|
||||
if (method === 'POST') {
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
body = query;
|
||||
payload += body;
|
||||
} else if (query.length) {
|
||||
url += '?' + query;
|
||||
}
|
||||
headers['X-KEY'] = this.apiKey;
|
||||
headers['X-SIGN'] = this.hmac (this.encode (payload), this.encode (this.secret));
|
||||
headers['X-NONCE'] = this.nonce ().toString ();
|
||||
}
|
||||
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
|
||||
}
|
||||
//
|
||||
// {"date":1570599531.4814300537,"error":"Out of balance -9.99243661 BTC"}
|
||||
//
|
||||
const error = this.safeString (response, 'error');
|
||||
const feedback = this.id + ' ' + body;
|
||||
if (error !== undefined) {
|
||||
this.throwExactlyMatchedException (this.exceptions['exact'], error, feedback);
|
||||
this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback);
|
||||
}
|
||||
if (code === 401 || code === 403) {
|
||||
throw new AuthenticationError (feedback);
|
||||
} else if (code === 429) {
|
||||
throw new DDoSProtection (feedback);
|
||||
}
|
||||
if (code < 400) {
|
||||
return;
|
||||
}
|
||||
throw new ExchangeError (feedback);
|
||||
}
|
||||
};
|
||||
Loaded 100 of 1926 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user