Merge pull request #249 from Superalgos/develop

Beta 5
This commit is contained in:
Luis Fernando Molina authored and GitHub committed 2020-06-01 19:05:48 +02:00
commit d997eaef77
117 files changed
+10768 -4762

No files matched your search

+1
View File
@@ -35,3 +35,4 @@ PATH_TO_WEB_SERVER=./BackendServers/
APP_SCHEMA_PATH=./
WORKSPACE_PATH=./
MY_WORKSPACES_PATH=./My-Workspaces
+74 -7
View File
@@ -432,6 +432,12 @@
"compatibleTypes": "->Shapes->"
},
"properties": [
{
"name": "polygonCondition",
"type": "node",
"childType": "Polygon Condition",
"autoAdd": true
},
{
"name": "polygonBody",
"type": "node",
@@ -452,6 +458,14 @@
}
],
"menuItems": [
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "polygonCondition",
"actionFunction": "payload.onMenuItemClick",
"label": "Add Polygon Condition",
"relatedUiObject": "Polygon Condition"
},
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
@@ -486,6 +500,41 @@
],
"addLeftIcons": true
},
{
"type": "Polygon Condition",
"icon": "style-condition",
"level": 4,
"propertyNameAtParent": "polygonCondition",
"chainAttachesTo": {
"compatibleTypes": "->Polygon->"
},
"editors": {
"code": true
},
"initialValues": {
"code": ""
},
"properties": [],
"menuItems": [
{
"action": "Edit Condition",
"actionFunction": "uiObject.codeEditor.activate",
"label": "Edit Condition",
"iconPathOn": "html",
"iconPathOff": "html"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"actionFunction": "payload.onMenuItemClick",
"label": "Delete Style Condition",
"iconPathOn": "delete",
"iconPathOff": "delete"
}
],
"addLeftIcons": true
},
{
"type": "Polygon Body",
"docURL": "https://docs.superalgos.org/suite-hierarchy-data-mine.html#polygon-body",
@@ -805,12 +854,30 @@
"type": "Condition",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#condition",
"menuItems": [
{
"action": "Edit Condition",
"actionFunction": "uiObject.conditionEditor.activate",
"label": "Edit Condition",
"relatedUiObject": "Condition"
},
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "javascriptCode",
"label": "Add Code",
"relatedUiObject": "Condition",
"relatedUiObject": "Javascript Code",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Fetch Code to Javascript Code",
"label": "Fetch from Javascript Code",
"relatedUiObject": "Javascript Code",
"actionFunction": "payload.onMenuItemClick"
},
{
"action": "Push Code to Javascript Code",
"label": "Push to Javascript Code",
"relatedUiObject": "Javascript Code",
"actionFunction": "payload.onMenuItemClick"
},
{
@@ -1756,7 +1823,7 @@
},
{
"type": "Parameters",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#parameters",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#parameters",
"menuItems": [
{
"action": "Add Missing Children",
@@ -1822,7 +1889,7 @@
},
{
"type": "Base Asset",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#base-asset",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#base-asset",
"menuItems": [
{
"action": "Configure Base Asset",
@@ -2012,7 +2079,7 @@
},
{
"type": "Time Range",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#time-range",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#time-range",
"menuItems": [
{
"action": "Configure Time Range",
@@ -2047,7 +2114,7 @@
},
{
"type": "Time Frame",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#time-frame",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#time-frame",
"menuItems": [
{
"action": "Configure Time Frame",
@@ -2082,7 +2149,7 @@
},
{
"type": "Slippage",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#slippage",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#slippage",
"menuItems": [
{
"action": "Configure Slippage",
@@ -2117,7 +2184,7 @@
},
{
"type": "Fee Structure",
"docURL": "https://docs.superalgos.org/suite-hierarchy-trading-system.html#fee-structure",
"docURL": "https://docs.superalgos.org/suite-hierarchy-network.html#fee-structure",
"menuItems": [
{
"action": "Configure Fee Structure",
+4 -2
View File
@@ -63,8 +63,9 @@ function newAppLoader() {
'Panels/PlotterPanel.js',
'Panels/PanelsVisibleButton.js',
'ControlsToolBox/SidePanel.js',
'ControlsToolBox/SidePanelTab.js',
'SideSpace/SidePanelTab.js',
'SideSpace/ListView.js',
'SideSpace/ListItem.js',
'Spaces/CockpitSpace.js',
'Spaces/TopSpace.js',
@@ -72,6 +73,7 @@ function newAppLoader() {
'Spaces/ChartingSpace.js',
'Spaces/FloatingSpace.js',
'Spaces/DesignSpace.js',
'Spaces/SideSpace.js',
'Files/SingleFile.js',
'Files/FileCloud.js',
Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 247 KiB

+109 -13
View File
@@ -55,19 +55,30 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
if (CONSOLE_LOG === true && request.url.indexOf('NO-LOG') === -1) { console.log('[INFO] webServer -> onBrowserRequest -> request.url = ' + request.url) }
function getBody(callback) { // Gets the de body from a POST request to the web server
let body = ''
try {
request.on('data', function (data) {
body += data
// Too much POST data
if (body.length > 1e6) {
request.connection.destroy()
}
})
let body = ''
request.on('end', function () {
callback(body)
})
request.on('data', function (data) {
body += data
// Too much POST data
//if (body.length > 1e6) {
// request.connection.destroy()
//}
})
request.on('end', function () {
callback(body)
})
request.on('error', function (err) {
if (CONSOLE_LOG === true) { console.log('[INFO] webServer -> onBrowserRequest -> getBody -> err = ' + err.stack) }
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response)
})
} catch (err) {
if (CONSOLE_LOG === true) { console.log('[INFO] webServer -> onBrowserRequest -> getBody -> err = ' + err.stack) }
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response)
}
}
let requestParameters = request.url.split('/')
@@ -170,8 +181,10 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
}
break
}
break
case 'Webhook': {
case 'Webhook':
{
switch (requestParameters[2]) { // switch by command
case 'Fetch-Messages': {
let exchange = requestParameters[3]
@@ -243,9 +256,10 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
break
}
}
break
}
break
case 'ResetLogsAndData':
{
try {
@@ -378,6 +392,7 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
respondWithFile(filePath, response)
}
break
case 'Panels':
{
respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response)
@@ -420,6 +435,12 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
}
break
case 'SideSpace':
{
respondWithFile(process.env.PATH_TO_CANVAS_APP + '/' + requestParameters[1] + '/' + requestParameters[2], response)
}
break
case 'AppSchema.js':
{
let fs = require('fs')
@@ -464,6 +485,81 @@ exports.newWebServer = function newWebServer(EVENTS_SERVER) {
}
break
case 'ListWorkspaces':
{
let dirPath = process.env.MY_WORKSPACES_PATH
try {
let fs = require('fs')
fs.readdir(dirPath, onDirRead)
function onDirRead(err, fileList) {
if (err) {
if (CONSOLE_LOG === true) { console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath) }
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), response)
return
} else {
respondWithContent(JSON.stringify(fileList), response)
return
}
}
} catch (err) {
if (CONSOLE_LOG === true) { console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath) }
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), response)
return
}
}
break
case 'LoadWorkspace':
{
let fileName = unescape(requestParameters[2])
let filePath = process.env.MY_WORKSPACES_PATH + '/' + fileName + '.json'
respondWithFile(filePath, response)
}
break
case 'SaveWorkspace':
{
getBody(processRequest)
async function processRequest(body) {
let fileContent = body
let fileName = unescape(requestParameters[2])
let filePath = process.env.MY_WORKSPACES_PATH + '/' + fileName + '.json'
try {
let fs = require('fs')
let dir = process.env.MY_WORKSPACES_PATH;
/* Create Dir if it does not exist */
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFile(filePath, fileContent, onFileWritten)
function onFileWritten(err) {
if (err) {
if (CONSOLE_LOG === true) { console.log('[ERROR] Error writting the Workspace file. fileName = ' + fileName) }
respondWithContent(JSON.stringify(exchanges), response)
} else {
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), response)
}
}
} catch (err) {
if (CONSOLE_LOG === true) { console.log('[ERROR] Error writting the Workspace file. fileName = ' + fileName) }
respondWithContent(JSON.stringify(exchanges), response)
}
}
}
break
case 'DataMines':
{
respondWithFile(process.env.DATA_MINES_PATH + '/' + requestParameters[2] + '.json', response)
+11 -2
View File
@@ -144,12 +144,20 @@ function newAnimation () {
browserCanvas.height = 1
}
/* Media Recording */
/* Video Recording */
videoRecorder.physics()
if (areWeRecording === true) {
if (ARE_WE_RECORDING_A_VIDEO === true) {
videoRecorder.recordCanvas()
}
/* Panorama Recording */
if (ARE_WE_RECORDING_A_MARKET_PANORAMA === true) {
if (PANORAMA_WAS_PANNED === true) {
addToMarketPanorama()
PANORAMA_WAS_PANNED = false
}
}
/* We request the next frame to be drawn, and stablishing a loop */
/* Use this code for full animation speed. */
@@ -168,6 +176,7 @@ function newAnimation () {
}
function clearBrowserCanvas () {
if (canWeDraw === false) { return }
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
browserCanvasContext.clearRect(0, 0, browserCanvas.width, browserCanvas.height)
}
+78 -46
View File
@@ -3,7 +3,6 @@ let browserCanvasContext // The context of the canvas object.
let stepsInitializationCounter = 0 // This counter the initialization steps required to be able to turn off the splash screen.
let marketInitializationCounter = 0 // This counter the initialization of markets required to be able to turn off the splash screen.
let splashScreenNeeded = true
function newCanvas () {
const MODULE_NAME = 'Canvas'
@@ -32,12 +31,13 @@ function newCanvas () {
let thisObject = {
eventHandler: undefined,
topSpace: undefined,
sideSpace: undefined,
chartingSpace: undefined,
floatingSpace: undefined,
panelsSpace: undefined,
cockpitSpace: undefined,
bottomSpace: undefined,
designSpace: undefined,
splashScreen: undefined,
animation: undefined,
mouse: undefined,
shorcutNumbers: new Map(),
@@ -47,7 +47,6 @@ function newCanvas () {
thisObject.eventHandler = newEventHandler()
let splashScreen
let lastContainerMouseOver
let lastShortcutKeyRejection
@@ -63,6 +62,7 @@ function newCanvas () {
function finalize () {
try {
thisObject.sideSpace.finalize()
thisObject.chartingSpace.finalize()
thisObject.floatingSpace.finalize()
thisObject.shorcutNumbers = undefined
@@ -86,7 +86,7 @@ function newCanvas () {
browserCanvas.removeEventListener('keydown', onKeyDown, false)
browserCanvas.removeEventListener('keyup', onKeyUp, false)
splashScreen = undefined
thisObject.splashScreen = undefined
lastContainerMouseOver = undefined
hisObject.mouse = undefined
@@ -109,22 +109,22 @@ function newCanvas () {
thisObject.topSpace = newTopSpace()
thisObject.topSpace.initialize()
thisObject.designSpace = newDesignSpace()
thisObject.designSpace.initialize()
thisObject.cockpitSpace = newCockpitSpace()
thisObject.cockpitSpace.initialize()
thisObject.designSpace = newDesignSpace()
thisObject.designSpace.initialize()
thisObject.panelsSpace = newPanelsSpace()
thisObject.panelsSpace.initialize()
thisObject.chartingSpace = newChartingSpace()
thisObject.chartingSpace.initialize()
thisObject.bottomSpace = thisObject.cockpitSpace
thisObject.sideSpace = newSideSpace()
thisObject.sideSpace.initialize()
splashScreen = newSplashScreen()
splashScreen.initialize()
thisObject.splashScreen = newSplashScreen()
let animation = newAnimation()
animation.initialize()
@@ -137,6 +137,7 @@ function newCanvas () {
animation.addCallBackFunction('Charting Space Physics', thisObject.chartingSpace.physics)
animation.addCallBackFunction('Design Space Physics', thisObject.designSpace.physics)
animation.addCallBackFunction('Panels Space Physics', thisObject.panelsSpace.physics)
animation.addCallBackFunction('Side Space Physics', thisObject.sideSpace.physics)
/* Spcaces Drawing */
animation.addCallBackFunction('Floating Space Draw', thisObject.floatingSpace.draw)
@@ -145,7 +146,8 @@ function newCanvas () {
animation.addCallBackFunction('CockpitSpace Draw', thisObject.cockpitSpace.draw)
animation.addCallBackFunction('Design Space Draw', thisObject.designSpace.draw)
animation.addCallBackFunction('Top Space Draw', thisObject.topSpace.draw)
animation.addCallBackFunction('Splash Screen Draw', splashScreen.draw)
animation.addCallBackFunction('Side Space Draw', thisObject.sideSpace.draw)
animation.addCallBackFunction('Splash Screen Draw', thisObject.splashScreen.draw)
animation.start()
} catch (err) {
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
@@ -211,30 +213,35 @@ function newCanvas () {
if ((event.ctrlKey === true || event.metaKey === true)) {
let constructorParams
switch (event.keyCode) {
case 118: { // F7
downloadPanorama('Superalgos.Market.Panorama')
return
break
}
case 119: { // F8
downloadCanvas('Superalgos.image.capture')
downloadCanvas('Superalgos.Image.Capture', browserCanvas)
return
break
}
case 120: { // F9
constructorParams = { format: 'gif', workersPath: 'externalScripts/', framerate: 8, name: 'Superalgos.video.capture'}
constructorParams = { format: 'gif', workersPath: 'externalScripts/', framerate: 8, name: 'Superalgos.Video.Capture'}
break
}
case 121: { // F10
constructorParams = { format: 'webm', framerate: 8, name: 'Superalgos.video.capture' }
constructorParams = { format: 'webm', framerate: 8, name: 'Superalgos.Video.Capture' }
break
}
default: return
}
if (areWeRecording === false) {
if (ARE_WE_RECORDING_A_VIDEO === false) {
console.log('RECORDING', constructorParams.format)
mediaRecorder = new CCapture(constructorParams)
mediaRecorder.start()
areWeRecording = true
ARE_WE_RECORDING_A_VIDEO = true
} else {
console.log('SAVING', constructorParams.format)
areWeRecording = false
ARE_WE_RECORDING_A_VIDEO = false
mediaRecorder.stop()
mediaRecorder.save()
}
@@ -295,11 +302,7 @@ function newCanvas () {
}
if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && (event.key === 'S' || event.key === 's')) {
let saved = canvas.designSpace.workspace.save()
if (saved === true) {
canvas.cockpitSpace.setStatus('Workspace Saved.', 50, canvas.cockpitSpace.statusTypes.ALL_GOOD)
}
canvas.designSpace.workspace.save()
event.preventDefault()
return
}
@@ -578,8 +581,21 @@ function newCanvas () {
let container
/* We check if the mouse is over an element of the Design Space / */
/* We check if the mouse is over an element of the Side Space / */
container = thisObject.sideSpace.getContainer(point)
if (container !== undefined && container.isDraggeable === true) {
containerBeingDragged = container
containerDragStarted = true
containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point)
return
}
if (container !== undefined && container.isDraggeable === false) {
return
}
/* We check if the mouse is over an element of the Design Space / */
container = thisObject.designSpace.getContainer(point)
if (container !== undefined && container.isDraggeable === true) {
@@ -593,8 +609,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over an element of the Top Space / */
/* We check if the mouse is over an element of the Top Space / */
container = thisObject.topSpace.getContainer(point)
if (container !== undefined && container.isDraggeable === true) {
@@ -604,8 +619,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over an element of the CockpitSpace / */
/* We check if the mouse is over an element of the CockpitSpace / */
container = thisObject.cockpitSpace.getContainer(point)
if (container !== undefined && container.isDraggeable === true) {
@@ -620,8 +634,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over a panel/ */
/* We check if the mouse is over a panel/ */
container = thisObject.panelsSpace.getContainer(point, GET_CONTAINER_PURPOSE.DRAGGING)
if (container !== undefined && event.shiftKey === false) {
@@ -640,8 +653,7 @@ function newCanvas () {
return
}
/* we check if it is over any of the existing containers at the Charting Space. */
/* we check if it is over any of the existing containers at the Charting Space. */
container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.DRAGGING)
if (container !== undefined) {
@@ -698,8 +710,15 @@ function newCanvas () {
let container
/* We check if the mouse is over an element of the Design Space / */
/* We check if the mouse is over an element of the Side Space / */
container = thisObject.sideSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_CLICK)
if (container !== undefined && container.isClickeable === true) {
container.eventHandler.raiseEvent('onMouseClick', point)
return
}
/* We check if the mouse is over an element of the Design Space / */
container = thisObject.designSpace.getContainer(point)
if (container !== undefined && container.isClickeable === true) {
@@ -707,8 +726,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over an element of the Top Space / */
/* We check if the mouse is over an element of the Top Space / */
container = thisObject.topSpace.getContainer(point)
if (container !== undefined && container.isClickeable === true) {
@@ -716,8 +734,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over an element of the CockpitSpace / */
/* We check if the mouse is over an element of the CockpitSpace / */
container = thisObject.cockpitSpace.getContainer(point)
if (container !== undefined && container.isClickeable === true) {
@@ -725,8 +742,7 @@ function newCanvas () {
return
}
/* We check if the mouse is over a panel/ */
/* We check if the mouse is over a panel/ */
container = thisObject.panelsSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_CLICK)
if (container !== undefined && container.isClickeable === true) {
@@ -734,8 +750,7 @@ function newCanvas () {
return
}
/* If it is not, then we check if it is over any of the existing containers at the Charting Space. */
/* If it is not, then we check if it is over any of the existing containers at the Charting Space. */
container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_CLICK)
if (container !== undefined && container.isClickeable === true) {
@@ -837,6 +852,16 @@ function newCanvas () {
let container
/* We check if the mouse is over an element of the Designe Space / */
if (thisObject.sideSpace !== undefined) {
container = thisObject.sideSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_OVER)
if (container !== undefined && container.detectMouseOver === true) {
containerFound()
return
}
}
/* We check if the mouse is over an element of the Designe Space / */
if (thisObject.designSpace !== undefined) {
container = thisObject.designSpace.getContainer(point)
@@ -934,6 +959,16 @@ function newCanvas () {
event.mousePosition = point
let container
/* Side Space */
container = canvas.sideSpace.getContainer({ x: point.x, y: point.y })
if (container !== undefined && container.isWheelable === true) {
container.eventHandler.raiseEvent('onMouseWheel', event)
return false // This instructs the browser not to take the event and scroll the page.
}
/* Panel Space */
container = canvas.panelsSpace.getContainer({ x: point.x, y: point.y })
if (container !== undefined && container.isWheelable === true) {
@@ -941,8 +976,7 @@ function newCanvas () {
return false // This instructs the browser not to take the event and scroll the page.
}
/* We try the CockpitSpace. */
/* We try the CockpitSpace. */
container = canvas.cockpitSpace.getContainer({ x: point.x, y: point.y })
if (container !== undefined && container.isWheelable === true) {
@@ -950,8 +984,7 @@ function newCanvas () {
return false // This instructs the browser not to take the event and scroll the page.
}
/* Charting Space. */
/* Charting Space. */
container = canvas.chartingSpace.getContainer({ x: point.x, y: point.y }, GET_CONTAINER_PURPOSE.MOUSE_WHEEL)
if (container !== undefined && container.isWheelable === true) {
container.eventHandler.raiseEvent('onMouseWheel', event)
@@ -963,8 +996,7 @@ function newCanvas () {
return false
}
/* We try second with floating objects. */
/* We try second with floating objects. */
container = canvas.floatingSpace.getContainer({ x: point.x, y: point.y })
if (container !== undefined && container.isWheelable === true) {
+12 -3
View File
@@ -195,9 +195,18 @@ function newEdgeEditor () {
}
if (event.shiftKey === false && event.ctrlKey === false && event.code === 'ArrowRight') {
onDragStarted(event)
thisObject.container.frame.position.x = thisObject.container.frame.position.x - STEP
whatHappened = 'left or right arrow key pressed'
if (ARE_WE_RECORDING_A_MARKET_PANORAMA === true) {
if (PANORAMA_WAS_PANNED === false) {
PANORAMA_WAS_PANNED = true
onDragStarted(event)
thisObject.container.frame.position.x = thisObject.container.frame.position.x - STEP * 2
whatHappened = 'left or right arrow key pressed'
}
} else {
onDragStarted(event)
thisObject.container.frame.position.x = thisObject.container.frame.position.x - STEP
whatHappened = 'left or right arrow key pressed'
}
return
}
+16 -12
View File
@@ -181,18 +181,6 @@ function newSpeedometer () {
/* We put the thisObject.params.VALUE in the middle */
fontSize = 22
if (thisObject.params.VALUE > 999) {
fontSize = 18
}
if (thisObject.params.VALUE > 9999) {
fontSize = 14
}
if (thisObject.params.VALUE > 99999) {
fontSize = 10
}
browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY
label = thisObject.params.VALUE
if (isNaN(label) === false) {
@@ -202,6 +190,22 @@ function newSpeedometer () {
if (label === 0) { label = label.toFixed(2) }
}
fontSize = 22
if (label.length > 8) {
fontSize = 18
}
if (label.length > 10) {
fontSize = 14
}
if (label.length > 12) {
fontSize = 10
}
if (label.length > 14) {
fontSize = 8
}
browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY
// label = label.substring(0, 6)
xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 7
@@ -1,5 +1,7 @@
function newUiObjectsFromNodes () {
thisObject = {
runTasks: runTasks,
runSessions: runSessions,
recreateWorkspace: recreateWorkspace,
tryToConnectChildrenWithReferenceParents: tryToConnectChildrenWithReferenceParents,
createUiObjectFromNode: createUiObjectFromNode,
@@ -14,7 +16,7 @@ function newUiObjectsFromNodes () {
return thisObject
function recreateWorkspace (node, replacingCurrentWorkspace) {
function recreateWorkspace (node, callBackFunction) {
mapOfNodes = new Map()
tasksToRun = []
sessionsToRun = []
@@ -22,7 +24,7 @@ function newUiObjectsFromNodes () {
addIncludedNodes()
function addIncludedNodes () {
blobService = newFileStorage()
let blobService = newFileStorage()
// if (node.code === undefined) {
node.code = '{ \n"includeDataMines": ["Masters", "Sparta", "TradingEngines"],\n"includeTradingSystems": ["Sparta-WHB-BTC-USDT", "Example-ETH-USDT", "Sparta-BRR-BTC-USDT"],\n"includeSuperScripts": ["Masters"]\n }'
@@ -135,15 +137,8 @@ function newUiObjectsFromNodes () {
tryToConnectChildrenWithReferenceParents()
if (replacingCurrentWorkspace === true) {
// We need to wait all tasks that were potentially running to stop
setTimeout(runTasks, 70000)
// We give a few seconds for the tasks to start
setTimeout(runSessions, 80000)
} else {
runTasks()
// We give a few seconds for the tasks to start
setTimeout(runSessions, 10000)
if (callBackFunction !== undefined) {
callBackFunction() // The recreation of the workspace is complete
}
}
}
+100 -14
View File
@@ -11,6 +11,7 @@ function newWorkspace () {
enabled: false,
nodeChildren: undefined,
eventsServerClients: new Map(),
replaceWorkspaceByLoadingOne: replaceWorkspaceByLoadingOne,
save: saveWorkspace,
getHierarchyHeads: getHierarchyHeads,
getNodeThatIsOnFocus: getNodeThatIsOnFocus,
@@ -68,7 +69,7 @@ function newWorkspace () {
let workingAtTask = 0
let circularProgressBar = newBusyProgressBar()
circularProgressBar.fitFunction = canvas.floatingSpace.fitIntoVisibleArea
let droppedNode
let loadedWorkspaceNode
let sessionTimestamp = (new Date()).valueOf()
window.localStorage.setItem('Session Timestamp', sessionTimestamp)
@@ -85,24 +86,56 @@ function newWorkspace () {
function initialize () {
try {
let reset = window.localStorage.getItem('Reset')
let savedWorkspace = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Workspace')
let lastUsedWorkspace = window.localStorage.getItem('Last Used Workspace')
if (savedWorkspace === null || reset !== null) {
thisObject.workspaceNode = getWorkspace()
if (lastUsedWorkspace !== 'undefined' && lastUsedWorkspace !== null && lastUsedWorkspace !== undefined) {
let blobService = newFileStorage()
blobService.getFileFromHost('LoadWorkspace' + '/' + lastUsedWorkspace, onFileReceived, true)
function onFileReceived (err, text, response) {
if (err && err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
canvas.cockpitSpace.setStatus('Could not load the last Workspace used, called "' + lastUsedWorkspace + '". Will switch to the default Workspace instead.', 500, canvas.cockpitSpace.statusTypes.WARNING)
thisObject.workspaceNode = getWorkspace() // This is the default workspace that comes with the system.
recreateWorkspace()
return
}
thisObject.workspaceNode = JSON.parse(text)
recreateWorkspace()
}
} else {
thisObject.workspaceNode = JSON.parse(savedWorkspace)
thisObject.workspaceNode = getWorkspace() // This is the default workspace that comes with the system.
recreateWorkspace()
}
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode)
setupEventsServerClients()
thisObject.enabled = true
setInterval(saveWorkspace, 60000)
function recreateWorkspace () {
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode, finishInitialization)
}
function finishInitialization () {
setupEventsServerClients()
runTasksAndSessions(false)
thisObject.enabled = true
canvas.cockpitSpace.initializePosition()
canvas.splashScreen.initialize()
setInterval(saveWorkspace, 60000)
}
} catch (err) {
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
}
}
function runTasksAndSessions (replacingCurrentWorkspace) {
if (replacingCurrentWorkspace === true) {
// We need to wait all tasks that were potentially running to stop
setTimeout(functionLibraryUiObjectsFromNodes.runTasks, 70000)
// We give a few seconds for the tasks to start
setTimeout(functionLibraryUiObjectsFromNodes.runSessions, 80000)
} else {
functionLibraryUiObjectsFromNodes.runTasks()
// We give a few seconds for the tasks to start
setTimeout(functionLibraryUiObjectsFromNodes.runSessions, 10000)
}
}
function setupEventsServerClients () {
for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) {
let rootNode = thisObject.workspaceNode.rootNodes[i]
@@ -170,6 +203,23 @@ function newWorkspace () {
let textToSave = stringifyWorkspace()
window.localStorage.setItem(CANVAS_APP_NAME + '.' + 'Workspace', textToSave)
window.localStorage.setItem('Session Timestamp', sessionTimestamp)
if (workspace.name !== undefined) {
let url = 'SaveWorkspace/' + workspace.name
callServer(textToSave, url, onResponse)
}
function onResponse (err) {
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
window.localStorage.setItem('Last Used Workspace', workspace.name)
window.localStorage.setItem('Session Timestamp', sessionTimestamp)
if (ARE_WE_RECORDING_A_MARKET_PANORAMA === false) {
canvas.cockpitSpace.setStatus(workspace.name + ' Saved.', 50, canvas.cockpitSpace.statusTypes.ALL_GOOD)
}
} else {
canvas.cockpitSpace.setStatus('Could not save the Workspace at the Backend. Please check the Backend Console for more information.', 150, canvas.cockpitSpace.statusTypes.WARNING)
}
}
return true
}
}
@@ -203,13 +253,14 @@ function newWorkspace () {
workingAtTask++
break
case 3:
thisObject.workspaceNode = droppedNode
droppedNode = undefined
thisObject.workspaceNode = loadedWorkspaceNode
loadedWorkspaceNode = undefined
workingAtTask++
break
case 4:
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode, true)
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode)
setupEventsServerClients()
runTasksAndSessions(true)
workingAtTask++
break
case 5:
@@ -304,6 +355,30 @@ function newWorkspace () {
return nodes
}
function replaceWorkspaceByLoadingOne (name) {
let blobService = newFileStorage()
blobService.getFileFromHost('LoadWorkspace' + '/' + name, onFileReceived, true)
function onFileReceived (err, text, response) {
if (err && err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
canvas.cockpitSpace.setStatus('Could not load the Workspace called "' + name + '". ', 500, canvas.cockpitSpace.statusTypes.WARNING)
return
}
loadedWorkspaceNode = JSON.parse(text)
saveWorkspace()
canvas.cockpitSpace.toTop()
let position = {
x: browserCanvas.width / 2,
y: browserCanvas.height / 2
}
circularProgressBar.initialize(position)
circularProgressBar.visible = true
workingAtTask = 1
}
}
function spawn (nodeText, mousePointer) {
try {
let point = {
@@ -314,9 +389,10 @@ function newWorkspace () {
spawnPosition.x = point.x
spawnPosition.y = point.y
droppedNode = JSON.parse(nodeText)
let droppedNode = JSON.parse(nodeText)
if (droppedNode.type === 'Workspace') {
loadedWorkspaceNode = droppedNode
circularProgressBar.initialize(mousePointer)
circularProgressBar.visible = true
workingAtTask = 1
@@ -508,6 +584,16 @@ function newWorkspace () {
referenceDetachNode(payload.node)
}
break
case 'Push Code to Javascript Code':
{
payload.node.javascriptCode.code = payload.node.code
}
break
case 'Fetch Code to Javascript Code':
{
payload.node.code = payload.node.javascriptCode.code
}
break
case 'Open Documentation':
{
let definition = APP_SCHEMA_MAP.get(payload.node.type)
File diff suppressed because it is too large. Load diff
+7 -7
View File
@@ -25,8 +25,8 @@ function newPicker () {
thisObject.container.frame.radius = 0
thisObject.container.frame.position.x = 0
thisObject.container.frame.position.y = 0
thisObject.container.frame.width = 200
thisObject.container.frame.height = 120
thisObject.container.frame.width = 150
thisObject.container.frame.height = 60
let isMouseOver
let optionsList
@@ -175,7 +175,7 @@ function newPicker () {
}
function drawForeground () {
const FONT_SIZE = 20
const FONT_SIZE = 15
const VISIBLE_LABELS = 5
let fontSize
let fontColor
@@ -190,11 +190,11 @@ function newPicker () {
fontColor = UI_COLOR.LIGHT_GREY
switch (i) {
case 0:
fontSize = FONT_SIZE - 10
fontSize = FONT_SIZE - 5
opacity = 0.4
break
case 1:
fontSize = FONT_SIZE - 5
fontSize = FONT_SIZE - 3
opacity = 0.5
break
case 2:
@@ -203,11 +203,11 @@ function newPicker () {
opacity = 1
break
case 3:
fontSize = FONT_SIZE - 5
fontSize = FONT_SIZE - 3
opacity = 0.5
break
case 4:
fontSize = FONT_SIZE - 10
fontSize = FONT_SIZE - 5
opacity = 0.4
break
}
+20 -1
View File
@@ -1205,7 +1205,26 @@ function newUiObject () {
}
}
function isEditorVisible () {
if (thisObject.codeEditor !== undefined) {
if (thisObject.codeEditor.visible === true) { return true }
}
if (thisObject.configEditor !== undefined) {
if (thisObject.configEditor.visible === true) { return true }
}
if (thisObject.conditionEditor !== undefined) {
if (thisObject.conditionEditor.visible === true) { return true }
}
if (thisObject.formulaEditor !== undefined) {
if (thisObject.formulaEditor.visible === true) { return true }
}
}
function drawText () {
if (isEditorVisible() === true) { return }
/* Text Follows */
let position = {
x: 0,
@@ -1332,7 +1351,7 @@ function newUiObject () {
label = currentValue
if (!isNaN(label)) {
if (currentValue.toFixed !== undefined) {
label = currentValue.toFixed(2)
label = dynamicDecimals(currentValue, 2)
}
}
+8 -3
View File
@@ -190,8 +190,7 @@ const MAX_PLOTABLE_DATE = maxDate;
const TOP_SPACE_HEIGHT = 40
const COCKPIT_SPACE_HEIGHT = 30;
const BREAKPOINT_HEIGHT = 15;
const SIDE_PANEL_WIDTH = 450
const SIDE_PANEL_WIDTH = 350
let COCKPIT_SPACE_POSITION = browserCanvas.height - COCKPIT_SPACE_HEIGHT
const LAYER_STATUS = {
@@ -222,7 +221,13 @@ const TIME_MACHINE_WIDTH = 8;
const TIME_MACHINE_HEIGHT = 8;
let mediaRecorder // to downloadText canvas animation as a mediaRecorder
let areWeRecording = false
let marketPanoramaCanvas
let ARE_WE_RECORDING_A_VIDEO = false
let ARE_WE_RECORDING_A_MARKET_PANORAMA = false
let PANORAMA_WAS_PANNED = false
let CURRENT_PANORAMA_POSITION = 0
let canWeDraw = false // This global variable regulates when spaces can or can not draw their contents.
function toRadians(angle) {
return angle * (Math.PI / 180);
+1 -1
View File
@@ -170,7 +170,7 @@ function newLayer () {
'Plotter Module->Plotter->' +
'Shapes->' +
'Chart Points->Point->Point Formula->' +
'Polygon->Polygon Body->Style->Style Condition->Style->' +
'Polygon->Polygon Condition->Polygon Body->Style->Style Condition->Style->' +
'Polygon Border->Style->Style Condition->Style->' +
'Polygon Vertex->Point->' +
'Plotter Panel->Javascript Code->Panel Data->Data Formula->'
+1 -1
View File
@@ -154,7 +154,7 @@ function newPlotterPanel () {
if (panelData.code.valueDecimals !== undefined) {
if (value !== undefined) {
if (isNaN(value) === false) {
value = value.toFixed(panelData.code.valueDecimals)
value = dynamicDecimals(value, panelData.code.valueDecimals)
}
}
}
+16 -4
View File
@@ -570,23 +570,23 @@ function newPlotter () {
let x = 0
let y = 0
eval(point.pointFormula.code)
let dataPoint = {
let rawPoint = {
x: x,
y: y
}
/* Contributing to Auto-Scale */
coordinateSystem.reportYValue(dataPoint.y)
/*
The information we store in files is independent from the charing system and its coordinate systems.
That means that the first thing we allways need to do is to trasform these points to the coordinate system of the timeline.
*/
dataPoint = coordinateSystem.transformThisPoint(dataPoint)
let dataPoint
dataPoint = coordinateSystem.transformThisPoint(rawPoint)
dataPoint = transformThisPoint(dataPoint, thisObject.container)
dataPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(dataPoint)
dataPoint = thisObject.fitFunction(dataPoint)
/* Store the data point at the local map */
dataPoint.rawPoint = rawPoint
dataPoints.set(point.id, dataPoint)
}
}
@@ -598,6 +598,12 @@ function newPlotter () {
for (let j = 0; j < productDefinition.referenceParent.shapes.polygons.length; j++) {
let polygon = productDefinition.referenceParent.shapes.polygons[j]
/* We will check if we need to plot this Polygon or not. */
if (polygon.polygonCondition !== undefined) {
let mustPlot = eval(polygon.polygonCondition.code)
if (mustPlot !== true) { continue }
}
let calculatedStyle
/* Finding out the fill style */
@@ -724,6 +730,12 @@ function newPlotter () {
console.log('[WARN] You have a Polygon Vertex not referencing any Point.')
continue
}
/* Contributing to Auto-Scale */
if (dataPointObject.rawPoint.y > 1 && dataPointObject.rawPoint.y !== undefined && isNaN(dataPointObject.rawPoint.y) === false) {
coordinateSystem.reportYValue(dataPointObject.rawPoint.y)
}
let dataPoint = {
x: dataPointObject.x,
y: dataPointObject.y
+107
View File
@@ -0,0 +1,107 @@
function newListItem () {
const MODULE_NAME = 'List Item'
const ERROR_LOG = true
const logger = newWebDebugLog()
logger.fileName = MODULE_NAME
let thisObject = {
container: undefined,
fitFunction: undefined,
physics: physics,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
}
thisObject.container = newContainer()
thisObject.container.initialize()
thisObject.container.isDraggeable = false
thisObject.container.isWheelable = false
thisObject.container.detectMouseOver = true
thisObject.container.isClickeable = true
thisObject.container.frame.width = SIDE_PANEL_WIDTH * 0.75
thisObject.container.frame.height = SIDE_PANEL_WIDTH * 0.75
let name
let type
let onMouseOverEventSubscriptionId
let onMouseNotOverEventSubscriptionId
let onMouseClickEventSubscriptionId
let isMouseOver = false
return thisObject
function finalize () {
thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId)
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
thisObject.container.eventHandler.stopListening(onMouseClickEventSubscriptionId)
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
}
function initialize (pName, pType) {
name = pName
type = pType
onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
onMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
}
function getContainer (point) {
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
return thisObject.container
}
}
function onMouseOver (event) {
isMouseOver = true
}
function onMouseNotOver () {
isMouseOver = false
}
function onMouseClick (event) {
canvas.designSpace.workspace.replaceWorkspaceByLoadingOne(name)
}
function physics () {
}
function draw () {
let icon = canvas.designSpace.iconByUiObjectType.get(type)
let backgroundColor = UI_COLOR.BLACK
let labelColor
const RED_LINE_HIGHT = 4
const OPACITY = 1
let params = {
cornerRadius: 0,
lineWidth: 1,
container: thisObject.container,
borderColor: UI_COLOR.RUSTED_RED,
castShadow: false,
backgroundColor: backgroundColor,
opacity: OPACITY
}
if (isMouseOver === false) {
labelColor = UI_COLOR.WHITE
} else {
labelColor = UI_COLOR.TITANIUM_YELLOW
}
roundedCornersBackground(params)
drawLabel(name, 1 / 2, 3.0 / 10, -5, 0, 15, thisObject.container, labelColor)
drawLabel(type, 1 / 2, 7.2 / 10, -5, 0, 15, thisObject.container, labelColor)
drawIcon(icon, 1 / 2, 1 / 2, 0, 0, 80, thisObject.container)
}
}
+271
View File
@@ -0,0 +1,271 @@
function newListView () {
const MODULE_NAME = 'List View'
const ERROR_LOG = true
const logger = newWebDebugLog()
logger.fileName = MODULE_NAME
let thisObject = {
fitFunction: undefined,
container: undefined,
listItems: [],
payload: undefined,
isVisible: false,
resize: resize,
turnOn: turnOn,
turnOff: turnOff,
physics: physics,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
}
thisObject.container = newContainer()
thisObject.container.initialize()
thisObject.container.isDraggeable = false
thisObject.container.isWheelable = true
thisObject.container.detectMouseOver = true
let isInitialized = false
let listItemsMap = new Map()
let visibleListItems = []
let firstVisibleListItem
let headerHeight = 40
let footerHeight = 30
let listItemHeight = SIDE_PANEL_WIDTH * 0.75
let posibleVisibleListItems
let itemSeparation
let onMouseWheelEventSuscriptionId
return thisObject
function finalize () {
thisObject.container.eventHandler.stopListening(onMouseWheelEventSuscriptionId)
listItemsMap = undefined
visibleListItems = undefined
thisObject.container.finalize()
thisObject.container = undefined
thisObject.payload = undefined
thisObject = undefined
}
function initialize () {
thisObject.container.name = MODULE_NAME
thisObject.container.frame.containerName = thisObject.container.name
thisObject.container.frame.width = SIDE_PANEL_WIDTH * 0.75
let position = { // Default position
x: SIDE_PANEL_WIDTH * 0.25 / 2,
y: 20
}
thisObject.container.frame.position = position
onMouseWheelEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
resize()
turnOn()
}
function resize () {
thisObject.container.frame.height = browserCanvas.height - thisObject.container.frame.position.y * 2
let spaceForListItems = (thisObject.container.frame.height - headerHeight - footerHeight)
posibleVisibleListItems = Math.trunc(spaceForListItems / (listItemHeight))
if ((posibleVisibleListItems - 1) !== 0) {
itemSeparation = (spaceForListItems - listItemHeight * posibleVisibleListItems) / (posibleVisibleListItems - 1)
} else {
itemSeparation = 0
}
}
function turnOn () {
firstVisibleListItem = 1
callServer(undefined, 'ListWorkspaces', onResponse)
function onResponse (err, text) {
if (err && err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> Could not load the list of your workspaces from the backend. ') }
return
}
let workspacelist = JSON.parse(text)
thisObject.listItems = []
for (let i = 0; i < workspacelist.length; i++) {
let workspace = workspacelist[i].replace('.json', '')
let listItem = newListItem()
listItem.initialize(workspace, 'Workspace')
listItem.container.connectToParent(thisObject.container, false, false)
thisObject.listItems.push(listItem)
}
calculateVisbleListItems()
isInitialized = true
thisObject.isVisible = true
}
}
function turnOff () {
thisObject.isVisible = false
}
function onMouseWheel (event) {
delta = event.wheelDelta
if (delta > 0) {
delta = -1
} else {
delta = 1
}
firstVisibleListItem = firstVisibleListItem + delta
calculateVisbleListItems()
}
function calculateVisbleListItems () {
let availableSlots = Math.trunc(posibleVisibleListItems)
if (firstVisibleListItem < 1) { firstVisibleListItem = 1 }
if (firstVisibleListItem > (thisObject.listItems.length - availableSlots + 1)) { firstVisibleListItem = thisObject.listItems.length - availableSlots + 1 }
visibleListItems = []
for (let i = 0; i < thisObject.listItems.length; i++) {
if (i + 1 >= firstVisibleListItem && i + 1 < firstVisibleListItem + availableSlots) {
let listItem = thisObject.listItems[i]
listItem.container.frame.position.x = 0
listItem.container.frame.position.y = (listItemHeight + itemSeparation) * visibleListItems.length + headerHeight
/* Add to Visible Product Array */
visibleListItems.push(listItem)
}
}
}
function getContainer (point, purpose) {
if (isInitialized === false || thisObject.isVisible === false) { return }
let container
/* First we check if thisObject point is inside thisObject space. */
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
/* Now we see which is the inner most container that has it */
for (let i = 0; i < visibleListItems.length; i++) {
container = visibleListItems[i].getContainer(point)
if (container !== undefined) {
if (container.isForThisPurpose(purpose) === true) {
return container
}
}
}
return thisObject.container
}
}
function childrenPhysics () {
for (let i = 0; i < thisObject.listItems.length; i++) {
let listItem = thisObject.listItems[i]
listItem.physics()
}
}
function physics () {
if (isInitialized === false || thisObject.isVisible === false) { return }
if (isInitialized === false) { return }
if (thisObject.isVisible === false) { return }
childrenPhysics()
}
function draw () {
if (isInitialized === false || thisObject.isVisible === false) { return }
drawBackground()
drawChildren()
drawScrollBar()
}
function drawChildren () {
for (let i = 0; i < visibleListItems.length; i++) {
let listItem = visibleListItems[i]
if (listItem !== undefined) {
listItem.draw()
}
}
}
function drawBackground () {
let label = 'Your Workspaces'
let backgroundColor = UI_COLOR.WHITE
let params = {
cornerRadius: 5,
lineWidth: 1,
container: thisObject.container,
borderColor: UI_COLOR.RUSTED_RED,
castShadow: false,
backgroundColor: backgroundColor,
opacity: 1
}
roundedCornersBackground(params)
drawLabel(label, 1 / 2, 0, 0, 25, 15, thisObject.container, UI_COLOR.BLACK)
}
function drawScrollBar () {
if (thisObject.listItems.length > posibleVisibleListItems && posibleVisibleListItems > 0) {
let xOffset = 4
let barTopPoint = {
x: thisObject.container.frame.width - xOffset,
y: headerHeight
}
let barBottomPoint = {
x: thisObject.container.frame.width - xOffset,
y: thisObject.container.frame.height - footerHeight
}
let ratio = (posibleVisibleListItems * listItemHeight + (posibleVisibleListItems - 1) * itemSeparation) / (thisObject.listItems.length * listItemHeight + (thisObject.listItems.length - 1) * itemSeparation)
let handleHeight = posibleVisibleListItems * (listItemHeight * ratio) + (posibleVisibleListItems - 1) * itemSeparation * ratio
let handleTopPoint = {
x: thisObject.container.frame.width - xOffset,
y: headerHeight + (listItemHeight * ratio * (firstVisibleListItem - 1) + itemSeparation * ratio * (firstVisibleListItem - 1))
}
let handleBottomPoint = {
x: thisObject.container.frame.width - xOffset,
y: handleTopPoint.y + handleHeight
}
barTopPoint = thisObject.container.frame.frameThisPoint(barTopPoint)
barBottomPoint = thisObject.container.frame.frameThisPoint(barBottomPoint)
handleTopPoint = thisObject.container.frame.frameThisPoint(handleTopPoint)
handleBottomPoint = thisObject.container.frame.frameThisPoint(handleBottomPoint)
browserCanvasContext.beginPath()
browserCanvasContext.moveTo(barTopPoint.x, barTopPoint.y)
browserCanvasContext.lineTo(barBottomPoint.x, barBottomPoint.y)
browserCanvasContext.closePath()
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 1
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + 1 + ')'
browserCanvasContext.stroke()
browserCanvasContext.beginPath()
browserCanvasContext.moveTo(handleTopPoint.x, handleTopPoint.y)
browserCanvasContext.lineTo(handleBottomPoint.x, handleBottomPoint.y)
browserCanvasContext.closePath()
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 4
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + 1 + ')'
browserCanvasContext.stroke()
}
}
}
@@ -3,6 +3,8 @@ function newSidePanelTab () {
let thisObject = {
pointerDirection: 'left',
container: undefined,
resize: resize,
physics: physics,
draw: draw,
getContainer: getContainer,
initialize: initialize
@@ -27,20 +29,27 @@ function newSidePanelTab () {
function initialize () {
thisObject.container.frame.width = TAB_WIDTH
thisObject.container.frame.height = TAB_HEIGHT
thisObject.container.frame.position.x = 0 + thisObject.container.parentContainer.frame.width
thisObject.container.frame.position.y = 20
ORIGINAL_PARENT_POSITON = thisObject.container.parentContainer.frame.position.x
thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
resize()
isInitialized = true
}
function resize () {
thisObject.container.frame.position.x = 0 + thisObject.container.parentContainer.frame.width
thisObject.container.frame.position.y = 69
setClosed()
}
function onMouseClick (event) {
if (thisObject.pointerDirection === 'left') {
animation = 'opening'
thisObject.container.eventHandler.raiseEvent('opening')
} else {
animation = 'closing'
thisObject.container.eventHandler.raiseEvent('closing')
}
}
@@ -53,9 +62,13 @@ function newSidePanelTab () {
}
}
function draw () {
function physics () {
if (isInitialized === false) { return }
animate()
}
function draw () {
if (isInitialized === false) { return }
borders()
arrow()
}
@@ -66,28 +79,37 @@ function newSidePanelTab () {
if (animation === 'opening') {
xOffset = xOffset + STEP
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
if (xOffset > thisObject.container.parentContainer.frame.width) {
animation = 'none'
xOffset = thisObject.container.parentContainer.frame.width
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
thisObject.container.parentContainer.status = 'visible'
thisObject.pointerDirection = 'right'
if (xOffset >= thisObject.container.parentContainer.frame.width) {
setOpened()
thisObject.container.eventHandler.raiseEvent('opened')
}
}
if (animation === 'closing') {
xOffset = xOffset - STEP
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
if (xOffset < 0) {
animation = 'none'
xOffset = 0
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON
thisObject.container.parentContainer.status = 'hidden'
thisObject.pointerDirection = 'left'
if (xOffset <= 0) {
setClosed()
thisObject.container.eventHandler.raiseEvent('closed')
}
}
}
function setOpened () {
animation = 'none'
xOffset = thisObject.container.parentContainer.frame.width
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
thisObject.container.parentContainer.status = 'visible'
thisObject.pointerDirection = 'right'
}
function setClosed () {
animation = 'none'
xOffset = 0
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON
thisObject.container.parentContainer.status = 'hidden'
thisObject.pointerDirection = 'left'
}
function arrow () {
let point1
let point2
@@ -213,4 +235,3 @@ function newSidePanelTab () {
browserCanvasContext.stroke()
}
}
+7 -2
View File
@@ -92,12 +92,16 @@ function newChartingSpace () {
function initialize () {
if (isInitialized === true) { return }
if (canvas.designSpace === undefined) { return }
if (canvas.designSpace.workspace === undefined) { return }
let rootNodes = canvas.designSpace.workspace.workspaceNode.rootNodes
for (let i = 0; i < rootNodes.length; i++) {
let rootNode = rootNodes[i]
if (rootNode.type === 'Charting Space') {
thisObject.payload = rootNode.payload
if (rootNode !== null) {
if (rootNode.type === 'Charting Space') {
thisObject.payload = rootNode.payload
}
}
}
@@ -402,6 +406,7 @@ function newChartingSpace () {
}
function draw () {
if (canWeDraw === false) { return }
if (thisObject.visible === false) { return }
drawBackground()
+19 -10
View File
@@ -20,7 +20,8 @@
physics: physics,
getContainer: getContainer,
finalize: finalize,
initialize: initialize
initialize: initialize,
initializePosition: initializePosition
}
thisObject.container = newContainer()
@@ -39,6 +40,7 @@
let statusCounter = 0
let statusText
let statusType
let isInitilized = false
return thisObject
@@ -52,6 +54,15 @@
}
function initialize () {
canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize)
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
thisObject.fullscreen = newFullScreen()
thisObject.fullscreen.container.connectToParent(thisObject.container)
thisObject.fullscreen.initialize()
}
function initializePosition () {
thisObject.container.frame.position.x = 0
let INITIAL_POSITION
@@ -68,13 +79,7 @@
INITIAL_POSITION = 100
thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT
}
canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize)
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
thisObject.fullscreen = newFullScreen()
thisObject.fullscreen.container.connectToParent(thisObject.container)
thisObject.fullscreen.initialize()
isInitilized = true
}
function onMouseClick (event) {
@@ -148,6 +153,7 @@
thisObject.fullscreen.physics()
}
function thisObjectPhysics () {
if (isInitilized === false) { return }
/* Check the limits */
thisObject.status = 'MIDDLE'
@@ -201,6 +207,7 @@
}
function draw () {
if (canWeDraw === false) { return }
thisObject.container.frame.draw(false, false)
drawBackground()
@@ -266,8 +273,10 @@
printLabel(statusText, position.x, position.y, 1, 15, textColor, true, thisObject.container)
}
if (canvas.designSpace.workspace.enabled === true && statusText === undefined) {
arrow()
if (canvas.designSpace !== undefined) {
if (canvas.designSpace.workspace.enabled === true && statusText === undefined) {
arrow()
}
}
}
+1
View File
@@ -334,6 +334,7 @@ function newDesignSpace () {
}
function draw () {
if (canWeDraw === false) { return }
if (thisObject.sidePanel !== undefined) {
thisObject.sidePanel.draw()
}
+1
View File
@@ -415,6 +415,7 @@ function newFloatingSpace () {
}
function draw () {
if (canWeDraw === false) { return }
if (visible === false) { return }
drawBackground()
thisObject.floatingLayer.draw()
+1
View File
@@ -336,6 +336,7 @@ function newPanelsSpace () {
}
function draw () {
if (canWeDraw === false) { return }
if (thisObject.visible !== true) { return }
thisObject.container.frame.draw(false, false)
@@ -1,12 +1,13 @@
function newSidePanel () {
const MODULE_NAME = 'Side Panel'
function newSideSpace () {
const MODULE_NAME = 'Side Space'
let thisObject = {
areas: [],
sidePanelTab: undefined,
container: undefined,
physics: physics,
draw: draw,
getContainer: getContainer,
initialize: initialize
initialize: initialize,
finalize: finalize
}
let isInitialized = false
@@ -16,9 +17,16 @@ function newSidePanel () {
thisObject.container.initialize()
thisObject.container.isClickeable = true
thisObject.container.isDraggeable = false
thisObject.container.detectMouseOver = true
thisObject.container.status = 'hidden'
let listView
resize()
let openingEventSubscriptionId
let closedEventSubscriptionId
let browserResizedEventSubscriptionId
return thisObject
function initialize () {
@@ -26,21 +34,48 @@ function newSidePanel () {
thisObject.sidePanelTab.container.connectToParent(thisObject.container, false, false)
thisObject.sidePanelTab.initialize()
canvas.eventHandler.listenToEvent('Browser Resized', resize)
browserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize)
initializeListView()
isInitialized = true
}
function finalize () {
canvas.eventHandler.stopListening(browserResizedEventSubscriptionId)
thisObject.sidePanelTab.container.eventHandler.stopListening(openingEventSubscriptionId)
thisObject.sidePanelTab.container.eventHandler.stopListening(closedEventSubscriptionId)
}
function initializeListView () {
listView = newListView()
listView.initialize()
listView.container.connectToParent(thisObject.container, false, false)
openingEventSubscriptionId = thisObject.sidePanelTab.container.eventHandler.listenToEvent('opening', listView.turnOn)
closedEventSubscriptionId = thisObject.sidePanelTab.container.eventHandler.listenToEvent('closed', listView.turnOff)
}
function resize () {
thisObject.container.frame.width = SIDE_PANEL_WIDTH
thisObject.container.frame.height = browserCanvas.height // - TOP_SPACE_HEIGHT
thisObject.container.frame.position.x = -SIDE_PANEL_WIDTH
thisObject.container.frame.position.y = 0 // TOP_SPACE_HEIGHT
if (listView !== undefined) {
listView.resize()
}
if (thisObject.sidePanelTab !== undefined) {
thisObject.sidePanelTab.resize()
}
}
function getContainer (point) {
function getContainer (point, purpose) {
let container
container = thisObject.sidePanelTab.getContainer(point)
container = thisObject.sidePanelTab.getContainer(point, purpose)
if (container !== undefined) { return container }
container = listView.getContainer(point, purpose)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
@@ -50,15 +85,17 @@ function newSidePanel () {
}
}
function physics () {
thisObject.sidePanelTab.physics()
listView.physics()
}
function draw () {
if (canWeDraw === false) { return }
if (isInitialized === false) { return }
borders()
thisObject.sidePanelTab.draw()
for (let i = 0; i < thisObject.areas.length; i++) {
let area = thisObject.areas[i]
area.draw()
}
listView.draw()
}
function borders () {
+2 -1
View File
@@ -54,6 +54,7 @@ function newTopSpace () {
}
function draw () {
if (canWeDraw === false) { return }
thisObject.container.frame.draw(false, false)
drawBackground()
@@ -65,7 +66,7 @@ function newTopSpace () {
let opacity = 1
browserCanvasContext.beginPath()
browserCanvasContext.rect(0, 0, thisObject.container.frame.width, thisObject.container.frame.height)
if (areWeRecording === true) {
if (ARE_WE_RECORDING_A_VIDEO === true) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'
} else {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'
+8 -5
View File
@@ -13,7 +13,7 @@ function newSplashScreen () {
initialize: initialize
}
var container = newContainer()
let container = newContainer()
container.initialize()
thisObject.container = container
@@ -31,21 +31,22 @@ function newSplashScreen () {
let logo
let canDrawLogo = false
let isInitialized = false
let splashScreenNeeded = true
return thisObject
function initialize () {
logo = new Image()
logo.onload = onImageLoad
function onImageLoad () {
canDrawLogo = true
}
logo.src = window.canvasApp.urlPrefix + 'Images/superalgos-logo.png'
canvas.eventHandler.listenToEvent('Browser Resized', resize)
isInitialized = true
}
function resize () {
@@ -54,6 +55,7 @@ function newSplashScreen () {
}
function draw () {
if (isInitialized === false) { return }
if (splashScreenNeeded === false) {
stepsInitializationCounter = 100
canDrawLogo = false
@@ -101,6 +103,7 @@ function newSplashScreen () {
if (stepsInitializationCounter > 99) {
splashScreenNeeded = false
canWeDraw = true
stepsInitializationCounter = 99
}
@@ -122,7 +125,7 @@ function newSplashScreen () {
fontSize = 35
title = 'Beta 4'
title = 'Beta 5'
titlePoint = {
x: thisObject.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO - 20,
+34 -2
View File
@@ -108,8 +108,8 @@ function downloadText (filename, text) {
document.body.removeChild(element)
}
function downloadCanvas (filename) {
let data = browserCanvas.toDataURL('image/png', 1)
function downloadCanvas (filename, canvasToUse) {
let data = canvasToUse.toDataURL('image/png', 1)
/* Change MIME type to trick the browser to downlaod the file instead of displaying it */
data = data.replace(/^data:image\/[^;]*/, 'data:application/octet-stream')
@@ -125,6 +125,38 @@ function downloadCanvas (filename) {
document.body.removeChild(element)
};
function downloadPanorama (filename) {
let INITIAL_WIDTH = 800
let FINAL_WIDTH = browserCanvas.width - INITIAL_WIDTH
if (ARE_WE_RECORDING_A_MARKET_PANORAMA === false) {
/* We start recording the panorame. */
ARE_WE_RECORDING_A_MARKET_PANORAMA = true
marketPanoramaCanvas = document.createElement('canvas')
marketPanoramaCanvas.width = INITIAL_WIDTH * 50
marketPanoramaCanvas.height = browserCanvas.height
marketPanoramaCanvas.getContext('2d').drawImage(browserCanvas, 0, 0, INITIAL_WIDTH, browserCanvas.height, 0, 0, INITIAL_WIDTH, browserCanvas.height)
CURRENT_PANORAMA_POSITION = INITIAL_WIDTH
} else {
/* We stop recording and download the resulting image. */
ARE_WE_RECORDING_A_MARKET_PANORAMA = false
marketPanoramaCanvas.getContext('2d').drawImage(browserCanvas, browserCanvas.width - FINAL_WIDTH, 0, FINAL_WIDTH, browserCanvas.height, CURRENT_PANORAMA_POSITION, 0, FINAL_WIDTH, browserCanvas.height)
let finalResultCanvas = document.createElement('canvas')
finalResultCanvas.width = CURRENT_PANORAMA_POSITION + FINAL_WIDTH
finalResultCanvas.height = browserCanvas.height
finalResultCanvas.getContext('2d').drawImage(marketPanoramaCanvas, 0, 0, finalResultCanvas.width, browserCanvas.height, 0, 0, finalResultCanvas.width, browserCanvas.height)
downloadCanvas(filename, finalResultCanvas)
marketPanoramaCanvas = undefined
}
}
function addToMarketPanorama () {
let SECTION_WIDTH = 80 * 2
let INITIAL_POSITION = 800 - SECTION_WIDTH
marketPanoramaCanvas.getContext('2d').drawImage(browserCanvas, INITIAL_POSITION, 0, SECTION_WIDTH, browserCanvas.height, CURRENT_PANORAMA_POSITION, 0, SECTION_WIDTH, browserCanvas.height)
CURRENT_PANORAMA_POSITION = CURRENT_PANORAMA_POSITION + SECTION_WIDTH
}
function transformThisPoint (point, container) {
/* We make the point relative to the current frame */
+1 -1
View File
@@ -1 +1 @@
{"type":"Data Mine","name":"Masters","code":"{\n\"codeName\": \"Masters\"\n}","sensorBots":[{"type":"Sensor Bot","name":"Exchange Raw Data","code":"{\n\"codeName\": \"Exchange-Raw-Data\",\n\"repo\": \"Exchange-Raw-Data-Sensor-Bot\"\n}","processes":[{"type":"Process Definition","name":"Historic-Trades","code":"{\n \"codeName\": \"Historic-Trades\",\n \"description\": \"Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"e3008f15-43ae-4452-9c28-bfbb6c1a5e84","savedPayload":{"position":{"x":13889.892304785908,"y":17976.7326423862},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"f796022a-7871-4bde-bcc7-bcf2818f1eaf","savedPayload":{"position":{"x":14299.325525762739,"y":17993.12379366583},"targetPosition":{"x":13938.17907937083,"y":17993.12379366583},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"116834de-6646-4387-9f4b-4f711cea9235"}}}],"dataDependencies":[],"id":"1332b41e-7cd2-4e69-a40a-39ea47df67b5","savedPayload":{"position":{"x":13938.17907937083,"y":17993.12379366583},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"116834de-6646-4387-9f4b-4f711cea9235","savedPayload":{"position":{"x":13983.913279342734,"y":18015.677405195172},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"a9ea7169-fcd8-48c3-b0a9-b4e8941aa170","savedPayload":{"position":{"x":14026.312379860185,"y":18044.00757842793},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"51908884-c7a2-46ca-8a55-209070ae5fa6","savedPayload":{"position":{"x":14064.650920558077,"y":18077.62957624547},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"7a6a9399-474f-4a93-bcbd-28a594ef1943","savedPayload":{"position":{"x":14351.93363503598,"y":18425.400341491753},"targetPosition":{"x":13888.04337325749,"y":18696.024787447222},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Process Definition","name":"Historic-OHLCVs","code":"{\n \"codeName\": \"Historic-OHLCVs\",\n \"description\": \"Retrieves and saves historical maket data\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"dfe2556e-a765-4e38-9d30-7f91090c0069","savedPayload":{"position":{"x":14546.018334597831,"y":18039.39680205973},"targetPosition":{"x":14407.127481538195,"y":18558.43550715096},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Exchange Raw Data Historic-OHLCVs","code":"{\n \"mainUtility\": \"Self ReferencLine truncated
{"type":"Data Mine","name":"Masters","code":"{\n\"codeName\": \"Masters\"\n}","sensorBots":[{"type":"Sensor Bot","name":"Exchange Raw Data","code":"{\n\"codeName\": \"Exchange-Raw-Data\",\n\"repo\": \"Exchange-Raw-Data-Sensor-Bot\"\n}","processes":[{"type":"Process Definition","name":"Historic-Trades","code":"{\n \"codeName\": \"Historic-Trades\",\n \"description\": \"Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"e3008f15-43ae-4452-9c28-bfbb6c1a5e84","savedPayload":{"position":{"x":13889.892304785908,"y":17976.7326423862},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":"{\n \"mainUtility\": \"Self Reference\"\n}","id":"f796022a-7871-4bde-bcc7-bcf2818f1eaf","savedPayload":{"position":{"x":14299.325525762739,"y":17993.12379366583},"targetPosition":{"x":13938.17907937083,"y":17993.12379366583},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"116834de-6646-4387-9f4b-4f711cea9235"}}}],"dataDependencies":[],"id":"1332b41e-7cd2-4e69-a40a-39ea47df67b5","savedPayload":{"position":{"x":13938.17907937083,"y":17993.12379366583},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","id":"116834de-6646-4387-9f4b-4f711cea9235","savedPayload":{"position":{"x":13983.913279342734,"y":18015.677405195172},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"a9ea7169-fcd8-48c3-b0a9-b4e8941aa170","savedPayload":{"position":{"x":14026.312379860185,"y":18044.00757842793},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"51908884-c7a2-46ca-8a55-209070ae5fa6","savedPayload":{"position":{"x":14064.650920558077,"y":18077.62957624547},"targetPosition":{"x":13788.99537092664,"y":18353.285125876908},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":4,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"7a6a9399-474f-4a93-bcbd-28a594ef1943","savedPayload":{"position":{"x":14351.93363503598,"y":18425.400341491753},"targetPosition":{"x":13888.04337325749,"y":18696.024787447222},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Process Definition","name":"Historic-OHLCVs","code":"{\n \"codeName\": \"Historic-OHLCVs\",\n \"description\": \"Retrieves and saves historical maket data\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"dfe2556e-a765-4e38-9d30-7f91090c0069","savedPayload":{"position":{"x":14546.018334597831,"y":18039.39680205973},"targetPosition":{"x":14407.127481538195,"y":18558.43550715096},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Exchange Raw Data Historic-OHLCVs","code":"{\n \"mainUtility\": \"Self ReferencLine truncated
+1 -1
View File
@@ -1 +1 @@
{"type":"Data Mine","name":"Trading Engines","code":"{ \n\"codeName\": \"TradingEngines\"\n}","sensorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Protocol Engine","code":"{\n\"codeName\": \"Protocol-Engine\",\n\"repo\": \"Protocol-Engine-Trading-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period","code":"{\n \"codeName\": \"Multi-Period\",\n \"startMode\": {\n \"userDefined\": {\n \"run\": \"true\"\n }\n },\n \"deadWaitTime\": 0,\n \"liveWaitTime\": 60000,\n \"normalWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period\",\n \"validPeriods\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"startDate\": {},\n \"endDate\": {}\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"2448e10f-8851-4f99-bbc3-4114045de8c0","savedPayload":{"position":{"x":12616.197483671245,"y":20140.853579740375},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"d7335089-1f50-4d48-bcbe-115136a88e6a"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"d1b73084-294b-46f5-a2c4-da08b994b770","savedPayload":{"position":{"x":12449.156688273564,"y":20626.87276271356},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"274082a4-1695-4e9b-a0ff-9464fa2d986f"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"156aa7e6-0514-465e-9734-3983a51ab69c","savedPayload":{"position":{"x":11997.878022585815,"y":20866.417195564853},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"52f26c43-552c-4557-af7a-878b951f5731"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"8067fbac-fbc3-4712-9c24-4b4b9a24d78d","savedPayload":{"position":{"x":11497.788898746769,"y":20785.44093339513},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"afb38dc6-0437-4215-8a7d-3091cd3c74fb"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"72529350-c20e-4f28-a308-2611df7a6468","savedPayload":{"position":{"x":11151.505147800972,"y":20402.34305659919},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"cb317eb0-047c-4076-bd46-7add2423aa87"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"1b491b01-465a-4f98-831d-a587fc428b58","savedPayload":{"position":{"x":11191.18832670422,"y":19893.807598803403},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"41498979-9228-4151-8b94-0d8acdf257e8"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"5711ecc2-5192-4d97-a13d-5269264ea825","savedPayload":{"position":{"x":11500.627966186497,"y":19501.183635137346},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"823a30de-093f-4c39-9181-5727c81bbad3"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"3be12bcb-4634-4933-9ae1-bad5089b8062","savedPayload":{"position":{"x":12002.767486869507,"y":19387.560434016126},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozeLine truncated
{"type":"Data Mine","name":"Trading Engines","code":"{ \n\"codeName\": \"TradingEngines\"\n}","sensorBots":[],"indicatorBots":[],"tradingBots":[{"type":"Trading Bot","name":"Protocol Engine","code":"{\n\"codeName\": \"Protocol-Engine\",\n\"repo\": \"Protocol-Engine-Trading-Bot\"\n}","processes":[{"type":"Process Definition","name":"Multi-Period","code":"{\n \"codeName\": \"Multi-Period\",\n \"startMode\": {\n \"userDefined\": {\n \"run\": \"true\"\n }\n },\n \"deadWaitTime\": 0,\n \"liveWaitTime\": 60000,\n \"normalWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period\",\n \"validPeriods\": [\n \"45-min\",\n \"40-min\",\n \"30-min\",\n \"20-min\",\n \"15-min\",\n \"10-min\",\n \"05-min\",\n \"04-min\",\n \"03-min\",\n \"02-min\",\n \"01-min\"\n ],\n \"startDate\": {},\n \"endDate\": {}\n }\n}","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"2448e10f-8851-4f99-bbc3-4114045de8c0","savedPayload":{"position":{"x":12616.197483671245,"y":20140.853579740375},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"d7335089-1f50-4d48-bcbe-115136a88e6a"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"d1b73084-294b-46f5-a2c4-da08b994b770","savedPayload":{"position":{"x":12449.156688273564,"y":20626.87276271356},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"274082a4-1695-4e9b-a0ff-9464fa2d986f"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"156aa7e6-0514-465e-9734-3983a51ab69c","savedPayload":{"position":{"x":11997.878022585815,"y":20866.417195564853},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"52f26c43-552c-4557-af7a-878b951f5731"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"8067fbac-fbc3-4712-9c24-4b4b9a24d78d","savedPayload":{"position":{"x":11497.788898746769,"y":20785.44093339513},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"afb38dc6-0437-4215-8a7d-3091cd3c74fb"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"72529350-c20e-4f28-a308-2611df7a6468","savedPayload":{"position":{"x":11151.505147800972,"y":20402.34305659919},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"cb317eb0-047c-4076-bd46-7add2423aa87"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"1b491b01-465a-4f98-831d-a587fc428b58","savedPayload":{"position":{"x":11191.18832670422,"y":19893.807598803403},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"41498979-9228-4151-8b94-0d8acdf257e8"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"5711ecc2-5192-4d97-a13d-5269264ea825","savedPayload":{"position":{"x":11500.627966186497,"y":19501.183635137346},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"823a30de-093f-4c39-9181-5727c81bbad3"}}},{"type":"Output Dataset","name":"New Output Dataset","id":"3be12bcb-4634-4933-9ae1-bad5089b8062","savedPayload":{"position":{"x":12002.767486869507,"y":19387.560434016126},"targetPosition":{"x":11869.94158089557,"y":20140.853579740375},"floatingObject":{"isPinLine truncated
@@ -384,6 +384,7 @@
record.beginRate = dailyFile[i][4];
record.endRate = dailyFile[i][5];
record.triggerOnSituation = dailyFile[i][6];
record.name = dailyFile[i][7];
if (
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
@@ -456,6 +457,7 @@
record.beginRate = marketFile[i][4];
record.endRate = marketFile[i][5];
record.triggerOnSituation = marketFile[i][6];
record.name = marketFile[i][7];
if (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) {
@@ -581,7 +583,11 @@
/* Situation Name */
let digit = record.begin.toFixed(0).substring(7,8)
let positionY = recordPoint3.y - (recordPoint3.y - recordPoint1.y) / 2 + (Number(digit) * 10 - 50)
if (canvas.chartingSpace.viewport.zoomLevel >= ZOOM_OUT_THRESHOLD_FOR_HIDDING_CHARTS_LABELS) {
if (record.name !== "New Strategy") {
printLabel(record.name, recordPoint1.x + 5, positionY - 15, '1', 12);
}
if (record.triggerOnSituation !== "New Situation") {
printLabel(record.triggerOnSituation, recordPoint1.x + 5, positionY, '1', 12);
}
+1
View File
@@ -0,0 +1 @@
{"type":"Workspace","name":"Binance - WHB - BBTB","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","code":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34247\", \n\"webSocketsPort\": \"8080\"\n}","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","exchangeDataProducts":[{"type":"Exchange Data Products","name":"Binance","singleMarketData":[{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"dd7c8795-bb24-4438-9d77-b37a9000ad7f","savedPayload":{"position":{"x":39655.39345377463,"y":28084.121285348076},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"98e6c917-f4b7-4b71-9215-de3ccaf3dc0c","savedPayload":{"position":{"x":39619.08803001109,"y":27971.34006849996},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"1d7dd537-5f6f-4eb2-8e56-ddba0aef720f","savedPayload":{"position":{"x":39594.11393185164,"y":27855.840877332517},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"221c8d22-8a7f-4014-a7ed-0bafc886b123","savedPayload":{"position":{"x":39580.59887306321,"y":27738.507421368748},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"6929ebe7-6486-4299-add5-ed0282500d91","savedPayload":{"position":{"x":39578.655464896365,"y":27620.63069875915},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4a3ed8eb-a73e-4fb4-835a-681cf47d7373","savedPayload":{"position":{"x":39588.297198778826,"y":27502.946480135615},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"5ffc6a67-e33b-422b-8e36-f9711f93c723","savedPayload":{"position":{"x":39609.428887621216,"y":27386.77785099828},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"48f7953c-f62d-4292-b042-d1ac05f8f80f","savedPayload":{"position":{"x":39641.84608404141,"y":27273.24280350331},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"3b9a3c93-6d41-440e-89a2-2fd1b2dde603","savedPayload":{"position":{"x":39685.2360649887,"y":27163.433854438405},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapLine truncated
+1 -1
View File
@@ -1 +1 @@
{"type":"Workspace","name":"Simple Workspace","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","code":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34247\", \n\"webSocketsPort\": \"8080\"\n}","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","exchangeDataProducts":[{"type":"Exchange Data Products","name":"New Exchange Data Products","singleMarketData":[{"type":"Single Market Data","name":"","dataProducts":[{"type":"Data Product","name":"Candles","id":"cc7f62f2-0aa8-498c-861c-85060dd70522","savedPayload":{"position":{"x":39641.333811210454,"y":28936.98413868935},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"70e3b1ab-65d9-4c9e-8489-35d6852e7ef5","savedPayload":{"position":{"x":39612.59634583407,"y":28848.5110343909},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"eebb207f-6a7e-405a-9bed-2ff3afdd12af","savedPayload":{"position":{"x":39594.15352768551,"y":28758.904338266497},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"09706013-dc8f-4e40-8a71-129191aa521d","savedPayload":{"position":{"x":39585.513470979095,"y":28668.600155855387},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"d81b957c-4a39-4c15-8158-c05a0e63e591","savedPayload":{"position":{"x":39586.515890435076,"y":28578.275802249987},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"dd2bbd8b-0c18-4cc2-b176-ad3b177a98a7","savedPayload":{"position":{"x":39597.01323868843,"y":28488.759535006342},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"c2f6e67c-b1b1-47d3-9600-cf18be037620","savedPayload":{"position":{"x":39616.82107043368,"y":28400.936525998834},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"074c15c8-317c-4358-963f-4186f245bb25","savedPayload":{"position":{"x":39645.70528040039,"y":28315.720626172813},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"c4a5297a-6f84-4337-8cd8-4ebde8fa1ae5","savedPayload":{"position":{"x":39683.34058679891,"y":28234.020419812852},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollaLine truncated
{"type":"Workspace","name":"Simple Workspace","rootNodes":[{"type":"Network","name":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","code":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34247\", \n\"webSocketsPort\": \"8080\"\n}","dataStorage":{"type":"Data Storage","name":"New Data Storage","sessionIndependentData":{"type":"Session Independent Data","name":"New Session Independent Data","exchangeDataProducts":[{"type":"Exchange Data Products","name":"Binance","singleMarketData":[{"type":"Single Market Data","name":"Binance BTC/USDT","dataProducts":[{"type":"Data Product","name":"Candles","id":"dd7c8795-bb24-4438-9d77-b37a9000ad7f","savedPayload":{"position":{"x":39655.39345377463,"y":28084.121285348076},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"98e6c917-f4b7-4b71-9215-de3ccaf3dc0c","savedPayload":{"position":{"x":39619.08803001109,"y":27971.34006849996},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"1d7dd537-5f6f-4eb2-8e56-ddba0aef720f","savedPayload":{"position":{"x":39594.11393185164,"y":27855.840877332517},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"221c8d22-8a7f-4014-a7ed-0bafc886b123","savedPayload":{"position":{"x":39580.59887306321,"y":27738.507421368748},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"6929ebe7-6486-4299-add5-ed0282500d91","savedPayload":{"position":{"x":39578.655464896365,"y":27620.63069875915},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"4a3ed8eb-a73e-4fb4-835a-681cf47d7373","savedPayload":{"position":{"x":39588.297198778826,"y":27502.946480135615},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"5ffc6a67-e33b-422b-8e36-f9711f93c723","savedPayload":{"position":{"x":39609.428887621216,"y":27386.77785099828},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"48f7953c-f62d-4292-b042-d1ac05f8f80f","savedPayload":{"position":{"x":39641.84608404141,"y":27273.24280350331},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"3b9a3c93-6d41-440e-89a2-2fd1b2dde603","savedPayload":{"position":{"x":39685.2360649887,"y":27163.433854438405},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed"Line truncated
+1 -1
View File
@@ -1 +1 @@
{"type":"Super Scripts","name":"Masters","code":"{ \n\"codeName\": \"Masters\"\n}","scriptsLibrary":{"type":"Scripts Library","name":"New Scripts Library","masterScripts":[{"type":"Master Script","name":"Setup Market","templateScripts":[{"type":"Template Script","name":"Install Single Market Data","templateStructure":{"type":"Template Structure","name":"New Template Structure","singleMarketData":{"type":"Single Market Data","name":"","dataProducts":[{"type":"Data Product","name":"Candles","id":"24c5f795-acf3-43c3-99ee-ddb521b082f4","savedPayload":{"position":{"x":39641.333811210454,"y":28936.98413868935},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"204ec959-0861-486a-aa2a-276dbf48f775","savedPayload":{"position":{"x":39612.59634583407,"y":28848.5110343909},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"90ee9733-ab7e-41a0-ac78-4a7661a27017","savedPayload":{"position":{"x":39594.15352768551,"y":28758.904338266497},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"9ade8e93-9071-422b-8cce-7a50d6dad739","savedPayload":{"position":{"x":39585.513470979095,"y":28668.600155855387},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"b5ef2cf7-3d35-45ec-848c-31970b59c04e","savedPayload":{"position":{"x":39586.515890435076,"y":28578.275802249987},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"1c3881ca-24b3-4cc9-b16b-9db7aa2b4c24","savedPayload":{"position":{"x":39597.01323868843,"y":28488.759535006342},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"92eec33b-86be-4b73-899f-e9550f7b0df3","savedPayload":{"position":{"x":39616.82107043368,"y":28400.936525998834},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"4b69df71-9091-4d22-9b0d-571304297033","savedPayload":{"position":{"x":39645.70528040039,"y":28315.720626172813},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"8a5f7e27-9ca5-4d6e-b6b9-2d6cbdda8040","savedPayload":{"position":{"x":39683.34058679891,"y":28234.020419812852},"targetPosition":{"x":40443.285556188,"y":28633.794640045882},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":Line truncated
{"type":"Super Scripts","name":"Masters","code":"{ \n\"codeName\": \"Masters\"\n}","scriptsLibrary":{"type":"Scripts Library","name":"New Scripts Library","masterScripts":[{"type":"Master Script","name":"Setup Market","templateScripts":[{"type":"Template Script","name":"Install Single Market Data","templateStructure":{"type":"Template Structure","name":"New Template Structure","singleMarketData":{"type":"Single Market Data","name":"","dataProducts":[{"type":"Data Product","name":"Candles","id":"24c5f795-acf3-43c3-99ee-ddb521b082f4","savedPayload":{"position":{"x":39655.39345377463,"y":28084.121285348076},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles","id":"fe6065ec-4d2d-47a0-bae0-8647f0e195d7"}}},{"type":"Data Product","name":"Volumes","id":"204ec959-0861-486a-aa2a-276dbf48f775","savedPayload":{"position":{"x":39619.08803001109,"y":27971.34006849996},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Volumes","id":"1a366aef-efef-470e-88e0-2a6fbe4dd91f"}}},{"type":"Data Product","name":"Candles Channels","id":"90ee9733-ab7e-41a0-ac78-4a7661a27017","savedPayload":{"position":{"x":39594.11393185164,"y":27855.840877332517},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels","id":"8c81d378-0947-4e9d-be25-58e69120152e"}}},{"type":"Data Product","name":"Candles Channels Objects","id":"9ade8e93-9071-422b-8cce-7a50d6dad739","savedPayload":{"position":{"x":39580.59887306321,"y":27738.507421368748},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Candles Channels Objects","id":"ba5dbb67-2030-42a0-9ff8-1ee80874f047"}}},{"type":"Data Product","name":"Bollinger Bands","id":"b5ef2cf7-3d35-45ec-848c-31970b59c04e","savedPayload":{"position":{"x":39578.655464896365,"y":27620.63069875915},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Bands","id":"29088877-8d63-4217-8d34-8742afc262f4"}}},{"type":"Data Product","name":"Percentage Bandwidth","id":"1c3881ca-24b3-4cc9-b16b-9db7aa2b4c24","savedPayload":{"position":{"x":39588.297198778826,"y":27502.946480135615},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Percentage Bandwidth","id":"3b7885a0-cd72-4a3f-a38b-3dc8c47bd068"}}},{"type":"Data Product","name":"Bollinger Channels Objects","id":"92eec33b-86be-4b73-899f-e9550f7b0df3","savedPayload":{"position":{"x":39609.428887621216,"y":27386.77785099828},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Channels Objects","id":"1f111f72-624f-4166-9f99-ab8d785d5267"}}},{"type":"Data Product","name":"Bollinger Sub-Channels Objects","id":"4b69df71-9091-4d22-9b0d-571304297033","savedPayload":{"position":{"x":39641.84608404141,"y":27273.24280350331},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Product Definition","name":"Bollinger Sub-Channels Objects","id":"080c87fc-4546-4482-ab11-180a33f6d9ab"}}},{"type":"Data Product","name":"Popular SMAs","id":"8a5f7e27-9ca5-4d6e-b6b9-2d6cbdda8040","savedPayload":{"position":{"x":39685.2360649887,"y":27163.433854438405},"targetPosition":{"x":40780.996436099675,"y":27659.843516028},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"PLine truncated
+17
View File
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\server.js"
}
]
}
+8 -1
View File
@@ -3,6 +3,7 @@
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const ONE_MIN_IN_MILISECONDS = 60 * 1000;
const MODULE_NAME = "Commons";
@@ -55,6 +56,11 @@
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return
}
if (outputDatasetNode.referenceParent.parentNode === undefined) {
logger.write(MODULE_NAME, "[ERROR] start -> Dataset not a child of a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent));
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return
}
if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) {
logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
@@ -284,7 +290,8 @@
let yesterday = {}
let system = { // These are the available system variables to be used in User Code and Formulas
timeFrame: timeFrame,
ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS
ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS,
ONE_MIN_IN_MILISECONDS: ONE_MIN_IN_MILISECONDS
}
let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas.
let results = []
+23 -23
View File
@@ -5,7 +5,7 @@
const ONE_YEAR_IN_MILISECONDS = 365 * 24 * 60 * 60 * 1000
let thisObject = {
initialize: initialize
initialize: initialize
}
return thisObject;
@@ -17,10 +17,10 @@
let telegramAPI
/* Initialize this info so that everything is logged propeerly */
bot.SESSION = {
name: bot.processNode.session.name,
id: bot.processNode.session.id
id: bot.processNode.session.id
}
/* Set the folderName for early logging */
@@ -29,7 +29,7 @@
} else {
bot.SESSION.folderName = bot.processNode.session.code.folderName + "-" + bot.SESSION.id
}
/* Check if there is a session */
if (bot.processNode.session === undefined) {
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> Cannot run without a Session.");
@@ -40,7 +40,7 @@
/* Listen to event to start or stop the session. */
bot.sessionKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id
global.SESSION_MAP.set(bot.sessionKey, bot.sessionKey)
global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Run Session', undefined, bot.sessionKey, undefined, runSession)
global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Stop Session', undefined, bot.sessionKey, undefined, stopSession)
@@ -133,7 +133,7 @@
}
bot.resumeExecution = false;
bot.hasTheBotJustStarted = true
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
return true
}
@@ -148,7 +148,7 @@
bot.startMode = "Live"
checkDatetimes()
bot.resumeExecution = false;
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.hasTheBotJustStarted = true
pProcessConfig.liveWaitTime = getTimeFrameFromLabel(bot.VALUES_TO_USE.timeFrame)
return true
@@ -165,23 +165,23 @@
bot.startMode = "Live"
checkDatetimes()
bot.resumeExecution = false;
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.hasTheBotJustStarted = true
/* Reduce the balance */
let balancePercentage = 1 // This is the default value
if (bot.SESSION.code.balancePercentage !== undefined) {
balancePercentage = bot.SESSION.code.balancePercentage
}
bot.VALUES_TO_USE.initialBalanceA = bot.VALUES_TO_USE.initialBalanceA * balancePercentage / 100
bot.VALUES_TO_USE.initialBalanceB = bot.VALUES_TO_USE.initialBalanceB * balancePercentage / 100
bot.VALUES_TO_USE.minimumBalanceA = bot.VALUES_TO_USE.minimumBalanceA * balancePercentage / 100
bot.VALUES_TO_USE.minimumBalanceB = bot.VALUES_TO_USE.minimumBalanceB * balancePercentage / 100
bot.VALUES_TO_USE.maximumBalanceA = bot.VALUES_TO_USE.maximumBalanceA * balancePercentage / 100
bot.VALUES_TO_USE.maximumBalanceB = bot.VALUES_TO_USE.maximumBalanceB * balancePercentage / 100
@@ -191,12 +191,12 @@
}
function startPaperTrading(message) {
bot.startMode = "Backtest"
bot.startMode = "Paper"
checkDatetimes()
bot.resumeExecution = false;
bot.hasTheBotJustStarted = true
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
pProcessConfig.normalWaitTime = getTimeFrameFromLabel(bot.VALUES_TO_USE.timeFrame)
return true
}
@@ -270,7 +270,7 @@
}
}
let tradingSystem = bot.TRADING_SYSTEM
let tradingSystem = bot.TRADING_SYSTEM
if (tradingSystem !== undefined) {
@@ -429,7 +429,7 @@
let socialBot = bot.SESSION.socialBots.bots[i]
if (socialBot.type === "Telegram Bot") {
let code = socialBot.code
socialBot.botInstance = initializeTelegramBot(code.botToken, code.chatId)
socialBot.botInstance = initializeTelegramBot(code.botToken, code.chatId)
}
}
}
@@ -440,7 +440,7 @@
let socialBot = bot.SESSION.socialBots.bots[i]
try {
let code = announcement.code
if (socialBot.type === "Telegram Bot") {
if (announcement.formulaValue !== undefined) {
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err))
@@ -448,7 +448,7 @@
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err))
}
}
} catch (err) {
parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> err = " + err.stack);
}
@@ -461,7 +461,7 @@
}
function finalizeSocialBots() {
if (bot.SESSION.socialBots === undefined) {return}
if (bot.SESSION.socialBots === undefined) { return }
if (bot.SESSION.socialBots.bots !== undefined) {
for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) {
let socialBot = bot.SESSION.socialBots.bots[i]
@@ -488,8 +488,8 @@
telegramAPI = new Telegram(botToken)
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was started with an initial balance of " + " " + bot.VALUES_TO_USE.initialBalanceA + " " + bot.VALUES_TO_USE.baseAsset + "."
telegramAPI.sendMessage(chatId, messge ).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> Telegram API error -> err = " + err))
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was started with an initial balance of " + " " + bot.VALUES_TO_USE.initialBalanceA + " " + bot.VALUES_TO_USE.baseAsset + "."
telegramAPI.sendMessage(chatId, messge).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> Telegram API error -> err = " + err))
let botInstance = {
telegramBot: telegramBot,
@@ -504,10 +504,10 @@
}
function finalizeTelegramBot(chatId) {
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was stopped."
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was stopped."
telegramAPI.sendMessage(chatId, messge).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> Telegram API error -> err = " + err))
}
} catch (err) {
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+147 -87
View File
@@ -121,6 +121,7 @@
const CONDITIONS_FOLDER_NAME = "Simulation-Conditions";
const STRATEGIES_FOLDER_NAME = "Simulation-Strategies";
const TRADES_FOLDER_NAME = "Simulation-Trades";
const SNAPSHOTS_FOLDER_NAME = "Snapshots";
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
@@ -129,6 +130,10 @@
let strategiesArray
let tradesArray
let snapshotHeaders
let triggerOnSnapshot
let takePositionSnapshot
let tradingSystem = {};
tradingSimulation.runSimulation(
@@ -142,7 +147,7 @@
writeFiles,
callBackFunction)
function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray) {
function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pSnapshotHeaders, pTriggerOnSnapshot, pTakePositionSnapshot) {
tradingSystem = pTradingSystem
recordsArray = pRecordsArray
@@ -150,11 +155,16 @@
strategiesArray = pStrategiesArray
tradesArray = pTradesArray
snapshotHeaders = pSnapshotHeaders
triggerOnSnapshot = pTriggerOnSnapshot
takePositionSnapshot = pTakePositionSnapshot
if (timeFrame > global.dailyFilePeriods[0][0]) {
writeMarketFiles()
} else {
writeDailyFiles()
}
}
function writeMarketFiles() {
@@ -367,7 +377,8 @@
record.number + "," +
record.beginRate + "," +
record.endRate + "," +
'"' + record.triggerOnSituation + '"' + "]";
'"' + record.triggerOnSituation + '"' + "," +
'"' + record.name + '"' + "]";
if (separator === "") { separator = ","; }
@@ -474,7 +485,7 @@
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
writeSnapshotFiles();
}
catch (err) {
@@ -489,97 +500,52 @@
}
}
}
function writeSnapshotFiles() {
function writeDailyFiles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); }
writeSnapshotFile(triggerOnSnapshot, 'Trigger-On', onFinish)
writeRecordsFile();
function onFinish() {
writeSnapshotFile(takePositionSnapshot, 'Take-Position', callBackFunction)
}
}
/* TODO : NEXT function is a work in progress, not used today */
function writeFinalResults() {
function writeSnapshotFile(snapshotArray, pFileName, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeSnapshotFile -> Entering function."); }
if ( // We only write this file if we are at the head of the market.
Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS
!==
Math.trunc((new Date()).valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS
) {
writeRecordsFile();
return;
}
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
let separator = "\r\n";
for (let i = 0; i < recordsArray.length; i++) {
let record = recordsArray[i];
/* Will only add to the file the records of the current day */
if (record.begin < currentDay.valueOf()) { continue; }
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.type + "," +
record.marketRate + "," +
record.amount + "," +
record.balanceA + "," +
record.balanceB + "," +
record.profit + "," +
record.lastTradeProfitLoss + "," +
record.stopLoss + "," +
record.roundtrips + "," +
record.hits + "," +
record.fails + "," +
record.hitRatio + "," +
record.ROI + "," +
record.periods + "," +
record.days + "," +
record.anualizedRateOfReturn + "," +
record.positionRate + "," +
record.lastTradeROI + "," +
record.strategy + "," +
record.strategyStageNumber + "," +
record.takeProfit + "," +
record.stopLossPhase + "," +
record.takeProfitPhase + "," +
JSON.stringify(record.executionRecord) + "," +
record.positionSize + "," +
record.initialBalanceA + "," +
record.minimumBalanceA + "," +
record.maximumBalanceA + "," +
record.initialBalanceB + "," +
record.minimumBalanceB + "," +
record.maximumBalanceB + "," +
record.baseAsset + "," +
record.quotedAsset + "," +
record.marketBaseAsset + "," +
record.marketQuotedAsset + "," +
record.positionPeriods + "," +
record.positionDays + "," +
record.distanceToLastTriggerOn + "," +
record.distanceToLastTriggerOff + "," +
record.distanceToLastTakePosition + "," +
record.distanceToLastClosePosition + "]";
if (separator === "") { separator = ","; }
parseRecord(snapshotHeaders)
for (let i = 0; i < snapshotArray.length; i++) {
let record = snapshotArray[i];
parseRecord(record)
fileRecordCounter++;
}
function parseRecord(record) {
for (let j = 0; j < record.length; j++) {
let property = record[j]
fileContent = fileContent + '' + property
if (j !== record.length - 1) {
fileContent = fileContent + ","
}
}
fileContent = fileContent + separator
}
fileContent = "[" + fileContent + "]";
fileContent = "" + fileContent + "";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel;
let fileName = pFileName + '.csv';
let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SNAPSHOTS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrameLabel;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
@@ -588,35 +554,42 @@
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> fileContent = " + fileContent); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
}
writeRecordsFile();
callBack(global.DEFAULT_OK_RESPONSE);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeSnapshotFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeSnapshotFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function writeDailyFiles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); }
writeRecordsFile();
function writeRecordsFile() {
try {
@@ -841,7 +814,8 @@
record.number + "," +
record.beginRate + "," +
record.endRate + "," +
'"' + record.triggerOnSituation + '"' + "]";
'"' + record.triggerOnSituation + '"' + "," +
'"' + record.name + '"' + "]";
if (separator === "") { separator = ","; }
@@ -955,7 +929,7 @@
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
writeSnapshotFiles()
}
catch (err) {
@@ -970,6 +944,92 @@
}
}
function writeSnapshotFiles() {
writeSnapshotFile(triggerOnSnapshot, 'Trigger-On', onFinish)
function onFinish() {
writeSnapshotFile(takePositionSnapshot, 'Take-Position', callBackFunction)
}
}
function writeSnapshotFile(snapshotArray, pFileName, callBack) {
if (bot.startMode !== 'Backtest') {
callBack(global.DEFAULT_OK_RESPONSE);
return;
}
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeSnapshotFile -> Entering function."); }
let fileRecordCounter = 0;
let fileContent = "";
let separator = "\r\n";
parseRecord(snapshotHeaders)
for (let i = 0; i < snapshotArray.length; i++) {
let record = snapshotArray[i];
parseRecord(record)
fileRecordCounter++;
}
function parseRecord(record) {
for (let j = 0; j < record.length; j++) {
let property = record[j]
fileContent = fileContent + '' + property
if (j !== record.length - 1) {
fileContent = fileContent + ","
}
}
fileContent = fileContent + separator
}
fileContent = "" + fileContent + "";
let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
let fileName = pFileName + '.csv';
let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SNAPSHOTS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timeFrameLabel + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
}
callBack(global.DEFAULT_OK_RESPONSE);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeSnapshotFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeSnapshotFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
}
catch (err) {
+251 -29
View File
@@ -56,13 +56,13 @@
/* Stop Loss Management */
const MIN_STOP_LOSS_VALUE = 1 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result.
const MIN_STOP_LOSS_VALUE = 0.0000000001 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result.
const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER
let stopLoss = 0;
/* Take Profit Management */
const MIN_TAKE_PROFIT_VALUE = 1 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result.
const MIN_TAKE_PROFIT_VALUE = 0.0000000001 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result.
const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER
let takeProfit = 0;
@@ -103,6 +103,15 @@
interExecutionMemory.
*/
/* Snapshots of Trigger On and Take Positions */
let snapshots = {
headers: undefined,
triggerOn: [],
takePosition: [],
lastTriggerOn: undefined,
lastTakePosition: undefined
}
/*Needed for statistics */
let previousBalanceBaseAsset = 0;
let previousBalanceQuotedAsset = 0;
@@ -130,7 +139,8 @@
number: 0,
beginRate: 0,
endRate: 0,
triggerOnSituation: ''
triggerOnSituation: '',
name: ''
}
let currentTrade = {
@@ -181,6 +191,15 @@
/* Initialization */
yesterday.snapshots = {
headers: undefined,
triggerOn: [],
takePosition: [],
lastTriggerOn: undefined,
lastTakePosition: undefined
}
yesterday.stopLoss = 0
yesterday.takeProfit = 0
@@ -206,7 +225,8 @@
number: 0,
beginRate: 0,
endRate: 0,
triggerOnSituation: ''
triggerOnSituation: '',
name: ''
}
yesterday.currentTrade = {
@@ -251,6 +271,14 @@
/* Initialize the data structure we will use inter execution. */
interExecutionMemory.snapshots = {
headers: undefined,
triggerOn: [],
takePosition: [],
lastTriggerOn: undefined,
lastTakePosition: undefined
}
interExecutionMemory.stopLoss = 0
interExecutionMemory.takeProfit = 0
@@ -276,7 +304,8 @@
number: 0,
beginRate: 0,
endRate: 0,
triggerOnSituation: ''
triggerOnSituation: '',
name: ''
}
interExecutionMemory.currentTrade = {
@@ -323,6 +352,14 @@
/* We get the initial values from the day previous to the candles we receive at the current execution */
snapshots = {
headers: interExecutionMemory.snapshots.headers,
triggerOn: interExecutionMemory.snapshots.triggerOn,
takePosition: interExecutionMemory.snapshots.takePosition,
lastTriggerOn: interExecutionMemory.snapshots.lastTriggerOn,
lastTakePosition: interExecutionMemory.snapshots.lastTakePosition
}
stopLoss = interExecutionMemory.stopLoss
takeProfit = interExecutionMemory.takeProfit
@@ -348,7 +385,8 @@
number: interExecutionMemory.currentStrategy.number,
beginRate: interExecutionMemory.currentStrategy.beginRate,
endRate: interExecutionMemory.currentStrategy.endRate,
triggerOnSituation: interExecutionMemory.currentStrategy.triggerOnSituation
triggerOnSituation: interExecutionMemory.currentStrategy.triggerOnSituation,
name: currentStrategy.name
}
currentTrade = {
@@ -398,6 +436,14 @@
/* For the case that any of these variables are not updated during the main loop, we need to store their value at the yesterday structure, otherwise it would be lost. */
yesterday.snapshots = {
headers: snapshots.headers,
triggerOn: snapshots.triggerOn,
takePosition: snapshots.takePosition,
lastTriggerOn: snapshots.lastTriggerOn,
lastTakePosition: snapshots.lastTakePosition
}
yesterday.stopLoss = stopLoss
yesterday.takeProfit = takeProfit
@@ -423,7 +469,8 @@
number: currentStrategy.number,
beginRate: currentStrategy.beginRate,
endRate: currentStrategy.endRate,
triggerOnSituation: currentStrategy.triggerOnSituation
triggerOnSituation: currentStrategy.triggerOnSituation,
name: currentStrategy.name
}
yesterday.currentTrade = {
@@ -500,6 +547,15 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + currentCandleIndex); }
/* We are going to take snapshots of the values of indicators at key moments of the sumulation. */
let snapshotKeys = new Map()
snapshotLoopHeaders = []
snapshotDataRecord = []
saveAsLastTriggerOnSnapshot = false
saveAsLastTakePositionSnapshot = false
addToSnapshots = false
let announcementsToBeMade = []
let candle = candles[currentCandleIndex];
@@ -767,7 +823,9 @@
if (initialDefinition.positionSize !== undefined) {
if (initialDefinition.positionSize.formula !== undefined) {
try {
positionSize = eval(initialDefinition.positionSize.formula.code);
let code = initialDefinition.positionSize.formula.code
positionSize = eval(code);
addCodeToSnapshot(code)
} catch (err) {
initialDefinition.positionSize.formula.error = err.message
}
@@ -794,7 +852,9 @@
if (initialDefinition.positionRate !== undefined) {
if (initialDefinition.positionRate.formula !== undefined) {
try {
let code = initialDefinition.positionRate.formula.code
positionRate = eval(initialDefinition.positionRate.formula.code);
addCodeToSnapshot(code)
} catch (err) {
initialDefinition.positionRate.formula.error = err.message
}
@@ -821,7 +881,9 @@
if (phase.formula !== undefined) {
try {
formulaValue = eval(phase.formula.code);
let code = phase.formula.code
formulaValue = eval(code);
addCodeToSnapshot(code)
if (formulaValue === Infinity) {
formulaError = "Formula evaluates to Infinity."
formulaValue = MAX_STOP_LOSS_VALUE
@@ -910,7 +972,9 @@
if (phase.formula !== undefined) {
try {
formulaValue = eval(phase.formula.code);
let code = phase.formula.code
formulaValue = eval(code);
addCodeToSnapshot(code)
if (formulaValue === Infinity) {
formulaValue = MAX_TAKE_PROFIT_VALUE
if (takeProfitStage === 'Open Stage') {
@@ -1007,7 +1071,9 @@
if (phase.formula !== undefined) {
try {
formulaValue = eval(phase.formula.code);
let code = phase.formula.code
formulaValue = eval(code);
addCodeToSnapshot(code)
if (formulaValue === Infinity) {
formulaError = ""
formulaValue = MAX_STOP_LOSS_VALUE
@@ -1096,7 +1162,9 @@
if (phase.formula !== undefined) {
try {
formulaValue = eval(phase.formula.code);
let code = phase.formula.code
formulaValue = eval(code);
addCodeToSnapshot(code)
if (formulaValue === Infinity) {
formulaError = "Formula evaluates to Infinity."
formulaValue = MAX_TAKE_PROFIT_VALUE
@@ -1181,7 +1249,9 @@
let value
try {
value = eval(node.code);
let code = node.code
value = eval(code);
addCodeToSnapshot(code)
} catch (err) {
/*
One possible error is that the conditions references a .previous that is undefined. For this
@@ -1262,6 +1332,11 @@
for (let j = 0; j < tradingSystem.strategies.length; j++) {
if (
strategyStage !== 'No Stage' ||
currentStrategyIndex !== -1
) {continue}
let strategy = tradingSystem.strategies[j];
let triggerStage = strategy.triggerStage
@@ -1273,7 +1348,10 @@
for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) {
let situation = triggerStage.triggerOn.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1298,6 +1376,7 @@
currentStrategy.beginRate = candle.min;
currentStrategy.endRate = candle.min; // In case the strategy does not get exited
currentStrategy.triggerOnSituation = situation.name
currentStrategy.name = strategy.name
if (processingDailyFiles) {
if (positionedAtYesterday) {
@@ -1307,6 +1386,7 @@
yesterday.currentStrategy.beginRate = currentStrategy.beginRate;
yesterday.currentStrategy.endRate = currentStrategy.endRate;
yesterday.currentStrategy.triggerOnSituation = currentStrategy.triggerOnSituation;
yesterday.currentStrategy.name = currentStrategy.name;
}
}
@@ -1319,6 +1399,7 @@
}
checkAnnouncements(triggerStage.triggerOn)
saveAsLastTriggerOnSnapshot = true
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to Trigger Stage because conditions at Trigger On Event were met."); }
break;
@@ -1344,7 +1425,10 @@
for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) {
let situation = triggerStage.triggerOff.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1411,7 +1495,10 @@
for (let k = 0; k < triggerStage.takePosition.situations.length; k++) {
let situation = triggerStage.takePosition.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1452,6 +1539,7 @@
currentTrade.takePositionSituation = situation.name
checkAnnouncements(triggerStage.takePosition)
saveAsLastTakePositionSnapshot = true
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Conditions at the Take Position Event were met."); }
break;
@@ -1513,7 +1601,10 @@
for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
let situation = nextPhaseEvent.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1559,7 +1650,10 @@
for (let k = 0; k < moveToPhaseEvent.situations.length; k++) {
let situation = moveToPhaseEvent.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1707,7 +1801,10 @@
for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
let situation = nextPhaseEvent.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1753,7 +1850,10 @@
for (let k = 0; k < moveToPhaseEvent.situations.length; k++) {
let situation = moveToPhaseEvent.situations[k];
let passed = true;
let passed
if (situation.conditions.length > 0) {
passed = true
}
for (let m = 0; m < situation.conditions.length; m++) {
@@ -1999,6 +2099,7 @@
currentTrade.endRate = closeRate;
closePositionNow = true;
addToSnapshots = true
if (processingDailyFiles) {
if (positionedAtYesterday) {
@@ -2071,6 +2172,7 @@
currentTrade.endRate = closeRate;
closePositionNow = true;
addToSnapshots = true
if (processingDailyFiles) {
if (positionedAtYesterday) {
@@ -2186,6 +2288,7 @@
}
takePositionAtSimulation()
return
function putOpeningOrder() {
@@ -2243,7 +2346,7 @@
if (baseAsset === bot.market.baseAsset) {
orderSide = "sell"
orderPrice = tradePositionRate - 100 // This is going to be ingnored at the Exchange API for now since we only put market orders.
orderPrice = tradePositionRate
amountA = tradePositionSize * orderPrice
amountB = tradePositionSize
@@ -2251,7 +2354,7 @@
} else {
orderSide = "buy"
orderPrice = tradePositionRate // This is going to be ingnored at the Exchange API for now since we only put market orders.
orderPrice = tradePositionRate
amountA = tradePositionSize
amountB = tradePositionSize / orderPrice
@@ -2845,7 +2948,8 @@
number: 0,
beginRate: 0,
endRate: 0,
triggerOnSituation: ''
triggerOnSituation: '',
name: ''
}
if (processingDailyFiles) {
@@ -2857,7 +2961,8 @@
number: 0,
beginRate: 0,
endRate: 0,
triggerOnSituation: ''
triggerOnSituation: '',
name: ''
}
}
}
@@ -2880,6 +2985,57 @@
}
}
/* Snapshots Management (before we generate the trade record and delete that info) */
if (saveAsLastTriggerOnSnapshot === true) {
snapshots.lastTriggerOn = snapshotDataRecord
saveAsLastTriggerOnSnapshot = false
}
if (saveAsLastTakePositionSnapshot === true) {
snapshots.lastTakePosition = snapshotDataRecord
saveAsLastTakePositionSnapshot = false
}
if (addToSnapshots === true) {
let closeValues = [
roundtrips, // Trade Number
(new Date(candle.begin)).toISOString(), // Datetime
tradingSystem.strategies[currentStrategyIndex].name, // Strategy
currentStrategy.triggerOnSituation, // Trigger On Situation
currentTrade.takePositionSituation, // Take Position Situation
hitOrFial(), // Result
lastTradeROI, // ROI
simulationRecord.type.replace(/"/g, '') // Exit Type
]
function hitOrFial() {
if (lastTradeROI > 0) { return 'HIT' } else { return 'FAIL' }
}
if (positionedAtYesterday === false) {
snapshots.triggerOn.push(closeValues.concat(snapshots.lastTriggerOn))
snapshots.takePosition.push(closeValues.concat(snapshots.lastTakePosition))
}
snapshots.lastTriggerOn = undefined
snapshots.lastTakePosition = undefined
addToSnapshots = false
}
let closeHeaders = ['Trade Number', 'Close Datetime', 'Strategy', 'Trigger On Situation', 'Take Position Situation', 'Result', 'ROI', 'Exit Type']
if (snapshots.headers === undefined) {
snapshots.headers = closeHeaders.concat(JSON.parse(JSON.stringify(snapshotLoopHeaders)))
}
if (processingDailyFiles) {
if (positionedAtYesterday) {
yesterday.snapshots.headers = snapshots.headers;
yesterday.snapshots.triggerOn = snapshots.triggerOn;
yesterday.snapshots.takePosition = snapshots.takePosition;
yesterday.snapshots.lastTriggerOn = snapshots.lastTriggerOn;
yesterday.snapshots.lastTakePosition = snapshots.lastTakePosition;
}
}
/* Prepare the information for the Trades File */
if (currentTrade.begin !== 0 && currentTrade.end !== 0) {
@@ -2914,7 +3070,8 @@
}
}
makeAnnoucements() // After everything at the simulation level was done, we will do the annoucements that are pending.
/* After everything at the simulation level was done, we will do the annoucements that are pending.*/
makeAnnoucements()
}
function checkAnnouncements(node, value) {
@@ -3005,10 +3162,65 @@
}
}
}
function addCodeToSnapshot(code) {
if (code === undefined) { return }
try {
let instructionsArray = code.split(' ')
for (let i = 0; i < instructionsArray.length; i++) {
let instruction = instructionsArray[i]
instruction = instruction.replace('(', '')
instruction = instruction.replace(')', '')
instruction = instruction.replace(/</g, '')
instruction = instruction.replace(/>/g, '')
instruction = instruction.replace(/<=/g, '')
instruction = instruction.replace(/>=/g, '')
instruction = instruction.replace(/!=/g, '')
instruction = instruction.replace(/!==/g, '')
instruction = instruction.replace(/==/g, '')
instruction = instruction.replace(/===/g, '')
instruction = instruction.replace(/{/g, '')
instruction = instruction.replace(/}/g, '')
if (instruction.indexOf('chart') >= 0) {
let parts = instruction.split('.')
let timeFrame = parts[1]
let product = parts[2]
let property
checkPrevious(3)
function checkPrevious(index) {
property = parts[index]
if (property === 'previous') {
product = product + '.previous'
checkPrevious(index + 1)
}
}
// Example: chart.at01hs.popularSMA.sma200 - chart.at01hs.popularSMA.sma100 < 10
if (timeFrame !== 'atAnyTimeFrame') {
timeFrame = timeFrame.substring(2, 4) + '-' + timeFrame.substring(4, 7)
}
let key = timeFrame + '-' + product + '-' + property
let existingKey = snapshotKeys.get(key)
if (existingKey === undefined) {// means that at the current loop this property of this product was not used before.
snapshotKeys.set(key, key)
snapshotLoopHeaders.push(key)
let value = eval(instruction)
snapshotDataRecord.push(value)
}
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] runSimulation -> addCodeToSnapshot -> code = " + code);
logger.write(MODULE_NAME, "[ERROR] runSimulation -> addCodeToSnapshot -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> Entering function."); }
@@ -3047,6 +3259,14 @@
if (lastCandle.end === lastInstantOfTheDay) {
interExecutionMemory.snapshots = {
headers: yesterday.snapshots.headers,
triggerOn: yesterday.snapshots.triggerOn,
takePosition: yesterday.snapshots.takePosition,
lastTriggerOn: yesterday.snapshots.lastTriggerOn,
lastTakePosition: yesterday.snapshots.lastTakePosition
}
interExecutionMemory.stopLoss = yesterday.stopLoss
interExecutionMemory.takeProfit = yesterday.takeProfit
@@ -3072,7 +3292,8 @@
number: yesterday.currentStrategy.number,
beginRate: yesterday.currentStrategy.beginRate,
endRate: yesterday.currentStrategy.endRate,
triggerOnSituation: yesterday.currentStrategy.triggerOnSituation
triggerOnSituation: yesterday.currentStrategy.triggerOnSituation,
name: yesterday.currentStrategy.name
}
interExecutionMemory.currentTrade = {
@@ -3114,7 +3335,7 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); }
callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray);
callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray, snapshots.headers, snapshots.triggerOn, snapshots.takePosition);
}
function getElement(pArray, currentCandle, datasetName) {
@@ -3156,6 +3377,7 @@
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] runSimulation -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+23 -1
View File
@@ -333,9 +333,21 @@ function exportKeywordsToPackageJson (exchanges) {
// ----------------------------------------------------------------------------
function exportEverything () {
function flatten (nested, result = []) {
for (const key in nested) {
result.push (key)
if (Object.keys (nested[key]).length)
flatten (nested[key], result)
}
return result
}
function exportEverything () {
const ids = getIncludedExchangeIds ()
const errorHierarchy = require ('../js/base/errorHierarchy.js')
const flat = flatten (errorHierarchy)
flat.push ('error_hierarchy')
const replacements = [
{
@@ -353,6 +365,16 @@ function exportEverything () {
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/__init__.py',
regex: /(?:from ccxt\.base\.errors import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]/,
replacement: flat.map (error => ('from ccxt.base.errors' + ' import ' + error).padEnd (60) + '# noqa: F401').join ("\n") + "\n\n",
},
{
file: './python/ccxt/async_support/__init__.py',
regex: /(?:from ccxt\.base\.errors import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]/,
replacement: flat.map (error => ('from ccxt.base.errors' + ' import ' + error).padEnd (60) + '# noqa: F401').join ("\n") + "\n\n",
},
{
file: './python/ccxt/async_support/__init__.py',
regex: /(?:from ccxt\.async_support\.[^\.]+ import [^\s]+\s+\# noqa\: F401[\r]?[\n])+[\r]?[\n]exchanges/,
+2
View File
@@ -199,6 +199,7 @@ class Transpiler {
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
[ /Object\.keys\s*\((.*)\)/g, 'list($1.keys())' ],
[ /Object\.values\s*\((.*)\)/g, 'list($1.values())' ],
[ /\[([^\]]+)\]\.join\s*\(([^\)]+)\)/g, "$2.join([$1])" ],
[ /hash \(([^,]+)\, \'(sha[0-9])\'/g, "hash($1, '$2'" ],
[ /hmac \(([^,]+)\, ([^,]+)\, \'(md5)\'/g, 'hmac($1, $2, hashlib.$3' ],
@@ -330,6 +331,7 @@ class Transpiler {
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
[ /Object\.keys\s*\((.*)\)/g, 'is_array($1) ? array_keys($1) : array()' ],
[ /Object\.values\s*\((.*)\)/g, 'is_array($1) ? array_values($1) : array()' ],
[ /([^\s]+\s*\(\))\.toString \(\)/g, '(string) $1' ],
[ /([^\s]+)\.toString \(\)/g, '(string) $1' ],
[ /throw new Error \((.*)\)/g, 'throw new \\Exception($1)' ],
+2 -3
View File
@@ -35,7 +35,7 @@ const Exchange = require ('./js/base/Exchange')
//-----------------------------------------------------------------------------
// this is updated by vss.js when building
const version = '1.25.80'
const version = '1.27.45'
Exchange.ccxtVersion = version
@@ -44,7 +44,6 @@ 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'),
'aofex': require ('./js/aofex.js'),
'bcex': require ('./js/bcex.js'),
@@ -115,8 +114,8 @@ const exchanges = {
'fybse': require ('./js/fybse.js'),
'gateio': require ('./js/gateio.js'),
'gemini': require ('./js/gemini.js'),
'hbtc': require ('./js/hbtc.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'),
+4395 -2136
View File
File diff suppressed because it is too large. Load diff
-1255
View File
File diff suppressed because it is too large. Load diff
+9 -3
View File
@@ -30,6 +30,7 @@ module.exports = class aofex extends Exchange {
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchClosedOrder': true,
'fetchOrderTrades': true,
'fetchTradingFee': true,
},
'timeframes': {
@@ -559,7 +560,7 @@ module.exports = class aofex extends Exchange {
//
const id = this.safeString (trade, 'id');
const ctime = this.parse8601 (this.safeString (trade, 'ctime'));
const timestamp = this.safeTimestamp (trade, 'ts', ctime);
const timestamp = this.safeTimestamp (trade, 'ts', ctime) - 28800000; // 8 hours, adjust to UTC;
let symbol = undefined;
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
@@ -725,7 +726,7 @@ module.exports = class aofex extends Exchange {
base = market['base'];
quote = market['quote'];
}
const timestamp = this.parse8601 (this.safeString (order, 'ctime'));
const timestamp = this.parse8601 (this.safeString (order, 'ctime')) - 28800000; // 8 hours, adjust to UTC
const orderType = this.safeString (order, 'type');
const type = (orderType === '2') ? 'limit' : 'market';
const side = this.safeString (order, 'side');
@@ -883,11 +884,16 @@ module.exports = class aofex extends Exchange {
return this.parseOrder (order);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
const response = await this.fetchClosedOrder (id, symbol, params);
return this.safeValue (response, 'trades', []);
}
async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
// 'from': 'BM7442641584965237751ZMAKJ5', // query start order_sn
// 'direct': 'prev', // next
'direct': 'prev', // next
};
let market = undefined;
if (symbol !== undefined) {
+43 -15
View File
@@ -288,8 +288,9 @@ module.exports = class Exchange {
const config = deepExtend (this.describe (), userConfig)
// merge to this
for (const [property, value] of Object.entries (config))
for (const [property, value] of Object.entries (config)) {
this[property] = deepExtend (this[property], value)
}
if (!this.httpAgent) {
this.httpAgent = defaultFetch.http ? new defaultFetch.http.Agent ({ 'keepAlive': true }) : undefined
@@ -304,13 +305,15 @@ module.exports = class Exchange {
this['has' + capitalize (k)] = !!this.has[k] // converts 'emulated' to true
}
if (this.api)
if (this.api) {
this.defineRestApi (this.api, 'request')
}
this.initRestRateLimiter ()
if (this.markets)
if (this.markets) {
this.setMarkets (this.markets)
}
}
defaults () {
@@ -342,20 +345,23 @@ module.exports = class Exchange {
checkAddress (address) {
if (address === undefined)
if (address === undefined) {
throw new InvalidAddress (this.id + ' address is undefined')
}
// check the address is not the same letter like 'aaaaa' nor too short nor has a space
if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' '))
if ((unique (address).length === 1) || address.length < this.minFundingAddressLength || address.includes (' ')) {
throw new InvalidAddress (this.id + ' address is invalid or has less than ' + this.minFundingAddressLength.toString () + ' characters: "' + this.json (address) + '"')
}
return address
}
initRestRateLimiter () {
if (this.rateLimit === undefined)
if (this.rateLimit === undefined) {
throw new Error (this.id + '.rateLimit property is not configured')
}
this.tokenBucket = this.extend ({
delay: 1,
@@ -386,15 +392,17 @@ module.exports = class Exchange {
const promise =
fetchImplementation (url, this.extend (params, this.fetchOptions))
.catch ((e) => {
if (isNode)
if (isNode) {
throw new ExchangeNotAvailable ([ this.id, method, url, e.type, e.message ].join (' '))
}
throw e // rethrow all unknown errors
})
.then (response => this.handleRestResponse (response, url, method, headers, body))
.then ((response) => this.handleRestResponse (response, url, method, headers, body))
return timeout (this.timeout, promise).catch ((e) => {
if (e instanceof TimedOut)
if (e instanceof TimedOut) {
throw new RequestTimeout (this.id + ' ' + method + ' ' + url + ' request timed out (' + this.timeout + ' ms)')
}
throw e
})
}
@@ -442,16 +450,20 @@ module.exports = class Exchange {
let underscore = type + '_' + lowercaseMethod + '_' + underscoreSuffix
if ('suffixes' in options) {
if ('camelcase' in options['suffixes'])
if ('camelcase' in options['suffixes']) {
camelcase += options['suffixes']['camelcase']
if ('underscore' in options.suffixes)
}
if ('underscore' in options.suffixes) {
underscore += options['suffixes']['underscore']
}
}
if ('underscore_suffix' in options)
if ('underscore_suffix' in options) {
underscore += options.underscoreSuffix;
if ('camelcase_suffix' in options)
}
if ('camelcase_suffix' in options) {
camelcase += options.camelcaseSuffix;
}
let partial = async params => this[methodName] (path, type, uppercaseMethod, params || {})
@@ -594,7 +606,7 @@ module.exports = class Exchange {
getResponseHeaders (response) {
const result = {}
response.headers.forEach ((value, key) => {
key = key.split ('-').map (word => capitalize (word)).join ('-')
key = key.split ('-').map ((word) => capitalize (word)).join ('-')
result[key] = value
})
return result
@@ -632,7 +644,7 @@ module.exports = class Exchange {
}
setMarkets (markets, currencies = undefined) {
const values = Object.values (markets).map (market => deepExtend ({
const values = Object.values (markets).map ((market) => deepExtend ({
'limits': this.limits,
'precision': this.precision,
}, this.fees['trading'], market))
@@ -803,6 +815,22 @@ module.exports = class Exchange {
throw new NotSupported (this.id + ' fetchOrder not supported yet');
}
fetchUnifiedOrder (order, params = {}) {
return this.fetchOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params);
}
createOrder (symbol, type, side, amount, price = undefined, params = {}) {
throw new NotSupported (this.id + ' createOrder not supported yet');
}
cancelOrder (id, symbol = undefined, params = {}) {
throw new NotSupported (this.id + ' cancelOrder not supported yet');
}
cancelUnifiedOrder (order, params = {}) {
return this.cancelOrder (this.safeValue (order, 'id'), this.safeValue (order, 'symbol'), params);
}
fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
throw new NotSupported (this.id + ' fetchOrders not supported yet');
}
+11 -7
View File
@@ -45,23 +45,27 @@ function numberToString (x) { // avoids scientific notation for too large and to
if (typeof x === 'string') return x
const s = x.toString ()
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)
return x
}
} 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')
const parts = s.split ('e')
if (parts[1]) {
let e = parseInt (parts[1])
const m = parts[0].split ('.')
if (m[1]) {
e -= m[1].length
}
return m[0] + m[1] + (new Array (e + 1)).join ('0')
}
}
return x.toString ()
return s;
}
//-----------------------------------------------------------------------------
+106 -34
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied, NotSupported } = require ('./base/errors');
const { ROUND, TRUNCATE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -37,6 +37,7 @@ module.exports = class binance extends Exchange {
'fetchTransactions': false,
'fetchTradingFee': true,
'fetchTradingFees': true,
'cancelAllOrders': true,
},
'timeframes': {
'1m': '1m',
@@ -57,6 +58,10 @@ module.exports = class binance extends Exchange {
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg',
'test': {
'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1',
'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1',
},
'api': {
'web': 'https://www.binance.com',
'wapi': 'https://api.binance.com/wapi/v3',
@@ -130,6 +135,8 @@ module.exports = class binance extends Exchange {
'lending/union/purchaseRecord',
'lending/union/redemptionRecord',
'lending/union/interestHistory',
'lending/project/list',
'lending/project/position/list',
],
'post': [
'asset/dust',
@@ -146,6 +153,7 @@ module.exports = class binance extends Exchange {
'userDataStream',
'futures/transfer',
// lending
'lending/customizedFixed/purchase',
'lending/daily/purchase',
'lending/daily/redeem',
],
@@ -211,6 +219,8 @@ module.exports = class binance extends Exchange {
'income',
],
'post': [
'batchOrders',
'positionSide/dual',
'positionMargin',
'marginType',
'order',
@@ -221,6 +231,7 @@ module.exports = class binance extends Exchange {
'listenKey',
],
'delete': [
'batchOrders',
'order',
'allOpenOrders',
'listenKey',
@@ -267,6 +278,7 @@ module.exports = class binance extends Exchange {
'order/test',
],
'delete': [
'openOrders', // added on 2020-04-25 for canceling all open orders per symbol
'orderList', // oco
'order',
],
@@ -289,7 +301,6 @@ module.exports = class binance extends Exchange {
'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades
'fetchTickersMethod': 'publicGetTicker24hr',
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
'defaultLimitOrderType': 'limit', // or 'limit_maker'
'defaultType': 'spot', // 'spot', 'future'
'hasAlreadyAuthenticatedSuccessfully': false,
'warnOnFetchOpenOrdersWithoutSymbol': true,
@@ -326,6 +337,32 @@ module.exports = class binance extends Exchange {
});
}
setSandboxMode (enabled) {
if (enabled) { // eslint-disable-line no-extra-boolean-cast
if ('test' in this.urls) {
const type = this.safeString (this.options, 'defaultType', 'spot');
if (type !== 'future') {
throw new NotSupported (this.id + ' does not have a sandbox URL for ' + type + " markets, set exchange.options['defaultType'] = 'future' or don't use the sandbox for " + this.id);
}
if (typeof this.urls['api'] === 'string') {
this.urls['api_backup'] = this.urls['api'];
this.urls['api'] = this.urls['test'];
} else {
this.urls['api_backup'] = this.extend ({}, this.urls['api']);
this.urls['api'] = this.extend ({}, this.urls['test']);
}
} else {
throw new NotSupported (this.id + ' does not have a sandbox URL');
}
} else if ('api_backup' in this.urls) {
if (typeof this.urls['api'] === 'string') {
this.urls['api'] = this.urls['api_backup'];
} else {
this.urls['api'] = this.extend ({}, this.urls['api_backup']);
}
}
}
nonce () {
return this.milliseconds () - this.options['timeDifference'];
}
@@ -476,7 +513,7 @@ module.exports = class binance extends Exchange {
'max': undefined,
},
'cost': {
'min': -1 * Math.log10 (precision['amount']),
'min': undefined,
'max': undefined,
},
},
@@ -720,7 +757,7 @@ module.exports = class binance extends Exchange {
async fetchBidsAsks (symbols = undefined, params = {}) {
await this.loadMarkets ();
const defaultType = this.safeString2 (this.options, 'fetchOpenOrders', 'defaultType', 'spot');
const defaultType = this.safeString2 (this.options, 'fetchBidsAsks', 'defaultType', 'spot');
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
const method = (type === 'spot') ? 'publicGetTickerBookTicker' : 'fapiPublicGetTickerBookTicker';
@@ -737,12 +774,12 @@ module.exports = class binance extends Exchange {
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
return [
ohlcv[0],
parseFloat (ohlcv[1]),
parseFloat (ohlcv[2]),
parseFloat (ohlcv[3]),
parseFloat (ohlcv[4]),
parseFloat (ohlcv[5]),
this.safeInteger (ohlcv, 0),
this.safeFloat (ohlcv, 1),
this.safeFloat (ohlcv, 2),
this.safeFloat (ohlcv, 3),
this.safeFloat (ohlcv, 4),
this.safeFloat (ohlcv, 5),
];
}
@@ -870,6 +907,10 @@ module.exports = class binance extends Exchange {
if (market !== undefined) {
symbol = market['symbol'];
}
let cost = undefined;
if ((price !== undefined) && (amount !== undefined)) {
cost = price * amount;
}
return {
'info': trade,
'timestamp': timestamp,
@@ -878,11 +919,11 @@ module.exports = class binance extends Exchange {
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': takerOrMaker,
'side': side,
'takerOrMaker': takerOrMaker,
'price': price,
'amount': amount,
'cost': price * amount,
'cost': cost,
'fee': fee,
};
}
@@ -1180,6 +1221,8 @@ module.exports = class binance extends Exchange {
} else if (uppercaseType === 'STOP') {
stopPriceIsRequired = true;
priceIsRequired = true;
} else if (uppercaseType === 'STOP_MARKET') {
stopPriceIsRequired = true;
}
if (priceIsRequired) {
if (price === undefined) {
@@ -1339,6 +1382,27 @@ module.exports = class binance extends Exchange {
return this.parseOrder (response);
}
async cancelAllOrders (symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const defaultType = this.safeString2 (this.options, 'cancelAllOrders', 'defaultType', 'spot');
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
const method = (type === 'spot') ? 'privateDeleteOpenOrders' : 'fapiPrivateDeleteAllOpenOrders';
const response = await this[method] (this.extend (request, query));
if (Array.isArray (response)) {
return this.parseOrders (response, market);
} else {
return response;
}
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
@@ -1571,10 +1635,7 @@ module.exports = class binance extends Exchange {
}
parseTransactionStatusByType (status, type = undefined) {
if (type === undefined) {
return status;
}
const statuses = {
const statusesByType = {
'deposit': {
'0': 'pending',
'1': 'ok',
@@ -1589,32 +1650,38 @@ module.exports = class binance extends Exchange {
'6': 'ok', // Completed
},
};
return (status in statuses[type]) ? statuses[type][status] : status;
const statuses = this.safeValue (statusesByType, type, {});
return this.safeString (statuses, status, status);
}
parseTransaction (transaction, currency = undefined) {
//
// fetchDeposits
// { insertTime: 1517425007000,
// amount: 0.3,
// address: "0x0123456789abcdef",
// addressTag: "",
// txId: "0x0123456789abcdef",
// asset: "ETH",
// status: 1 }
//
// {
// insertTime: 1517425007000,
// amount: 0.3,
// address: "0x0123456789abcdef",
// addressTag: "",
// txId: "0x0123456789abcdef",
// asset: "ETH",
// status: 1
// }
//
// fetchWithdrawals
//
// { amount: 14,
// address: "0x0123456789abcdef...",
// {
// amount: 14,
// address: "0x0123456789abcdef...",
// successTime: 1514489710000,
// transactionFee: 0.01,
// addressTag: "",
// txId: "0x0123456789abcdef...",
// id: "0123456789abcdef...",
// asset: "ETH",
// applyTime: 1514488724000,
// status: 6 }
// transactionFee: 0.01,
// addressTag: "",
// txId: "0x0123456789abcdef...",
// id: "0123456789abcdef...",
// asset: "ETH",
// applyTime: 1514488724000,
// status: 6
// }
//
const id = this.safeString (transaction, 'id');
const address = this.safeString (transaction, 'address');
@@ -1624,7 +1691,7 @@ module.exports = class binance extends Exchange {
tag = undefined;
}
}
const txid = this.safeValue (transaction, 'txId');
const txid = this.safeString (transaction, 'txId');
const currencyId = this.safeString (transaction, 'asset');
const code = this.safeCurrencyCode (currencyId, currency);
let timestamp = undefined;
@@ -1854,6 +1921,11 @@ module.exports = class binance extends Exchange {
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
} else if (path === 'batchOrders') {
query = this.rawencode (this.extend ({
'timestamp': this.nonce (),
'recvWindow': this.options['recvWindow'],
}, params));
} else {
query = this.urlencode (this.extend ({
'timestamp': this.nonce (),
+3 -3
View File
@@ -31,10 +31,10 @@ module.exports = class binanceus extends binance {
},
'fees': {
'trading': {
'tierBased': false,
'tierBased': true,
'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
'taker': 0.001, // 0.1% trading fee, zero fees for all trading pairs before November 1
'maker': 0.001, // 0.1% trading fee, zero fees for all trading pairs before November 1
},
},
});
+67 -2
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound, AccountSuspended, BadSymbol, OrderImmediatelyFillable, RateLimitExceeded } = require ('./base/errors');
const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound, AccountSuspended, BadSymbol, OrderImmediatelyFillable, RateLimitExceeded, OnMaintenance } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -53,6 +53,7 @@ module.exports = class bitbay extends Exchange {
'https://github.com/BitBayNet/API',
'https://docs.bitbay.net/v1.0.1-en/reference',
],
'support': 'https://support.bitbay.net',
'fees': 'https://bitbay.net/en/fees',
},
'api': {
@@ -117,8 +118,60 @@ module.exports = class bitbay extends Exchange {
},
'fees': {
'trading': {
'maker': 0.0,
'taker': 0.1 / 100,
'percentage': true,
'tierBased': false,
},
'fiat': {
'maker': 0.30 / 100,
'taker': 0.43 / 100,
'percentage': true,
'tierBased': true,
'tiers': {
'taker': [
[ 0.0043, 0 ],
[ 0.0042, 1250 ],
[ 0.0041, 3750 ],
[ 0.0040, 7500 ],
[ 0.0039, 10000 ],
[ 0.0038, 15000 ],
[ 0.0037, 20000 ],
[ 0.0036, 25000 ],
[ 0.0035, 37500 ],
[ 0.0034, 50000 ],
[ 0.0033, 75000 ],
[ 0.0032, 100000 ],
[ 0.0031, 150000 ],
[ 0.0030, 200000 ],
[ 0.0029, 250000 ],
[ 0.0028, 375000 ],
[ 0.0027, 500000 ],
[ 0.0026, 625000 ],
[ 0.0025, 875000 ],
],
'maker': [
[ 0.0030, 0 ],
[ 0.0029, 1250 ],
[ 0.0028, 3750 ],
[ 0.0028, 7500 ],
[ 0.0027, 10000 ],
[ 0.0026, 15000 ],
[ 0.0025, 20000 ],
[ 0.0025, 25000 ],
[ 0.0024, 37500 ],
[ 0.0023, 50000 ],
[ 0.0023, 75000 ],
[ 0.0022, 100000 ],
[ 0.0021, 150000 ],
[ 0.0021, 200000 ],
[ 0.0020, 250000 ],
[ 0.0019, 375000 ],
[ 0.0018, 500000 ],
[ 0.0018, 625000 ],
[ 0.0017, 875000 ],
],
},
},
'funding': {
'withdraw': {
@@ -135,6 +188,9 @@ module.exports = class bitbay extends Exchange {
},
},
},
'options': {
'fiatCurrencies': [ 'EUR', 'USD', 'GBP', 'PLN' ],
},
'exceptions': {
'400': ExchangeError, // At least one parameter wasn't set
'401': InvalidOrder, // Invalid order type
@@ -159,12 +215,14 @@ module.exports = class bitbay extends Exchange {
'OFFER_NOT_FOUND': OrderNotFound,
'OFFER_WOULD_HAVE_BEEN_PARTIALLY_FILLED': OrderImmediatelyFillable,
'ACTION_LIMIT_EXCEEDED': RateLimitExceeded,
'UNDER_MAINTENANCE': OnMaintenance,
},
});
}
async fetchMarkets (params = {}) {
const response = await this.v1_01PublicGetTradingTicker (params);
const fiatCurrencies = this.safeValue (this.options, 'fiatCurrencies', []);
//
// {
// status: 'Ok',
@@ -203,6 +261,12 @@ module.exports = class bitbay extends Exchange {
'amount': this.safeInteger (first, 'scale'),
'price': this.safeInteger (second, 'scale'),
};
let fees = this.safeValue (this.fees, 'trading', {});
if (this.inArray (base, fiatCurrencies) || this.inArray (quote, fiatCurrencies)) {
fees = this.safeValue (this.fees, 'fiat', {});
}
const maker = this.safeFloat (fees, 'maker');
const taker = this.safeFloat (fees, 'taker');
// todo: check that the limits have ben interpreted correctly
// todo: parse the fees page
result.push ({
@@ -214,7 +278,8 @@ module.exports = class bitbay extends Exchange {
'quoteId': quoteId,
'precision': precision,
'active': undefined,
'fee': undefined,
'maker': maker,
'taker': taker,
'limits': {
'amount': {
'min': this.safeFloat (first, 'minOffer'),
+6 -6
View File
@@ -905,12 +905,12 @@ module.exports = class bitfinex extends Exchange {
parseOHLCV (ohlcv, market = undefined, timeframe = '1m', since = undefined, limit = undefined) {
return [
ohlcv[0],
ohlcv[1],
ohlcv[3],
ohlcv[4],
ohlcv[2],
ohlcv[5],
this.safeInteger (ohlcv, 0),
this.safeFloat (ohlcv, 1),
this.safeFloat (ohlcv, 3),
this.safeFloat (ohlcv, 4),
this.safeFloat (ohlcv, 2),
this.safeFloat (ohlcv, 5),
];
}
+1
View File
@@ -699,6 +699,7 @@ module.exports = class bitfinex2 extends bitfinex {
'PARTIALLY FILLED': 'open',
'EXECUTED': 'closed',
'CANCELED': 'canceled',
'CANCELED was: PARTIALLY FILLED': 'canceled',
'INSUFFICIENT MARGIN': 'canceled',
'INSUFFICIENT BALANCE (G1) was: PARTIALLY FILLED': 'canceled',
'RSN_DUST': 'rejected',
+393 -44
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress } = require ('./base/errors');
const { ExchangeError, ExchangeNotAvailable, AuthenticationError, BadRequest, PermissionDenied, InvalidAddress, ArgumentsRequired, InvalidOrder } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -16,7 +16,12 @@ module.exports = class bithumb extends Exchange {
'rateLimit': 500,
'has': {
'CORS': true,
'createOrder': true,
'cancelOrder': true,
'createMarketOrder': true,
'fetchTickers': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'withdraw': true,
},
'urls': {
@@ -48,8 +53,8 @@ module.exports = class bithumb extends Exchange {
'info/ticker',
'info/orders',
'info/user_transactions',
'trade/place',
'info/order_detail',
'trade/place',
'trade/cancel',
'trade/btc_withdrawal',
'trade/krw_deposit',
@@ -165,15 +170,53 @@ module.exports = class bithumb extends Exchange {
'currency': market['base'],
};
if (limit !== undefined) {
request['count'] = limit; // max = 50
request['count'] = limit; // default 30, max 30
}
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');
//
// {
// "status":"0000",
// "data":{
// "timestamp":"1587621553942",
// "payment_currency":"KRW",
// "order_currency":"BTC",
// "bids":[
// {"price":"8652000","quantity":"0.0043"},
// {"price":"8651000","quantity":"0.0049"},
// {"price":"8650000","quantity":"8.4791"},
// ],
// "asks":[
// {"price":"8654000","quantity":"0.119"},
// {"price":"8655000","quantity":"0.254"},
// {"price":"8658000","quantity":"0.119"},
// ]
// }
// }
//
const data = this.safeValue (response, 'data', {});
const timestamp = this.safeInteger (data, 'timestamp');
return this.parseOrderBook (data, timestamp, 'bids', 'asks', 'price', 'quantity');
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker, fetchTickers
//
// {
// "opening_price":"227100",
// "closing_price":"228400",
// "min_price":"222300",
// "max_price":"230000",
// "units_traded":"82618.56075337",
// "acc_trade_value":"18767376138.6031",
// "prev_closing_price":"227100",
// "units_traded_24H":"151871.13484676",
// "acc_trade_value_24H":"34247610416.8974",
// "fluctate_24H":"8700",
// "fluctate_rate_24H":"3.96",
// "date":"1587710327264", // fetchTickers inject this
// }
//
const timestamp = this.safeInteger (ticker, 'date');
let symbol = undefined;
if (market !== undefined) {
@@ -224,9 +267,31 @@ module.exports = class bithumb extends Exchange {
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.publicGetTickerAll (params);
//
// {
// "status":"0000",
// "data":{
// "BTC":{
// "opening_price":"9045000",
// "closing_price":"9132000",
// "min_price":"8938000",
// "max_price":"9168000",
// "units_traded":"4619.79967497",
// "acc_trade_value":"42021363832.5187",
// "prev_closing_price":"9041000",
// "units_traded_24H":"8793.5045804",
// "acc_trade_value_24H":"78933458515.4962",
// "fluctate_24H":"530000",
// "fluctate_rate_24H":"6.16"
// },
// "date":"1587710878669"
// }
// }
//
const result = {};
const timestamp = this.safeInteger (response['data'], 'date');
const tickers = this.omit (response['data'], 'date');
const data = this.safeValue (response, 'data', {});
const timestamp = this.safeInteger (data, 'date');
const tickers = this.omit (data, 'date');
const ids = Object.keys (tickers);
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
@@ -253,19 +318,72 @@ module.exports = class bithumb extends Exchange {
'currency': market['base'],
};
const response = await this.publicGetTickerCurrency (this.extend (request, params));
return this.parseTicker (response['data'], market);
//
// {
// "status":"0000",
// "data":{
// "opening_price":"227100",
// "closing_price":"228400",
// "min_price":"222300",
// "max_price":"230000",
// "units_traded":"82618.56075337",
// "acc_trade_value":"18767376138.6031",
// "prev_closing_price":"227100",
// "units_traded_24H":"151871.13484676",
// "acc_trade_value_24H":"34247610416.8974",
// "fluctate_24H":"8700",
// "fluctate_rate_24H":"3.96",
// "date":"1587710327264"
// }
// }
//
const data = this.safeValue (response, 'data', {});
return this.parseTicker (data, market);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// "transaction_date":"2020-04-23 22:21:46",
// "type":"ask",
// "units_traded":"0.0125",
// "price":"8667000",
// "total":"108337"
// }
//
// fetchOrder (private)
//
// {
// "transaction_date": "1572497603902030",
// "price": "8601000",
// "units": "0.005",
// "fee_currency": "KRW",
// "fee": "107.51",
// "total": "43005"
// }
//
// 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 = undefined;
const transactionDatetime = this.safeString (trade, 'transaction_date');
if (transactionDatetime !== undefined) {
const parts = transactionDatetime.split (' ');
const numParts = parts.length;
if (numParts > 1) {
const transactionDate = parts[0];
let transactionTime = parts[1];
if (transactionTime.length < 8) {
transactionTime = '0' + transactionTime;
}
timestamp = this.parse8601 (transactionDate + ' ' + transactionTime);
} else {
timestamp = this.safeIntegerProduct (trade, 'transaction_date', 0.001);
}
}
if (timestamp !== undefined) {
timestamp -= 9 * 3600000; // they report UTC + 9 hours, server in Korean timezone
}
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';
@@ -276,12 +394,24 @@ module.exports = class bithumb extends Exchange {
}
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;
let cost = this.safeFloat (trade, 'total');
if (cost === undefined) {
if (amount !== undefined) {
if (price !== undefined) {
cost = price * amount;
}
}
}
let fee = undefined;
const feeCost = this.safeFloat (trade, 'fee');
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'fee_currency');
const feeCurrencyCode = this.commonCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
};
}
return {
'id': id,
'info': trade,
@@ -295,7 +425,7 @@ module.exports = class bithumb extends Exchange {
'price': price,
'amount': amount,
'cost': cost,
'fee': undefined,
'fee': fee,
};
}
@@ -309,57 +439,276 @@ module.exports = class bithumb extends Exchange {
request['count'] = limit; // default 20, max 100
}
const response = await this.publicGetTransactionHistoryCurrency (this.extend (request, params));
return this.parseTrades (response['data'], market, since, limit);
//
// {
// "status":"0000",
// "data":[
// {
// "transaction_date":"2020-04-23 22:21:46",
// "type":"ask",
// "units_traded":"0.0125",
// "price":"8667000",
// "total":"108337"
// },
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseTrades (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';
const request = {
'order_currency': market['id'],
'Payment_currency': market['quote'],
'units': amount,
};
let method = 'privatePostTradePlace';
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);
request['price'] = price;
request['type'] = (side === 'buy') ? 'bid' : 'ask';
} else {
method = 'privatePostTradeMarket' + this.capitalize (side);
}
const response = await this[method] (this.extend (request, params));
const id = this.safeString (response, 'order_id');
if (id === undefined) {
throw new InvalidOrder (this.id + ' createOrder did not return an order id');
}
return {
'info': response,
'symbol': symbol,
'type': type,
'side': side,
'id': id,
};
}
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_id': id,
'count': 1,
'order_currency': market['base'],
'payment_currency': market['quote'],
};
const response = await this.privatePostInfoOrderDetail (this.extend (request, params));
//
// {
// "status": "0000",
// "data": {
// "transaction_date": "1572497603668315",
// "type": "bid",
// "order_status": "Completed",
// "order_currency": "BTC",
// "payment_currency": "KRW",
// "order_price": "8601000",
// "order_qty": "0.007",
// "cancel_date": "",
// "cancel_type": "",
// "contract": [
// {
// "transaction_date": "1572497603902030",
// "price": "8601000",
// "units": "0.005",
// "fee_currency": "KRW",
// "fee": "107.51",
// "total": "43005"
// },
// ]
// }
// }
//
const data = this.safeValue (response, 'data');
return this.parseOrder (this.extend (data, { 'order_id': id }), market);
}
parseOrderStatus (status) {
const statuses = {
'Pending': 'open',
'Completed': 'closed',
'Cancel': 'canceled',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// fetchOrder
//
// {
// "transaction_date": "1572497603668315",
// "type": "bid",
// "order_status": "Completed",
// "order_currency": "BTC",
// "payment_currency": "KRW",
// "order_price": "8601000",
// "order_qty": "0.007",
// "cancel_date": "",
// "cancel_type": "",
// "contract": [
// {
// "transaction_date": "1572497603902030",
// "price": "8601000",
// "units": "0.005",
// "fee_currency": "KRW",
// "fee": "107.51",
// "total": "43005"
// },
// ]
// }
//
// fetchOpenOrders
//
// {
// "order_currency": "BTC",
// "payment_currency": "KRW",
// "order_id": "C0101000007408440032",
// "order_date": "1571728739360570",
// "type": "bid",
// "units": "5.0",
// "units_remaining": "5.0",
// "price": "501000",
// }
//
const timestamp = this.safeIntegerProduct (order, 'order_date', 0.001);
const sideProperty = this.safeValue2 (order, 'type', 'side');
const side = (sideProperty === 'bid') ? 'buy' : 'sell';
const status = this.parseOrderStatus (this.safeString (order, 'order_status'));
let price = this.safeFloat2 (order, 'order_price', 'price');
let type = 'limit';
if (price === 0) {
price = undefined;
type = 'market';
}
const amount = this.safeFloat2 (order, 'order_qty', 'units');
let remaining = this.safeFloat (order, 'units_remaining');
if (remaining === undefined) {
if (status === 'closed') {
remaining = 0;
} else {
remaining = amount;
}
}
let filled = undefined;
if ((amount !== undefined) && (remaining !== undefined)) {
filled = amount - remaining;
}
let symbol = undefined;
const baseId = this.safeString (order, 'order_currency');
const quoteId = this.safeString (order, 'payment_currency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
if ((base !== undefined) && (quote !== undefined)) {
symbol = base + '/' + quote;
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
}
const rawTrades = this.safeValue (order, 'contract');
let trades = undefined;
const id = this.safeString (order, 'order_id');
if (rawTrades !== undefined) {
trades = this.parseTrades (rawTrades, market, undefined, undefined, {
'side': side,
'symbol': symbol,
'order': id,
});
}
return {
'info': order,
'id': id,
'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': trades,
};
}
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);
if (limit === undefined) {
limit = 100;
}
const request = {
'count': limit,
'order_currency': market['base'],
'payment_currency': market['quote'],
};
if (since !== undefined) {
request['after'] = since;
}
const response = await this.privatePostInfoOrders (this.extend (request, params));
//
// {
// "status": "0000",
// "data": [
// {
// "order_currency": "BTC",
// "payment_currency": "KRW",
// "order_id": "C0101000007408440032",
// "order_date": "1571728739360570",
// "type": "bid",
// "units": "5.0",
// "units_remaining": "5.0",
// "price": "501000",
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseOrders (data, market, since, limit);
}
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');
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)');
}
const currency = this.safeString (params, 'currency');
if (currency === undefined) {
throw new ExchangeError (this.id + ' cancelOrder requires a `currency` parameter (a currency id)');
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)');
}
const market = this.market (symbol);
const side = (params['side'] === 'buy') ? 'bid' : 'ask';
params = this.omit (params, [ 'side', 'currency' ]);
// https://github.com/ccxt/ccxt/issues/6771
const request = {
'order_id': id,
'type': side,
'currency': currency,
'order_currency': market['base'],
'payment_currency': market['quote'],
};
return await this.privatePostTradeCancel (this.extend (request, params));
}
cancelUnifiedOrder (order, params = {}) {
const request = {
'side': order['side'],
};
return this.cancelOrder (order['id'], order['symbol'], this.extend (request, params));
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
this.checkAddress (address);
await this.loadMarkets ();
@@ -372,7 +721,7 @@ module.exports = class bithumb extends Exchange {
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');
throw new ArgumentsRequired (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param');
} else if (tag !== undefined) {
request['destination'] = tag;
}
+2
View File
@@ -35,6 +35,7 @@ module.exports = class bitmart extends Exchange {
'fetchClosedOrders': true,
'fetchCanceledOrders': true,
'fetchOrder': true,
'signIn': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg',
@@ -42,6 +43,7 @@ module.exports = class bitmart extends Exchange {
'www': 'https://www.bitmart.com/',
'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs',
'referral': 'http://www.bitmart.com/?r=rQCFLh',
'fees': 'https://www.bitmart.com/fee/en',
},
'requiredCredentials': {
'apiKey': true,
+9 -1
View File
@@ -1134,11 +1134,19 @@ module.exports = class bitstamp extends Exchange {
body = this.urlencode (query);
contentType = 'application/x-www-form-urlencoded';
headers['Content-Type'] = contentType;
} else {
// sending an empty POST request will trigger
// an API0020 error returned by the exchange
// therefore for empty requests we send a dummy object
// https://github.com/ccxt/ccxt/issues/6846
body = this.urlencode ({ 'foo': 'bar' });
contentType = 'application/x-www-form-urlencoded';
headers['Content-Type'] = contentType;
}
}
const authBody = body ? body : '';
const auth = xAuth + method + url.replace ('https://', '') + contentType + xAuthNonce + xAuthTimestamp + xAuthVersion + authBody;
const signature = this.encode (this.hmac (this.encode (auth), this.encode (this.secret)));
const signature = this.hmac (this.encode (auth), this.encode (this.secret));
headers['X-Auth-Signature'] = signature;
}
}
+1 -1
View File
@@ -650,7 +650,7 @@ module.exports = class bitz extends Exchange {
}
} else {
if (since !== undefined) {
throw new ExchangeError (this.id + ' fetchOHLCV requires a limit argument if the since argument is specified');
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a limit argument if the since argument is specified');
}
}
const response = await this.marketGetKline (this.extend (request, params));
+5 -2
View File
@@ -73,6 +73,9 @@ module.exports = class bleutrade extends Exchange {
],
},
'v3Private': {
'get': [
'statement',
],
'post': [
'getbalance',
'getbalances',
@@ -189,8 +192,8 @@ module.exports = class bleutrade extends Exchange {
// MarketCurrencyLong: 'Litecoin',
// BaseCurrencyLong: 'Tether' }
const id = this.safeString (market, 'MarketName');
const baseId = this.safeString (market, 'MarketCurrency');
const quoteId = this.safeString (market, 'BaseCurrency');
const baseId = this.safeString (market, 'MarketAsset');
const quoteId = this.safeString (market, 'BaseAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
+1
View File
@@ -18,6 +18,7 @@ module.exports = class btctradeua extends Exchange {
'CORS': false,
'createMarketOrder': false,
'fetchOpenOrders': true,
'signIn': true,
},
'urls': {
'referral': 'https://btc-trade.com.ua/registration/22689',
+3 -3
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { AddressPending, AuthenticationError, ExchangeError, NotSupported, PermissionDenied } = require ('./base/errors');
const { AddressPending, AuthenticationError, ExchangeError, NotSupported, PermissionDenied, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -692,7 +692,7 @@ module.exports = class buda extends Exchange {
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (code === undefined) {
throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument');
throw new ArgumentsRequired (this.id + ': fetchDeposits() requires a currency code argument');
}
const currency = this.currency (code);
const request = {
@@ -707,7 +707,7 @@ module.exports = class buda extends Exchange {
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (code === undefined) {
throw new ExchangeError (this.id + ': fetchDeposits() requires a currency code argument');
throw new ArgumentsRequired (this.id + ': fetchDeposits() requires a currency code argument');
}
const currency = this.currency (code);
const request = {
+220 -57
View File
@@ -4,7 +4,7 @@
const Exchange = require ('./base/Exchange');
const { TICK_SIZE } = require ('./base/functions/number');
const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, InsufficientFunds, BadRequest, RateLimitExceeded, InvalidNonce } = require ('./base/errors');
const { AuthenticationError, ExchangeError, ArgumentsRequired, PermissionDenied, InvalidOrder, OrderNotFound, InsufficientFunds, BadRequest, RateLimitExceeded, InvalidNonce, NotSupported } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -48,7 +48,7 @@ module.exports = class bybit extends Exchange {
'30m': '30',
'1h': '60',
'2h': '120',
'3h': '180',
'4h': '240',
'6h': '360',
'12h': '720',
'1d': 'D',
@@ -63,6 +63,7 @@ module.exports = class bybit extends Exchange {
'www': 'https://www.bybit.com/',
'doc': [
'https://bybit-exchange.github.io/docs/inverse/',
'https://bybit-exchange.github.io/docs/linear/',
'https://github.com/bybit-exchange',
],
'fees': 'https://help.bybit.com/hc/en-us/articles/360039261154',
@@ -116,6 +117,41 @@ module.exports = class bybit extends Exchange {
'position/trading-stop',
],
},
'publicLinear': {
'get': [
'kline',
'recent-trading-records',
'funding/prev-funding-rate',
'mark-price-kline',
],
},
'privateLinear': {
'get': [
'order/list',
'order/search',
'stop-order/list',
'stop-order/search',
'position/list',
'position/set-auto-add-margin',
'position/set-leverage',
'position/switch-isolated',
'position/trading-stop',
'position/add-margin',
'trade/execution/list',
'trade/closed-pnl/list',
'risk-limit',
'funding/prev-funding',
'funding/predicted-funding',
],
'post': [
'order/create',
'order/cancel',
'order/cancelAll',
'stop-order/create',
'stop-order/cancel',
'stop-order/cancelAll',
],
},
'position': {
'post': [
'change-position-margin',
@@ -227,6 +263,9 @@ module.exports = class bybit extends Exchange {
},
'precisionMode': TICK_SIZE,
'options': {
'marketTypes': {
'BTC/USDT': 'linear',
},
'code': 'BTC',
'fetchBalance': {
'code': 'BTC',
@@ -309,6 +348,8 @@ module.exports = class bybit extends Exchange {
// }
//
const markets = this.safeValue (response, 'result', []);
const options = this.safeValue (this.options, 'fetchMarkets', {});
const linearQuoteCurrencies = this.safeValue (options, 'linear', { 'USDT': true });
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
@@ -317,6 +358,8 @@ module.exports = class bybit extends Exchange {
const quoteId = this.safeString (market, 'quote_currency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const linear = (quote in linearQuoteCurrencies);
const inverse = !linear;
const symbol = base + '/' + quote;
const lotSizeFilter = this.safeValue (market, 'lot_size_filter', {});
const priceFilter = this.safeValue (market, 'price_filter', {});
@@ -333,6 +376,12 @@ module.exports = class bybit extends Exchange {
'precision': precision,
'taker': this.safeFloat (market, 'taker_fee'),
'maker': this.safeFloat (market, 'maker_fee'),
'type': 'future',
'spot': false,
'future': true,
'option': false,
'linear': linear,
'inverse': inverse,
'limits': {
'amount': {
'min': this.safeFloat (lotSizeFilter, 'min_trading_qty'),
@@ -347,10 +396,6 @@ module.exports = class bybit extends Exchange {
'max': undefined,
},
},
'type': 'future',
'spot': false,
'future': true,
'option': false,
'info': market,
});
}
@@ -610,13 +655,25 @@ module.exports = class bybit extends Exchange {
// turnover: '162.32773718999994'
// },
//
// {
// "id":143536,
// "symbol":"BTCUSDT",
// "period":"15",
// "start_at":1587883500,
// "volume":1.035,
// "open":7540.5,
// "high":7541,
// "low":7540.5,
// "close":7541
// }
//
return [
this.safeTimestamp (ohlcv, 'open_time'),
this.safeTimestamp2 (ohlcv, 'open_time', 'start_at'),
this.safeFloat (ohlcv, 'open'),
this.safeFloat (ohlcv, 'high'),
this.safeFloat (ohlcv, 'low'),
this.safeFloat (ohlcv, 'close'),
this.safeFloat (ohlcv, 'turnover'),
this.safeFloat2 (ohlcv, 'turnover', 'volume'),
];
}
@@ -641,7 +698,13 @@ module.exports = class bybit extends Exchange {
if (limit !== undefined) {
request['limit'] = limit; // max 200, default 200
}
const response = await this.publicGetKlineList (this.extend (request, params));
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'publicLinearGetKline' : 'publicGetKlineList';
const response = await this[method] (this.extend (request, params));
//
// inverse perpetual BTC/USD
//
// {
// ret_code: 0,
// ret_msg: 'OK',
@@ -662,6 +725,30 @@ module.exports = class bybit extends Exchange {
// ],
// time_now: '1583953082.397330'
// }
//
// linear perpetual BTC/USDT
//
// {
// "ret_code":0,
// "ret_msg":"OK",
// "ext_code":"",
// "ext_info":"",
// "result":[
// {
// "id":143536,
// "symbol":"BTCUSDT",
// "period":"15",
// "start_at":1587883500,
// "volume":1.035,
// "open":7540.5,
// "high":7541,
// "low":7540.5,
// "close":7541
// }
// ],
// "time_now":"1587884120.168077"
// }
//
const result = this.safeValue (response, 'result', {});
return this.parseOHLCVs (result, market, timeframe, since, limit);
}
@@ -688,6 +775,8 @@ module.exports = class bybit extends Exchange {
// "exec_id": "256e5ef8-abfe-5772-971b-f944e15e0d68",
// "exec_price": "8178.5",
// "exec_qty": 1,
// // the docs say the exec_time field is "abandoned" now
// // the user should use "trade_time_ms"
// "exec_time": "1571676941.70682",
// "exec_type": "Trade", //Exec Type Enum
// "exec_value": "0.00012227",
@@ -702,30 +791,35 @@ module.exports = class bybit extends Exchange {
// "order_type": "Market", //Order Type Enum
// "side": "Buy", //Side Enum
// "symbol": "BTCUSD", //Symbol Enum
// "user_id": 1
// "user_id": 1,
// "trade_time_ms": 1577480599000
// }
//
const id = this.safeString2 (trade, 'id', 'exec_id');
let symbol = undefined;
let base = undefined;
const marketId = this.safeString (trade, 'symbol');
let amount = this.safeFloat2 (trade, 'qty', 'exec_qty');
let cost = this.safeFloat (trade, 'exec_value');
const price = this.safeFloat2 (trade, 'price', 'exec_price');
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
base = market['base'];
}
if ((symbol === undefined) && (market !== undefined)) {
symbol = market['symbol'];
base = market['base'];
if (market !== undefined) {
if (symbol === undefined) {
symbol = market['symbol'];
base = market['base'];
}
// if private trade
if ('exec_fee' in trade) {
if (market['inverse']) {
amount = this.safeFloat (trade, 'exec_value');
cost = this.safeFloat (trade, 'exec_qty');
}
}
}
let timestamp = this.parse8601 (this.safeString (trade, 'time'));
if (timestamp === undefined) {
timestamp = this.safeTimestamp (trade, 'exec_time');
}
const side = this.safeStringLower (trade, 'side');
const price = this.safeFloat2 (trade, 'price', 'exec_price');
const amount = this.safeFloat2 (trade, 'qty', 'exec_qty');
let cost = this.safeFloat (trade, 'exec_value');
if (cost === undefined) {
if (amount !== undefined) {
if (price !== undefined) {
@@ -733,6 +827,11 @@ module.exports = class bybit extends Exchange {
}
}
}
let timestamp = this.parse8601 (this.safeString (trade, 'time'));
if (timestamp === undefined) {
timestamp = this.safeInteger (trade, 'trade_time_ms');
}
const side = this.safeStringLower (trade, 'side');
const lastLiquidityInd = this.safeString (trade, 'last_liquidity_ind');
const takerOrMaker = (lastLiquidityInd === 'AddedLiquidity') ? 'maker' : 'taker';
const feeCost = this.safeFloat (trade, 'exec_fee');
@@ -771,7 +870,10 @@ module.exports = class bybit extends Exchange {
if (limit !== undefined) {
request['count'] = limit; // default 500, max 1000
}
const response = await this.publicGetTradingRecords (this.extend (request, params));
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'publicGetTradingRecords';
const response = await this[method] (this.extend (request, params));
//
// {
// ret_code: 0,
@@ -885,8 +987,8 @@ module.exports = class bybit extends Exchange {
// "last_exec_time": 0,
// "last_exec_price": 0,
// "leaves_qty": 1,
// "cum_exec_qty": 0,
// "cum_exec_value": 0,
// "cum_exec_qty": 0, // in contracts, where 1 contract = 1 quote currency unit (USD for inverse contracts)
// "cum_exec_value": 0, // in contract's underlying currency (BTC for inverse contracts)
// "cum_exec_fee": 0,
// "reject_reason": "",
// "order_link_id": "",
@@ -894,18 +996,69 @@ module.exports = class bybit extends Exchange {
// "updated_at": "2019-11-30T11:03:43.455Z"
// }
//
// fetchOrder
//
// {
// "user_id" : 599946,
// "symbol" : "BTCUSD",
// "side" : "Buy",
// "order_type" : "Limit",
// "price" : "7948",
// "qty" : 10,
// "time_in_force" : "GoodTillCancel",
// "order_status" : "Filled",
// "ext_fields" : {
// "o_req_num" : -1600687220498,
// "xreq_type" : "x_create"
// },
// "last_exec_time" : "1588150113.968422",
// "last_exec_price" : "7948",
// "leaves_qty" : 0,
// "leaves_value" : "0",
// "cum_exec_qty" : 10,
// "cum_exec_value" : "0.00125817",
// "cum_exec_fee" : "-0.00000031",
// "reject_reason" : "",
// "cancel_type" : "",
// "order_link_id" : "",
// "created_at" : "2020-04-29T08:45:24.399146Z",
// "updated_at" : "2020-04-29T08:48:33.968422Z",
// "order_id" : "dd2504b9-0157-406a-99e1-efa522373944"
// }
//
const marketId = this.safeString (order, 'symbol');
let symbol = undefined;
let base = undefined;
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
}
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
const id = this.safeString (order, 'order_id');
const price = this.safeFloat (order, 'price');
const average = this.safeFloat (order, 'average_price');
const amount = this.safeFloat (order, 'qty');
let filled = this.safeFloat (order, 'cum_exec_qty');
let remaining = this.safeFloat (order, 'leaves_qty');
let amount = undefined;
let cost = undefined;
let filled = undefined;
let remaining = undefined;
if (market !== undefined) {
symbol = market['symbol'];
base = market['base'];
if (market['inverse']) {
cost = this.safeFloat (order, 'cum_exec_qty');
filled = this.safeFloat (order, 'cum_exec_value');
remaining = this.safeFloat (order, 'leaves_value');
amount = this.sum (filled, remaining);
} else {
amount = this.safeFloat (order, 'qty');
cost = this.safeFloat (order, 'cum_exec_value');
filled = this.safeFloat (order, 'cum_exec_qty');
remaining = this.safeFloat (order, 'leaves_qty');
}
}
let lastTradeTimestamp = this.safeTimestamp (order, 'last_exec_time');
if (lastTradeTimestamp === 0) {
lastTradeTimestamp = undefined;
}
let cost = this.safeFloat (order, 'cum_exec_value');
if ((filled === undefined) && (amount !== undefined) && (remaining !== undefined)) {
filled = amount - remaining;
}
@@ -920,22 +1073,6 @@ module.exports = class bybit extends Exchange {
}
}
const status = this.parseOrderStatus (this.safeString (order, 'order_status'));
const marketId = this.safeString (order, 'symbol');
let symbol = undefined;
let base = undefined;
if (marketId in this.markets_by_id) {
market = this.markets_by_id[marketId];
symbol = market['symbol'];
base = market['base'];
}
if (market !== undefined) {
if (symbol === undefined) {
symbol = market['symbol'];
}
if (base === undefined) {
base = market['base'];
}
}
const side = this.safeStringLower (order, 'side');
let feeCost = this.safeFloat (order, 'cum_exec_fee');
let fee = undefined;
@@ -987,7 +1124,9 @@ module.exports = class bybit extends Exchange {
// conditional orders ---------------------------------------------
// 'stop_order_id': id, // one of stop_order_id or order_link_id is required for conditional orders
};
let method = 'privateGetOrder';
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearGetOrderSearch' : 'privateGetOrder';
const stopOrderId = this.safeString (params, 'stop_order_id');
if (stopOrderId === undefined) {
const orderLinkId = this.safeString (params, 'order_link_id');
@@ -995,7 +1134,7 @@ module.exports = class bybit extends Exchange {
request['order_id'] = id;
}
} else {
method = 'privateGetStopOrder';
method = (marketType === 'linear') ? 'privateLinearGetStopOrderSearch' : 'privateGetStopOrder';
}
const response = await this[method] (this.extend (request, params));
//
@@ -1107,12 +1246,14 @@ module.exports = class bybit extends Exchange {
}
const stopPx = this.safeValue (params, 'stop_px');
const basePrice = this.safeValue (params, 'base_price');
let method = 'privatePostOrderCreate';
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearPostOrderCreate' : 'privatePostOrderCreate';
if (stopPx !== undefined) {
if (basePrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires both the stop_px and base_price params for a conditional ' + type + ' order');
} else {
method = 'openapiPostStopOrderCreate';
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCreate' : 'openapiPostStopOrderCreate';
request['stop_px'] = this.priceToPrecision (symbol, stopPx);
request['base_price'] = this.priceToPrecision (symbol, basePrice);
params = this.omit (params, [ 'stop_px', 'base_price' ]);
@@ -1206,6 +1347,11 @@ module.exports = class bybit extends Exchange {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires an symbol argument');
}
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
if (marketType === 'linear') {
throw new NotSupported (this.id + ' does not support editOrder for ' + marketType + ' ' + symbol + ' market type');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
@@ -1283,7 +1429,9 @@ module.exports = class bybit extends Exchange {
// conditional orders ---------------------------------------------
// 'stop_order_id': id, // one of stop_order_id or order_link_id is required for conditional orders
};
let method = 'privatePostOrderCancel';
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeValue (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearPostOrderCancel' : 'privatePostOrderCancel';
const stopOrderId = this.safeString (params, 'stop_order_id');
if (stopOrderId === undefined) {
const orderLinkId = this.safeString (params, 'order_link_id');
@@ -1291,7 +1439,7 @@ module.exports = class bybit extends Exchange {
request['order_id'] = id;
}
} else {
method = 'openapiPostStopOrderCancel';
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCancel' : 'openapiPostStopOrderCancel';
}
const response = await this[method] (this.extend (request, params));
const result = this.safeValue (response, 'result', {});
@@ -1308,7 +1456,10 @@ module.exports = class bybit extends Exchange {
'symbol': market['id'],
};
const options = this.safeValue (this.options, 'cancelAllOrders');
const method = this.safeString (options, 'method', 'privatePostOrderCancelAll');
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const defaultMethod = (marketType === 'linear') ? 'privateLinearPostOrderCancelAll' : 'privatePostOrderCancelAll';
const method = this.safeString (options, 'method', defaultMethod);
const response = await this[method] (this.extend (request, params));
const result = this.safeValue (response, 'result', []);
return this.parseOrders (result, market);
@@ -1337,7 +1488,9 @@ module.exports = class bybit extends Exchange {
request['limit'] = limit;
}
const options = this.safeValue (this.options, 'fetchOrders', {});
let defaultMethod = 'openapiGetOrderList';
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let defaultMethod = (marketType === 'linear') ? 'privateLinearGetOrderList' : 'openapiGetOrderList';
let query = params;
if (('stop_order_id' in params) || ('stop_order_status' in params)) {
let stopOrderStatus = this.safeValue (params, 'stopOrderStatus');
@@ -1348,7 +1501,7 @@ module.exports = class bybit extends Exchange {
request['stop_order_status'] = stopOrderStatus;
query = this.omit (params, 'stop_order_status');
}
defaultMethod = 'openapiGetStopOrderList';
defaultMethod = (marketType === 'linear') ? 'privateLinearGetStopOrderList' : 'openapiGetStopOrderList';
}
const method = this.safeString (options, 'method', defaultMethod);
const response = await this[method] (this.extend (request, query));
@@ -1524,12 +1677,15 @@ module.exports = class bybit extends Exchange {
request['symbol'] = market['id'];
}
if (since !== undefined) {
request['start_time'] = parseInt (since / 1000);
request['start_time'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 20, max 50
}
const response = await this.privateGetExecutionList (this.extend (request, params));
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'privateLinearGetTradeExecutionList' : 'privateGetExecutionList';
const response = await this[method] (this.extend (request, params));
//
// {
// "ret_code": 0,
@@ -1864,6 +2020,11 @@ module.exports = class bybit extends Exchange {
if (Object.keys (params).length) {
request += '?' + this.rawencode (params);
}
} else if (api === 'publicLinear') {
request = '/public/linear/' + request;
if (Object.keys (params).length) {
request += '?' + this.rawencode (params);
}
} else {
this.checkRequiredCredentials ();
if (api === 'openapi') {
@@ -1871,14 +2032,16 @@ module.exports = class bybit extends Exchange {
} else if (api === 'private') {
// private v2
request = '/' + this.version + '/' + api + '/' + request;
} else if (api === 'privateLinear') {
request = '/private/linear/' + request;
} else {
// position, user
request += '/' + api + '/' + request;
request = '/' + api + '/' + request;
}
const timestamp = this.nonce ();
const query = this.extend (params, {
'api_key': this.apiKey,
'recvWindow': this.options['recvWindow'],
'recv_window': this.options['recvWindow'],
'timestamp': timestamp,
});
const auth = this.rawencode (this.keysort (query));
+11 -5
View File
@@ -47,9 +47,15 @@ module.exports = class bytetrade extends Exchange {
'1M': '1M',
},
'urls': {
'test': 'https://api-v2-test.byte-trade.com',
'test': {
'market': 'https://api-v2-test.byte-trade.com',
'public': 'https://api-v2-test.byte-trade.com',
},
'logo': 'https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg',
'api': 'https://api-v2.byte-trade.com',
'api': {
'market': 'https://api-v2.bytetrade.com',
'public': 'https://api-v2.bytetrade.com',
},
'www': 'https://www.byte-trade.com',
'doc': 'https://github.com/Bytetrade/bytetrade-official-api-docs/wiki',
},
@@ -1222,7 +1228,7 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (address.length, 1),
this.stringToBinary (this.encode (address)),
this.numberToLE (parseInt (coinId), 4),
this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8),
this.numberToLE (this.integerDivide (amountChain, eightBytes), 8),
this.numberToLE (this.integerModulo (amountChain, eightBytes), 8),
this.numberToLE (1, 1),
this.numberToLE (this.integerDivide (assetFee, eightBytes), 8),
@@ -1255,7 +1261,7 @@ module.exports = class bytetrade extends Exchange {
this.numberToLE (middleAddress.length, 1),
this.stringToBinary (this.encode (middleAddress)),
this.numberToLE (parseInt (coinId), 4),
this.numberToLE (Math.floor (parseInt (parseFloat (this.integerDivide (amountChain, eightBytes)))), 8),
this.numberToLE (this.integerDivide (amountChain, eightBytes), 8),
this.numberToLE (this.integerModulo (amountChain, eightBytes), 8),
this.numberToLE (0, 1),
this.numberToLE (1, 1),
@@ -1358,7 +1364,7 @@ module.exports = class bytetrade extends Exchange {
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'];
let url = this.urls['api'][api];
url += '/' + path;
if (Object.keys (params).length) {
url += '?' + this.urlencode (params);
+37 -1
View File
@@ -564,9 +564,45 @@ module.exports = class cex extends Exchange {
request['order_type'] = type;
}
const response = await this.privatePostPlaceOrderPair (this.extend (request, params));
//
// {
// "id": "12978363524",
// "time": 1586610022259,
// "type": "buy",
// "price": "0.033934",
// "amount": "0.10722802",
// "pending": "0.10722802",
// "complete": false
// }
//
const placedAmount = this.safeFloat (response, 'amount');
const remaining = this.safeFloat (response, 'pending');
const timestamp = this.safeValue (response, 'time');
const complete = this.safeValue (response, 'complete');
const status = complete ? 'closed' : 'open';
let filled = undefined;
if ((placedAmount !== undefined) && (remaining !== undefined)) {
filled = Math.max (placedAmount - remaining, 0);
}
return {
'id': this.safeString (response, 'id'),
'info': response,
'id': response['id'],
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'type': type,
'side': this.safeString (response, 'type'),
'symbol': symbol,
'status': status,
'price': this.safeFloat (response, 'price'),
'amount': placedAmount,
'cost': undefined,
'average': undefined,
'remaining': remaining,
'filled': filled,
'fee': undefined,
'trades': undefined,
};
}
+6 -2
View File
@@ -561,6 +561,7 @@ module.exports = class coinmate extends Exchange {
const statuses = {
'FILLED': 'closed',
'CANCELLED': 'canceled',
'PARTIALLY_FILLED': 'open',
'OPEN': 'open',
};
return this.safeString (statuses, status, status);
@@ -623,12 +624,15 @@ module.exports = class coinmate extends Exchange {
const price = this.safeFloat (order, 'price');
const amount = this.safeFloat2 (order, 'originalAmount', 'amount');
const remaining = this.safeFloat (order, 'remainingAmount', amount);
const status = this.parseOrderStatus (this.safeString (order, 'status'));
let status = this.parseOrderStatus (this.safeString (order, 'status'));
const type = this.parseOrderType (this.safeString (order, 'orderTradeType'));
let filled = undefined;
let cost = undefined;
if ((amount !== undefined) && (remaining !== undefined)) {
filled = amount - remaining;
filled = Math.min (amount - remaining, 0);
if (remaining === 0) {
status = 'closed';
}
if (price !== undefined) {
cost = filled * price;
}
-1
View File
@@ -129,7 +129,6 @@ module.exports = class crex24 extends Exchange {
'fetchClosedOrdersMethod': 'tradingGetOrderHistory', // or 'tradingGetActiveOrders'
'fetchTickersMethod': 'publicGetTicker24hr',
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
'defaultLimitOrderType': 'limit', // or 'limit_maker'
'hasAlreadyAuthenticatedSuccessfully': false,
'warnOnFetchOpenOrdersWithoutSymbol': true,
'parseOrderToPrecision': false, // force amounts and costs in parseOrder to precision
+4 -2
View File
@@ -525,11 +525,13 @@ module.exports = class deribit extends Exchange {
'info': response,
};
const balance = this.safeValue (response, 'result', {});
const currencyId = this.safeString (balance, 'currency');
const currencyCode = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'availableFunds');
account['used'] = this.safeFloat (balance, 'maintenanceMargin');
account['total'] = this.safeFloat (balance, 'equity');
result[code] = account;
result[currencyCode] = account;
return this.parseBalance (result);
}
@@ -883,7 +885,7 @@ module.exports = class deribit extends Exchange {
// M = maker, T = taker, MT = both
takerOrMaker = (liquidity === 'M') ? 'maker' : 'taker';
}
const feeCost = this.safeFloat (trade, 'feeCost');
const feeCost = this.safeFloat (trade, 'fee');
let fee = undefined;
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'fee_currency');
+363 -152
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded } = require ('./base/errors');
const { ArgumentsRequired, ExchangeError, OrderNotFound, AuthenticationError, InsufficientFunds, InvalidOrder, InvalidNonce, NotSupported, OnMaintenance, RateLimitExceeded, BadRequest, PermissionDenied } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -14,7 +14,7 @@ module.exports = class exmo extends Exchange {
'name': 'EXMO',
'countries': [ 'ES', 'RU' ], // Spain, Russia
'rateLimit': 350, // once every 350 ms ≈ 180 requests per minute ≈ 3 requests per second
'version': 'v1',
'version': 'v1.1',
'has': {
'CORS': false,
'fetchClosedOrders': 'emulated',
@@ -32,6 +32,21 @@ module.exports = class exmo extends Exchange {
'fetchFundingFees': true,
'fetchCurrencies': true,
'fetchTransactions': true,
'fetchOHLCV': true,
},
'timeframes': {
'1m': '1',
'5m': '5',
'15m': '15',
'30m': '30',
'45m': '45',
'1h': '60',
'2h': '120',
'3h': '180',
'4h': '240',
'1d': 'D',
'1w': 'W',
'1M': 'M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg',
@@ -62,6 +77,7 @@ module.exports = class exmo extends Exchange {
'pair_settings',
'ticker',
'trades',
'candles_history',
],
},
'private': {
@@ -69,6 +85,8 @@ module.exports = class exmo extends Exchange {
'user_info',
'order_create',
'order_cancel',
'stop_market_order_create',
'stop_market_order_cancel',
'user_open_orders',
'user_trades',
'user_cancelled_orders',
@@ -103,86 +121,90 @@ module.exports = class exmo extends Exchange {
'error': '',
'data': {
'limits': [
{ 'pair': 'EXM/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'EXM/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'ALGO/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'ALGO/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '50' },
{ 'pair': 'ATOM/EXM', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '50000000' },
{ 'pair': 'ATOM/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTC/USD', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/KZT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1000', 'max_p': '12000000', 'min_a': '1000', 'max_a': '100000000' },
{ 'pair': 'ETH/KZT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '4', 'max_p': '40000000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDT/KZT', 'min_q': '1', 'max_q': '500000', 'min_p': '200', 'max_p': '4000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ONT/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '15000000' },
{ 'pair': 'ONT/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONT/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '6000000' },
{ 'pair': 'ONT/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '250000' },
{ 'pair': 'ONG/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '15000000' },
{ 'pair': 'ONG/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONG/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '250000' },
{ 'pair': 'ONG/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '6000000' },
{ 'pair': 'CRON/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'CRON/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'CRON/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'CRON/EXM', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'BTC/RUB', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '2000000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'BTC/EUR', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'BTC/UAH', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '15000000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BTC/GBP', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '30000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'ETH/GBP', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'XRP/GBP', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'USDT/GBP', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'BTC/PLN', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '20000000', 'min_a': '50', 'max_a': '2000000' },
{ 'pair': 'BTC/TRY', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1', 'max_p': '800000', 'min_a': '40', 'max_a': '6000000' },
{ 'pair': 'BTC/KZT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '1000', 'max_p': '12000000', 'min_a': '1000', 'max_a': '100000000' },
{ 'pair': 'BTC/USDT', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.01', 'max_p': '30000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/BTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETH/USD', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/RUB', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '100000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'XRP/BTC', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'XRP/USD', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/RUB', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'ADA/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ADA/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'ADA/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' },
{ 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' },
{ 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '10000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ALGO/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'ALGO/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'ALGO/EUR', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'ATOM/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'ATOM/EUR', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'BCH/USD', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '30000', 'min_a': '0.0001', 'max_a': '500000' },
{ 'pair': 'BCH/RUB', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '2000000', 'min_a': '0.0001', 'max_a': '50000000' },
{ 'pair': 'BCH/EUR', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BCH/UAH', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.1', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'BCH/BTC', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '5', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'BCH/ETH', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.0000001', 'max_p': '200', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'BCH/USDT', 'min_q': '0.003', 'max_q': '5000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BTCZ/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'BTG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'BTG/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'BTG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'BTT/RUB', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/UAH', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'BTT/BTC', 'min_q': '1', 'max_q': '500000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'CRON/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'CRON/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'CRON/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'CRON/EXM', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'DAI/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DAI/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '0.5', 'max_a': '30000000' },
{ 'pair': 'DAI/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'DAI/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DASH/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'DASH/UAH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '200000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'DASH/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DASH/USDT', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.01', 'max_p': '5000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'DCR/RUB', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '3000000' },
{ 'pair': 'DCR/UAH', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.25', 'max_a': '1000000' },
{ 'pair': 'DCR/BTC', 'min_q': '0.01', 'max_q': '50000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '1000000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'DOGE/USD', 'min_q': '100', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'DOGE/BTC', 'min_q': '100', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'DXT/USD', 'min_q': '100', 'max_q': '50000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'DXT/BTC', 'min_q': '100', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'EOS/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/EUR', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'EOS/BTC', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETC/USD', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'ETC/RUB', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '0.01', 'max_a': '50000000' },
{ 'pair': 'ETC/BTC', 'min_q': '0.2', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '0.5', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETH/EUR', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/UAH', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '1000000', 'min_a': '90', 'max_a': '15000000' },
{ 'pair': 'ETH/PLN', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '2000000' },
{ 'pair': 'ETH/TRY', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.1', 'max_p': '80000', 'min_a': '10', 'max_a': '6000000' },
{ 'pair': 'ETH/KZT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '4', 'max_p': '40000000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/USDT', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'ETH/LTC', 'min_q': '0.001', 'max_q': '5000', 'min_p': '0.00000001', 'max_p': '100000', 'min_a': '0.05', 'max_a': '100000' },
{ 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' },
{ 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' },
{ 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETZ/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '10' },
{ 'pair': 'ETZ/ETH', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ETZ/USDT', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '1000' },
{ 'pair': 'EXM/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'EXM/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'GAS/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'GAS/BTC', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GNT/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'GNT/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'GUSD/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
@@ -197,84 +219,94 @@ module.exports = class exmo extends Exchange {
{ 'pair': 'KICK/USDT', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'KICK/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'KICK/ETH', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '5000' },
{ 'pair': 'LSK/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'LSK/RUB', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' },
{ 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/UAH', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '5', 'max_a': '18000000' },
{ 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'LSK/USD', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.1', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'LSK/RUB', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '50000000' },
{ 'pair': 'LSK/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.0015', 'max_a': '100' },
{ 'pair': 'LTC/USD', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/RUB', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '150', 'max_a': '50000000' },
{ 'pair': 'LTC/EUR', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'LTC/UAH', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.01', 'max_p': '300000', 'min_a': '5', 'max_a': '18000000' },
{ 'pair': 'LTC/BTC', 'min_q': '0.05', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'MKR/BTC', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '100', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MKR/DAI', 'min_q': '0.0001', 'max_q': '1000', 'min_p': '0.5', 'max_p': '500000', 'min_a': '0.005', 'max_a': '500000' },
{ 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' },
{ 'pair': 'MNC/USD', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'MNC/BTC', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '100' },
{ 'pair': 'MNC/ETH', 'min_q': '10', 'max_q': '500000000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '1000' },
{ 'pair': 'MNX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'MNX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'MNX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' },
{ 'pair': 'NEO/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'OMG/BTC', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'PTI/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '600000', 'min_a': '10', 'max_a': '600000' },
{ 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
{ 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' },
{ 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'NEO/USD', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.01', 'max_p': '50000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'NEO/RUB', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1500000', 'min_a': '50', 'max_a': '50000000' },
{ 'pair': 'NEO/BTC', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/USD', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'OMG/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'OMG/ETH', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
{ 'pair': 'ONG/EXM', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '15000000' },
{ 'pair': 'ONG/BTC', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONG/RUB', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '250000' },
{ 'pair': 'ONG/UAH', 'min_q': '1', 'max_q': '1000000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '50', 'max_a': '6000000' },
{ 'pair': 'ONT/EXM', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '15000000' },
{ 'pair': 'ONT/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'ONT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '100', 'max_a': '6000000' },
{ 'pair': 'ONT/UAH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '100000', 'min_a': '200', 'max_a': '250000' },
{ 'pair': 'PTI/RUB', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '600000', 'min_a': '10', 'max_a': '600000' },
{ 'pair': 'PTI/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.000001', 'max_a': '10' },
{ 'pair': 'PTI/EOS', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.0000001', 'max_p': '5000', 'min_a': '0.01', 'max_a': '20000' },
{ 'pair': 'PTI/USDT', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '10000', 'min_a': '0.01', 'max_a': '100000' },
{ 'pair': 'QTUM/USD', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '10000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'QTUM/BTC', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'QTUM/ETH', 'min_q': '0.1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'ROOBEE/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'TRX/USD', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'TRX/RUB', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'SMART/USD', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/RUB', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.0001', 'max_p': '100000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'SMART/EUR', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.000001', 'max_p': '1000', 'min_a': '1', 'max_a': '500000' },
{ 'pair': 'SMART/BTC', 'min_q': '10', 'max_q': '100000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'TRX/USD', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'TRX/EUR', 'min_q': '0.01', 'max_q': '50000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'TRX/RUB', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/UAH', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.000001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'TRX/BTC', 'min_q': '1', 'max_q': '50000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'USDC/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDC/BTC', 'min_q': '1', 'max_q': '500000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'USDC/ETH', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '1000' },
{ 'pair': 'USDC/USDT', 'min_q': '1', 'max_q': '500000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '3', 'max_a': '500000' },
{ 'pair': 'USDT/USD', 'min_q': '1', 'max_q': '500000', 'min_p': '0.5', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/RUB', 'min_q': '1', 'max_q': '500000', 'min_p': '0.01', 'max_p': '1000', 'min_a': '10', 'max_a': '50000000' },
{ 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '100000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '100000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' },
{ 'pair': 'USDT/EUR', 'min_q': '0.01', 'max_q': '500000', 'min_p': '0.1', 'max_p': '10', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'USDT/UAH', 'min_q': '0.01', 'max_q': '500000', 'min_p': '1', 'max_p': '3000', 'min_a': '2', 'max_a': '15000000' },
{ 'pair': 'USDT/KZT', 'min_q': '1', 'max_q': '500000', 'min_p': '200', 'max_p': '4000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'VLX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '0.1', 'min_a': '0.00001', 'max_a': '100' },
{ 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' },
{ 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '5000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' },
{ 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' },
{ 'pair': 'XLM/BTC', 'min_q': '1', 'max_q': '200000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/RUB', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '600000', 'min_a': '10', 'max_a': '16000000' },
{ 'pair': 'XMR/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/UAH', 'min_q': '0.01', 'max_q': '200000', 'min_p': '0.001', 'max_p': '300000', 'min_a': '5', 'max_a': '16000000' },
{ 'pair': 'XMR/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/ETH', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' },
{ 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' },
{ 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '100000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'WAVES/USD', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.001', 'max_p': '3500', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'WAVES/RUB', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.01', 'max_p': '10000', 'min_a': '1', 'max_a': '50000000' },
{ 'pair': 'WAVES/BTC', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.000001', 'max_p': '1', 'min_a': '0.0001', 'max_a': '100' },
{ 'pair': 'WAVES/ETH', 'min_q': '0.5', 'max_q': '500000', 'min_p': '0.00001', 'max_p': '30', 'min_a': '0.0035', 'max_a': '3500' },
{ 'pair': 'XEM/USD', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/EUR', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XEM/UAH', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.0001', 'max_p': '30000', 'min_a': '10', 'max_a': '15000000' },
{ 'pair': 'XEM/BTC', 'min_q': '10', 'max_q': '10000000', 'min_p': '0.0000001', 'max_p': '1', 'min_a': '0.00015', 'max_a': '100' },
{ 'pair': 'XLM/USD', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '500000' },
{ 'pair': 'XLM/RUB', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'XLM/TRY', 'min_q': '0.01', 'max_q': '5000000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '6000000' },
{ 'pair': 'XLM/BTC', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '600000', 'min_a': '10', 'max_a': '16000000' },
{ 'pair': 'XMR/EUR', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'XMR/UAH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '300000', 'min_a': '5', 'max_a': '16000000' },
{ 'pair': 'XMR/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.0001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'XMR/ETH', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.00000001', 'max_p': '100', 'min_a': '0.001', 'max_a': '5000' },
{ 'pair': 'XRP/EUR', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/TRY', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '6000000' },
{ 'pair': 'XRP/UAH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.01', 'max_a': '15000000' },
{ 'pair': 'XRP/USDT', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.001', 'max_p': '1000', 'min_a': '0.001', 'max_a': '500000' },
{ 'pair': 'XRP/ETH', 'min_q': '1', 'max_q': '5000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.00001', 'max_a': '5000' },
{ 'pair': 'XTZ/USD', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '100000' },
{ 'pair': 'XTZ/RUB', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00001', 'max_p': '100000', 'min_a': '0.5', 'max_a': '500000' },
{ 'pair': 'XTZ/BTC', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.00001', 'max_a': '10' },
{ 'pair': 'XTZ/ETH', 'min_q': '0.1', 'max_q': '100000', 'min_p': '0.0000001', 'max_p': '10', 'min_a': '0.0001', 'max_a': '1000' },
{ 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '1000', 'min_p': '0.00001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZEC/USD', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/RUB', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '100000', 'min_a': '0.1', 'max_a': '50000000' },
{ 'pair': 'ZEC/EUR', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.001', 'max_p': '5000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZEC/BTC', 'min_q': '0.01', 'max_q': '10000', 'min_p': '0.00001', 'max_p': '10', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZRX/USD', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00001', 'max_p': '1000', 'min_a': '0.1', 'max_a': '500000' },
{ 'pair': 'ZRX/BTC', 'min_q': '1', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '1', 'min_a': '0.001', 'max_a': '100' },
{ 'pair': 'ZRX/ETH', 'min_q': '0.01', 'max_q': '10000000', 'min_p': '0.00000001', 'max_p': '10', 'min_a': '0.01', 'max_a': '5000' },
@@ -337,6 +369,8 @@ module.exports = class exmo extends Exchange {
{ 'prov': 'CRON', 'dep': '0%', 'wd': '5 CRON' },
{ 'prov': 'ONT', 'dep': '0%', 'wd': '1 ONT' },
{ 'prov': 'ONG', 'dep': '0%', 'wd': '5 ONG' },
{ 'prov': 'ALGO', 'dep': '0%', 'wd': '0.01 ALGO' },
{ 'prov': 'ATOM', 'dep': '0%', 'wd': '' },
],
},
{
@@ -345,9 +379,10 @@ module.exports = class exmo extends Exchange {
'items': [
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Payeer', 'dep': '3.95%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.99%' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.49%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 0.5 USD', 'wd': '-' },
{ 'prov': 'Visa', 'dep': '3.45%', 'wd': '-' },
{ 'prov': 'Frick Bank', 'dep': '0 USD', 'wd': '-' },
],
},
{
@@ -355,22 +390,24 @@ module.exports = class exmo extends Exchange {
'title': 'EUR',
'items': [
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 0.5 EUR', 'wd': '-' },
{ 'prov': 'Visa/MasterCard', 'dep': '4.5% + 0.5 EUR', 'wd': '-' },
{ 'prov': 'Visa', 'dep': '2.95%', 'wd': '-' },
{ 'prov': 'SEPA', 'dep': '10 EUR', 'wd': '50 EUR' },
{ 'prov': 'Frick Internal Transfer', 'dep': '0 EUR', 'wd': '-' },
{ 'prov': 'SEPA Frick Bank', 'dep': '0 EUR', 'wd': '-' },
{ 'prov': 'WIRE Frick Bank', 'dep': '0%', 'wd': '-' },
{ 'prov': 'SEPA Weg Ag', 'dep': '-', 'wd': '1 EUR' },
],
},
{
'group': 'rub',
'title': 'RUB',
'items': [
{ 'prov': 'Payeer', 'dep': '2.49%', 'wd': '-' },
{ 'prov': 'Payeer', 'dep': '2.49%', 'wd': '3.99%' },
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Qiwi', 'dep': '0.99%', 'wd': '2%' },
{ 'prov': 'Yandex Money', 'dep': '1.95%', 'wd': '-' },
{ 'prov': 'Enfins', 'dep': '0%', 'wd': '1.49%' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '2.49%' },
{ 'prov': 'Visa/MasterCard', 'dep': '1.99%', 'wd': '4.49% + 60 RUB' },
{ 'prov': 'Qiwi', 'dep': '1.49%', 'wd': '2.49%' },
{ 'prov': 'Yandex Money', 'dep': '1.49%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '3.49%' },
{ 'prov': 'Visa/MasterCard', 'dep': '2.99%', 'wd': '3.99% + 60 RUB' },
],
},
{
@@ -378,7 +415,6 @@ module.exports = class exmo extends Exchange {
'title': 'PLN',
'items': [
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 1 PLN', 'wd': '-' },
],
},
{
@@ -388,6 +424,7 @@ module.exports = class exmo extends Exchange {
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa', 'dep': '3.05%', 'wd': '-' },
{ 'prov': 'Visa/MasterCard (Simplex)', 'dep': '4.5% + 2 TRY', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '-' },
],
},
{
@@ -396,17 +433,18 @@ module.exports = class exmo extends Exchange {
'items': [
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Terminal', 'dep': '2.6%', 'wd': '-' },
{ 'prov': 'Enfins', 'dep': '0%', 'wd': '1.95%' },
{ 'prov': 'Visa/MasterCard', 'dep': '1% + 5 UAH', 'wd': '2.99%' },
{ 'prov': 'Visa/MasterCard', 'dep': '1% + 5 UAH', 'wd': '-' },
{ 'prov': 'Visa/MasterCard EasyTransfer', 'dep': '-', 'wd': '2.99%' },
],
},
{
'group': 'kzt',
'title': 'KZT',
'items': [
{ 'prov': 'EX-CODE', 'dep': '', 'wd': '0.2%' },
{ 'prov': 'Visa/MasterCard RoyalPay', 'dep': '-', 'wd': '2.99% + 450 KZT' },
{ 'prov': 'Visa/MasterCard', 'dep': '3.5%', 'wd': '-' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '1.49%' },
{ 'prov': 'AdvCash', 'dep': '0%', 'wd': '-' },
],
},
],
@@ -420,6 +458,7 @@ module.exports = class exmo extends Exchange {
'40015': ExchangeError, // API function do not exist
'40016': OnMaintenance, // {"result":false,"error":"Error 40016: Maintenance work in progress"}
'40017': AuthenticationError, // Wrong API Key
'40032': PermissionDenied, // {"result":false,"error":"Error 40032: Access is denied for this API key"}
'40034': RateLimitExceeded, // {"result":false,"error":"Error 40034: Access is denied, rate limit is exceeded"}
'50052': InsufficientFunds,
'50054': InsufficientFunds,
@@ -429,6 +468,8 @@ module.exports = class exmo extends Exchange {
'50321': InvalidOrder, // Price by order is more than permissible maximum for this pair
},
'broad': {
'range period is too long': BadRequest,
'invalid syntax': BadRequest,
'API rate limit exceeded': RateLimitExceeded, // {"result":false,"error":"API rate limit exceeded for 99.33.55.224. Retry after 60 sec.","history":[],"begin":1579392000,"end":1579478400}
},
},
@@ -590,8 +631,21 @@ module.exports = class exmo extends Exchange {
}
async fetchMarkets (params = {}) {
const fees = await this.fetchTradingFees ();
const response = await this.publicGetPairSettings (params);
//
// {
// "EXM_ETH": {
// "min_quantity": "1",
// "max_quantity": "1000",
// "min_price": "1",
// "max_price": "1000",
// "max_amount": "1000",
// "min_amount": "1",
// "commission_taker_percent": "0.2",
// "commission_maker_percent": "0.2"
// },
// }
//
const keys = Object.keys (response);
const result = [];
for (let i = 0; i < keys.length; i++) {
@@ -601,6 +655,8 @@ module.exports = class exmo extends Exchange {
const [ baseId, quoteId ] = symbol.split ('/');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const taker = this.safeFloat (market, 'commission_taker_percent');
const maker = this.safeFloat (market, 'commission_maker_percent');
result.push ({
'id': id,
'symbol': symbol,
@@ -609,8 +665,8 @@ module.exports = class exmo extends Exchange {
'baseId': baseId,
'quoteId': quoteId,
'active': true,
'taker': fees['taker'],
'maker': fees['maker'],
'taker': taker / 100,
'maker': maker / 100,
'limits': {
'amount': {
'min': this.safeFloat (market, 'min_quantity'),
@@ -635,6 +691,74 @@ module.exports = class exmo extends Exchange {
return result;
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
'resolution': this.timeframes[timeframe],
};
const options = this.safeValue (this.options, 'fetchOHLCV');
const maxLimit = this.safeInteger (options, 'maxLimit', 3000);
const duration = this.parseTimeframe (timeframe);
const now = this.milliseconds ();
if (since === undefined) {
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument');
} else {
if (limit > maxLimit) {
throw new BadRequest (this.id + ' fetchOHLCV will serve ' + maxLimit.toString () + ' candles at most');
}
request['from'] = parseInt (now / 1000) - limit * duration - 1;
request['to'] = parseInt (now / 1000);
}
} else {
request['from'] = parseInt (since / 1000) - 1;
if (limit === undefined) {
request['to'] = parseInt (now / 1000);
} else {
if (limit > maxLimit) {
throw new BadRequest (this.id + ' fetchOHLCV will serve ' + maxLimit.toString () + ' candles at most');
}
const to = this.sum (since, limit * duration * 1000);
request['to'] = parseInt (to / 1000);
}
}
const response = await this.publicGetCandlesHistory (this.extend (request, params));
//
// {
// "candles":[
// {"t":1584057600000,"o":0.02235144,"c":0.02400233,"h":0.025171,"l":0.02221,"v":5988.34031761},
// {"t":1584144000000,"o":0.0240373,"c":0.02367413,"h":0.024399,"l":0.0235,"v":2027.82522329},
// {"t":1584230400000,"o":0.02363458,"c":0.02319242,"h":0.0237948,"l":0.02223196,"v":1707.96944997},
// ]
// }
//
const candles = this.safeValue (response, 'candles', []);
return this.parseOHLCVs (candles, market, timeframe, since, limit);
}
parseOHLCV (ohlcv, market = undefined, timeframe = '5m', since = undefined, limit = undefined) {
//
// {
// "t":1584057600000,
// "o":0.02235144,
// "c":0.02400233,
// "h":0.025171,
// "l":0.02221,
// "v":5988.34031761
// }
//
return [
this.safeInteger (ohlcv, 't'),
this.safeFloat (ohlcv, 'o'),
this.safeFloat (ohlcv, 'h'),
this.safeFloat (ohlcv, 'l'),
this.safeFloat (ohlcv, 'c'),
this.safeFloat (ohlcv, 'v'),
];
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostUserInfo (params);
@@ -758,8 +882,36 @@ module.exports = class exmo extends Exchange {
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// "trade_id":165087520,
// "date":1587470005,
// "type":"buy",
// "quantity":"1.004",
// "price":"0.02491461",
// "amount":"0.02501426"
// },
//
// fetchMyTrades, fetchOrderTrades
//
// {
// "trade_id": 3,
// "date": 1435488248,
// "type": "buy",
// "pair": "BTC_USD",
// "order_id": 12345,
// "quantity": 1,
// "price": 100,
// "amount": 100,
// "exec_type": "taker",
// "commission_amount": "0.02",
// "commission_currency": "BTC",
// "commission_percent": "0.2"
// }
//
const timestamp = this.safeTimestamp (trade, 'date');
let fee = undefined;
let symbol = undefined;
const id = this.safeString (trade, 'trade_id');
const orderId = this.safeString (trade, 'order_id');
@@ -768,24 +920,35 @@ module.exports = class exmo extends Exchange {
const cost = this.safeFloat (trade, 'amount');
const side = this.safeString (trade, 'type');
const type = undefined;
if (market !== undefined) {
const marketId = this.safeString (trade, 'pair');
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'];
if (market['taker'] !== market['maker']) {
throw new ExchangeError (this.id + ' parseTrade can not deduce proper fee costs, taker and maker fees now differ');
}
if ((side === 'buy') && (amount !== undefined)) {
fee = {
'currency': market['base'],
'cost': amount * market['taker'],
'rate': market['taker'],
};
} else if ((side === 'sell') && (cost !== undefined)) {
fee = {
'currency': market['quote'],
'cost': cost * market['taker'],
'rate': market['taker'],
};
}
const takerOrMaker = this.safeString (trade, 'exec_type');
let fee = undefined;
const feeCost = this.safeFloat (trade, 'commission_amount');
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'commission_currency');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
let feeRate = this.safeFloat (trade, 'commission_percent');
if (feeRate !== undefined) {
feeRate /= 1000;
}
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
'rate': feeRate,
};
}
return {
'id': id,
@@ -796,7 +959,7 @@ module.exports = class exmo extends Exchange {
'order': orderId,
'type': type,
'side': side,
'takerOrMaker': undefined,
'takerOrMaker': takerOrMaker,
'price': price,
'amount': amount,
'cost': cost,
@@ -811,6 +974,28 @@ module.exports = class exmo extends Exchange {
'pair': market['id'],
};
const response = await this.publicGetTrades (this.extend (request, params));
//
// {
// "ETH_BTC":[
// {
// "trade_id":165087520,
// "date":1587470005,
// "type":"buy",
// "quantity":"1.004",
// "price":"0.02491461",
// "amount":"0.02501426"
// },
// {
// "trade_id":165087369,
// "date":1587469938,
// "type":"buy",
// "quantity":"0.94",
// "price":"0.02492348",
// "amount":"0.02342807"
// }
// ]
// }
//
const data = this.safeValue (response, market['id'], []);
return this.parseTrades (data, market, since, limit);
}
@@ -968,6 +1153,31 @@ module.exports = class exmo extends Exchange {
'order_id': id.toString (),
};
const response = await this.privatePostOrderTrades (this.extend (request, params));
//
// {
// "type": "buy",
// "in_currency": "BTC",
// "in_amount": "1",
// "out_currency": "USD",
// "out_amount": "100",
// "trades": [
// {
// "trade_id": 3,
// "date": 1435488248,
// "type": "buy",
// "pair": "BTC_USD",
// "order_id": 12345,
// "quantity": 1,
// "price": 100,
// "amount": 100,
// "exec_type": "taker",
// "commission_amount": "0.02",
// "commission_currency": "BTC",
// "commission_percent": "0.2"
// }
// ]
// }
//
const trades = this.safeValue (response, 'trades');
return this.parseTrades (trades, market, since, limit);
}
@@ -1415,9 +1625,10 @@ module.exports = class exmo extends Exchange {
if (response === undefined) {
return; // fallback to default error handler
}
if ('result' in response) {
if (('result' in response) || ('errmsg' in response)) {
//
// {"result":false,"error":"Error 50052: Insufficient funds"}
// {"s":"error","errmsg":"strconv.ParseInt: parsing \"\": invalid syntax"}
//
let success = this.safeValue (response, 'result', false);
if (typeof success === 'string') {
@@ -1429,7 +1640,7 @@ module.exports = class exmo extends Exchange {
}
if (!success) {
let code = undefined;
const message = this.safeString (response, 'error');
const message = this.safeString2 (response, 'error', 'errmsg');
const errorParts = message.split (':');
const numParts = errorParts.length;
if (numParts > 1) {
+4
View File
@@ -580,6 +580,7 @@ module.exports = class ftx extends Exchange {
// {
// "fee": 20.1374935,
// "feeRate": 0.0005,
// "feeCurrency": "USD",
// "future": "EOS-0329",
// "id": 11215,
// "liquidity": "taker",
@@ -626,8 +627,11 @@ module.exports = class ftx extends Exchange {
let fee = undefined;
const feeCost = this.safeFloat (trade, 'fee');
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'feeCurrency');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
'rate': this.safeFloat (trade, 'feeRate'),
};
}
+94 -25
View File
@@ -164,6 +164,26 @@ module.exports = class gateio extends Exchange {
async fetchMarkets (params = {}) {
const response = await this.publicGetMarketinfo (params);
//
// {
// "result":"true",
// "pairs":[
// {
// "usdt_cnyx":{
// "decimal_places":3,
// "amount_decimal_places":3,
// "min_amount":1,
// "min_amount_a":1,
// "min_amount_b":3,
// "fee":0.02,
// "trade_disabled":0,
// "buy_disabled":0,
// "sell_disabled":0
// }
// },
// ]
// }
//
const markets = this.safeValue (response, 'pairs');
if (!markets) {
throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response');
@@ -190,14 +210,14 @@ module.exports = class gateio extends Exchange {
const symbol = base + '/' + quote;
const precision = {
'amount': 8,
'price': details['decimal_places'],
'price': this.safeInteger (details, 'decimal_places'),
};
const amountLimits = {
'min': details['min_amount'],
'min': this.safeFloat (details, 'min_amount'),
'max': undefined,
};
const priceLimits = {
'min': Math.pow (10, -details['decimal_places']),
'min': Math.pow (10, -precision['price']),
'max': undefined,
};
const defaultCost = amountLimits['min'] * priceLimits['min'];
@@ -211,9 +231,13 @@ module.exports = class gateio extends Exchange {
'price': priceLimits,
'cost': costLimits,
};
const active = true;
const disabled = this.safeValue (details, 'trade_disabled');
const active = !disabled;
const uppercaseId = id.toUpperCase ();
const fee = this.safeFloat (details, 'fee');
result.push ({
'id': id,
'uppercaseId': uppercaseId,
'symbol': symbol,
'base': base,
'quote': quote,
@@ -221,8 +245,8 @@ module.exports = class gateio extends Exchange {
'quoteId': quoteId,
'info': market,
'active': active,
'maker': details['fee'] / 100,
'taker': details['fee'] / 100,
'maker': fee / 100,
'taker': fee / 100,
'precision': precision,
'limits': limits,
});
@@ -484,22 +508,64 @@ module.exports = class gateio extends Exchange {
parseOrder (order, market = undefined) {
//
// {'amount': '0.00000000',
// 'currencyPair': 'xlm_usdt',
// 'fee': '0.0113766632239302 USDT',
// 'feeCurrency': 'USDT',
// 'feePercentage': 0.18,
// 'feeValue': '0.0113766632239302',
// 'filledAmount': '30.14004987',
// 'filledRate': 0.2097,
// 'initialAmount': '30.14004987',
// 'initialRate': '0.2097',
// 'left': 0,
// 'orderNumber': '998307286',
// 'rate': '0.2097',
// 'status': 'closed',
// 'timestamp': 1531158583,
// 'type': 'sell'},
// createOrder
//
// {
// "fee": "0 ZEC",
// "code": 0,
// "rate": "0.0055",
// "side": 2,
// "type": "buy",
// "ctime": 1586460839.138,
// "market": "ZEC_BTC",
// "result": "true",
// "status": "open",
// "iceberg": "0",
// "message": "Success",
// "feeValue": "0",
// "filledRate": "0.005500000",
// "leftAmount": "0.60607456",
// "feeCurrency": "ZEC",
// "orderNumber": 10755887009,
// "filledAmount": "0",
// "feePercentage": 0.002,
// "initialAmount": "0.60607456"
// }
//
// {
// 'amount': '0.00000000',
// 'currencyPair': 'xlm_usdt',
// 'fee': '0.0113766632239302 USDT',
// 'feeCurrency': 'USDT',
// 'feePercentage': 0.18,
// 'feeValue': '0.0113766632239302',
// 'filledAmount': '30.14004987',
// 'filledRate': 0.2097,
// 'initialAmount': '30.14004987',
// 'initialRate': '0.2097',
// 'left': 0,
// 'orderNumber': '998307286',
// 'rate': '0.2097',
// 'status': 'closed',
// 'timestamp': 1531158583,
// 'type': 'sell'
// }
//
// {
// "orderNumber": 10802237760,
// "orderType": 1,
// "type": "buy",
// "rate": "0.54250000",
// "amount": "45.55638518",
// "total": "24.71433896",
// "initialRate": "0.54250000",
// "initialAmount": "45.55638518",
// "filledRate": "0.54250000",
// "filledAmount": "0",
// "currencyPair": "nano_usdt",
// "timestamp": 1586556143,
// "status": "open"
// }
//
const id = this.safeString2 (order, 'orderNumber', 'id');
let symbol = undefined;
@@ -520,12 +586,15 @@ module.exports = class gateio extends Exchange {
} else if (side === '2') {
side = 'buy';
}
const price = this.safeFloat2 (order, 'initialRate', 'price');
const price = this.safeFloat2 (order, 'initialRate', 'rate');
const average = this.safeFloat (order, 'filledRate');
const amount = this.safeFloat2 (order, 'initialAmount', 'amount');
const filled = this.safeFloat (order, 'filledAmount');
// In the order status response, this field has a different name.
const remaining = this.safeFloat2 (order, 'leftAmount', 'left');
let remaining = this.safeFloat2 (order, 'leftAmount', 'left');
if (remaining === undefined) {
remaining = amount - filled;
}
const feeCost = this.safeFloat (order, 'feeValue');
const feeCurrencyId = this.safeString (order, 'feeCurrency');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
@@ -651,7 +720,7 @@ module.exports = class gateio extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires symbol param');
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
+60 -54
View File
@@ -180,8 +180,7 @@ module.exports = class gemini extends Exchange {
if (numTables < 2) {
throw new NotSupported (error);
}
// tables[1] = tables[1].replace ("\n", ''); // eslint-disable-line quotes
const rows = tables[1].split ("<tr>\n"); // eslint-disable-line quotes
const rows = tables[1].split ("\n<tr>\n"); // eslint-disable-line quotes
const numRows = rows.length;
if (numRows < 2) {
throw new NotSupported (error);
@@ -192,66 +191,73 @@ module.exports = class gemini extends Exchange {
const row = rows[i];
const cells = row.split ("</td>\n"); // eslint-disable-line quotes
const numCells = cells.length;
if (numCells < 7) {
if (numCells < 9) {
throw new NotSupported (error);
}
//
// [
// '<td><code class="prettyprint">btcusd</code>',
// '<td>USD', // quote
// '<td>BTC', // base
// '<td>0.00001 BTC (1e-5)', // min amount
// '<td>0.00000001 BTC (1e-8)', // amount min tick size
// '<td>0.01 USD', // price min tick size
// '</tr>\n'
// '<td>BTC', // currency
// '<td>0.00001 BTC (1e-5)', // min order size
// '<td>0.00000001 BTC (1e-8)', // tick size
// '<td>0.01 USD', // usd price increment
// '<td>N/A', // btc price increment
// '<td>0.0001 ETH (1e-4)', // eth price increment
// '<td>0.0001 BCH (1e-4)', // bch price increment
// '<td>0.001 LTC (1e-3)', // ltc price increment
// '</tr>'
// ]
//
let id = cells[0].replace ('<td>', '');
id = id.replace ('<code class="prettyprint">', '');
id = id.replace ('</code>', '');
let baseId = cells[2].replace ('<td>', '');
let quoteId = cells[1].replace ('<td>', '');
const minAmountAsString = cells[3].replace ('<td>', '');
const amountTickSizeAsString = cells[4].replace ('<td>', '');
const priceTickSizeAsString = cells[5].replace ('<td>', '');
const minAmount = minAmountAsString.split (' ');
const amountPrecision = amountTickSizeAsString.split (' ');
const pricePrecision = priceTickSizeAsString.split (' ');
baseId = baseId.toLowerCase ();
quoteId = quoteId.toLowerCase ();
const uppercaseBaseId = cells[0].replace ('<td>', '');
const baseId = uppercaseBaseId.toLowerCase ();
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'amount': this.precisionFromString (amountPrecision[0]),
'price': this.precisionFromString (pricePrecision[0]),
};
const active = undefined;
result.push ({
'id': id,
'info': row,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'precision': precision,
'limits': {
'amount': {
'min': parseFloat (minAmount[0]),
'max': undefined,
const quoteIds = [ 'usd', 'btc', 'eth', 'bch', 'ltc' ];
const minAmountString = cells[1].replace ('<td>', '');
const minAmountParts = minAmountString.split (' ');
const minAmount = this.safeFloat (minAmountParts, 0);
const amountPrecisionString = cells[2].replace ('<td>', '');
const amountPrecisionParts = amountPrecisionString.split (' ');
const amountPrecision = this.precisionFromString (amountPrecisionParts[0]);
for (let j = 0; j < quoteIds.length; j++) {
const quoteId = quoteIds[j];
const quote = this.safeCurrencyCode (quoteId);
const pricePrecisionIndex = this.sum (3, j);
const pricePrecisionString = cells[pricePrecisionIndex].replace ('<td>', '');
if (pricePrecisionString === 'N/A') {
continue;
}
const pricePrecisionParts = pricePrecisionString.split (' ');
const pricePrecision = this.precisionFromString (pricePrecisionParts[0]);
const symbol = base + '/' + quote;
const id = baseId + quoteId;
const active = undefined;
result.push ({
'id': id,
'info': row,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'precision': {
'amount': amountPrecision,
'price': pricePrecision,
},
'price': {
'min': undefined,
'max': undefined,
'limits': {
'amount': {
'min': minAmount,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'cost': {
'min': undefined,
'max': undefined,
},
},
});
});
}
}
return result;
}
+2019
View File
File diff suppressed because it is too large. Load diff
+6 -7
View File
@@ -730,14 +730,13 @@ module.exports = class hitbtc extends Exchange {
}
parseOHLCV (ohlcv, market = undefined, timeframe = '1d', since = undefined, limit = undefined) {
const timestamp = this.parse8601 (ohlcv['timestamp']);
return [
timestamp,
parseFloat (ohlcv['open']),
parseFloat (ohlcv['max']),
parseFloat (ohlcv['min']),
parseFloat (ohlcv['close']),
parseFloat (ohlcv['volume']),
this.parse8601 (this.safeString (ohlcv, 'timestamp')),
this.safeFloat (ohlcv, 'open'),
this.safeFloat (ohlcv, 'max'),
this.safeFloat (ohlcv, 'min'),
this.safeFloat (ohlcv, 'close'),
this.safeFloat (ohlcv, 'volume'),
];
}
-17
View File
@@ -1,17 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const hitbtc = require ('./hitbtc');
// ---------------------------------------------------------------------------
module.exports = class hitbtc2 extends hitbtc {
describe () {
// this is a temporary stub for backward compatibility
// https://github.com/ccxt/ccxt/issues/6678
return this.deepExtend (super.describe (), {
'id': 'hitbtc2',
});
}
};
+27 -1
View File
@@ -163,12 +163,14 @@ module.exports = class huobipro extends Exchange {
'exceptions': {
'exact': {
// err-code
'bad-request': BadRequest,
'api-not-support-temp-addr': PermissionDenied, // {"status":"error","err-code":"api-not-support-temp-addr","err-msg":"API withdrawal does not support temporary addresses","data":null}
'timeout': RequestTimeout, // {"ts":1571653730865,"status":"error","err-code":"timeout","err-msg":"Request Timeout"}
'gateway-internal-error': ExchangeNotAvailable, // {"status":"error","err-code":"gateway-internal-error","err-msg":"Failed to load data. Try again later.","data":null}
'account-frozen-balance-insufficient-error': InsufficientFunds, // {"status":"error","err-code":"account-frozen-balance-insufficient-error","err-msg":"trade account balance is not enough, left: `0.0027`","data":null}
'invalid-amount': InvalidOrder, // eg "Paramemter `amount` is invalid."
'order-limitorder-amount-min-error': InvalidOrder, // limit order amount error, min: `0.001`
'order-limitorder-amount-max-error': InvalidOrder, // market order amount error, max: `1000000`
'order-marketorder-amount-min-error': InvalidOrder, // market order amount error, min: `0.01`
'order-limitorder-price-min-error': InvalidOrder, // limit order price error
'order-limitorder-price-max-error': InvalidOrder, // limit order price error
@@ -331,6 +333,8 @@ module.exports = class huobipro extends Exchange {
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker
//
// {
// "amount": 26228.672978342216,
@@ -346,25 +350,47 @@ module.exports = class huobipro extends Exchange {
// "bid": [ 9146.86, 0.080758 ],
// }
//
// fetchTickers
// {
// symbol: "bhdht",
// open: 2.3938,
// high: 2.4151,
// low: 2.3323,
// close: 2.3909,
// amount: 628.992,
// vol: 1493.71841095,
// count: 2088,
// bid: 2.3643,
// bidSize: 0.7136,
// ask: 2.4061,
// askSize: 0.4156
// }
//
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeInteger (ticker, 'ts');
let bid = undefined;
let ask = undefined;
let bidVolume = undefined;
let ask = undefined;
let askVolume = undefined;
if ('bid' in ticker) {
if (Array.isArray (ticker['bid'])) {
bid = this.safeFloat (ticker['bid'], 0);
bidVolume = this.safeFloat (ticker['bid'], 1);
} else {
bid = this.safeFloat (ticker, 'bid');
bidVolume = this.safeValue (ticker, 'bidSize');
}
}
if ('ask' in ticker) {
if (Array.isArray (ticker['ask'])) {
ask = this.safeFloat (ticker['ask'], 0);
askVolume = this.safeFloat (ticker['ask'], 1);
} else {
ask = this.safeFloat (ticker, 'ask');
askVolume = this.safeValue (ticker, 'askSize');
}
}
const open = this.safeFloat (ticker, 'open');
+2 -2
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError } = require ('./base/errors');
const { ExchangeError, AuthenticationError, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -221,7 +221,7 @@ module.exports = class ice3x extends Exchange {
const type = this.safeString (params, 'type');
if ((type !== 'ask') && (type !== 'bid')) {
// eslint-disable-next-line quotes
throw new ExchangeError (this.id + " fetchOrderBook requires an exchange-specific extra 'type' param ('bid' or 'ask') when used with a limit");
throw new ArgumentsRequired (this.id + " fetchOrderBook requires an exchange-specific extra 'type' param ('bid' or 'ask') when used with a limit");
} else {
request['items_per_page'] = limit;
}
+3 -3
View File
@@ -362,7 +362,7 @@ module.exports = class indodax extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ExchangeError (this.id + ' fetchOrder requires a symbol');
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -409,7 +409,7 @@ module.exports = class indodax extends Exchange {
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ExchangeError (this.id + ' fetchOrders requires a symbol');
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
}
await this.loadMarkets ();
const request = {};
@@ -458,7 +458,7 @@ module.exports = class indodax extends Exchange {
}
const side = this.safeValue (params, 'side');
if (side === undefined) {
throw new ExchangeError (this.id + ' cancelOrder requires an extra "side" param');
throw new ArgumentsRequired (this.id + ' cancelOrder requires an extra "side" param');
}
await this.loadMarkets ();
const market = this.market (symbol);
+2 -2
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError } = require ('./base/errors');
const { ExchangeError, AuthenticationError, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -254,7 +254,7 @@ module.exports = class itbit extends Exchange {
await this.loadMarkets ();
const walletId = this.safeString (params, 'walletId');
if (walletId === undefined) {
throw new ExchangeError (this.id + ' fetchMyTrades requires a walletId parameter');
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a walletId parameter');
}
const request = {
'walletId': walletId,
+263 -32
View File
@@ -36,6 +36,7 @@ module.exports = class kraken extends Exchange {
'withdraw': true,
'fetchLedgerEntry': true,
'fetchLedger': true,
'fetchOrderTrades': 'emulated',
},
'marketsByAltname': {},
'timeframes': {
@@ -54,7 +55,7 @@ module.exports = class kraken extends Exchange {
'api': {
'public': 'https://api.kraken.com',
'private': 'https://api.kraken.com',
'zendesk': 'https://support.kraken.com/hc/en-us/articles',
'zendesk': 'https://kraken.zendesk.com/api/v2/help_center/en-us/articles', // use the public zendesk api to receive article bodies and bypass new anti-spam protections
},
'www': 'https://www.kraken.com',
'doc': 'https://www.kraken.com/features/api',
@@ -149,9 +150,9 @@ module.exports = class kraken extends Exchange {
'get': [
// we should really refrain from putting fixed fee numbers and stop hardcoding
// we will be using their web APIs to scrape all numbers from these articles
'205893708-What-is-the-minimum-order-size-',
'201396777-What-are-the-deposit-fees-',
'201893608-What-are-the-withdrawal-fees-',
'205893708', // -What-is-the-minimum-order-size-
'360000292886', // -What-are-the-deposit-fees-
'201893608', // -What-are-the-withdrawal-fees-
],
},
'public': {
@@ -234,12 +235,14 @@ module.exports = class kraken extends Exchange {
return this.decimalToPrecision (fee, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES);
}
async fetchMinOrderAmounts () {
const html = await this.zendeskGet205893708WhatIsTheMinimumOrderSize ();
async fetchMinOrderAmounts (params = {}) {
const response = await this.zendeskGet205893708 (params);
const article = this.safeValue (response, 'article');
const html = this.safeString (article, 'body');
const parts = html.split ('<td class="wysiwyg-text-align-right">');
const numParts = parts.length;
if (numParts < 3) {
throw new NotSupported (this.id + ' fetchMinOrderAmounts HTML page markup has changed: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-');
throw new NotSupported (this.id + ' fetchMinOrderAmounts HTML page markup has changed: https://kraken.zendesk.com/api/v2/help_center/en-us/articles/205893708');
}
const result = {};
// skip the part before the header and the header itself
@@ -262,6 +265,52 @@ module.exports = class kraken extends Exchange {
async fetchMarkets (params = {}) {
const response = await this.publicGetAssetPairs (params);
//
// {
// "error":[],
// "result":{
// "ADAETH":{
// "altname":"ADAETH",
// "wsname":"ADA\/ETH",
// "aclass_base":"currency",
// "base":"ADA",
// "aclass_quote":"currency",
// "quote":"XETH",
// "lot":"unit",
// "pair_decimals":7,
// "lot_decimals":8,
// "lot_multiplier":1,
// "leverage_buy":[],
// "leverage_sell":[],
// "fees":[
// [0,0.26],
// [50000,0.24],
// [100000,0.22],
// [250000,0.2],
// [500000,0.18],
// [1000000,0.16],
// [2500000,0.14],
// [5000000,0.12],
// [10000000,0.1]
// ],
// "fees_maker":[
// [0,0.16],
// [50000,0.14],
// [100000,0.12],
// [250000,0.1],
// [500000,0.08],
// [1000000,0.06],
// [2500000,0.04],
// [5000000,0.02],
// [10000000,0]
// ],
// "fee_volume_currency":"ZUSD",
// "margin_call":80,
// "margin_stop":40
// },
// }
// }
//
const fetchMinOrderAmounts = this.safeValue (this.options, 'fetchMinOrderAmounts', false);
let limits = {};
if (fetchMinOrderAmounts) {
@@ -441,6 +490,13 @@ module.exports = class kraken extends Exchange {
};
}
parseBidAsk (bidask, priceKey = 0, amountKey = 1) {
const price = this.safeFloat (bidask, priceKey);
const amount = this.safeFloat (bidask, amountKey);
const timestamp = this.safeInteger (bidask, 2);
return [ price, amount, timestamp ];
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
@@ -454,7 +510,27 @@ module.exports = class kraken extends Exchange {
request['count'] = limit; // 100
}
const response = await this.publicGetDepth (this.extend (request, params));
const orderbook = response['result'][market['id']];
//
// {
// "error":[],
// "result":{
// "XETHXXBT":{
// "asks":[
// ["0.023480","4.000",1586321307],
// ["0.023490","50.095",1586321306],
// ["0.023500","28.535",1586321302],
// ],
// "bids":[
// ["0.023470","59.580",1586321307],
// ["0.023460","20.000",1586321301],
// ["0.023440","67.832",1586321306],
// ]
// }
// }
// }
//
const result = this.safeValue (response, 'result', {});
const orderbook = this.safeValue (result, market['id']);
return this.parseOrderBook (orderbook);
}
@@ -708,26 +784,46 @@ module.exports = class kraken extends Exchange {
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// [
// "0.032310", // price
// "4.28169434", // amount
// 1541390792.763, // timestamp
// "s", // sell or buy
// "l", // limit or market
// ""
// ]
//
// fetchOrderTrades (private)
//
// {
// id: 'TIMIRG-WUNNE-RRJ6GT', // injected from outside
// ordertxid: 'OQRPN2-LRHFY-HIFA7D',
// postxid: 'TKH2SE-M7IF5-CFI7LT',
// pair: 'USDCUSDT',
// time: 1586340086.457,
// type: 'sell',
// ordertype: 'market',
// price: '0.99860000',
// cost: '22.16892001',
// fee: '0.04433784',
// vol: '22.20000000',
// margin: '0.00000000',
// misc: ''
// }
//
let timestamp = undefined;
let side = undefined;
let type = undefined;
let price = undefined;
let amount = undefined;
let cost = undefined;
let id = undefined;
let order = undefined;
let fee = undefined;
const marketId = this.safeString (trade, 'pair');
const foundMarket = this.findMarketByAltnameOrId (marketId);
let symbol = undefined;
if (foundMarket !== undefined) {
market = foundMarket;
} else if (marketId !== undefined) {
// delisted market ids go here
market = this.getDelistedMarketById (marketId);
}
if (market !== undefined) {
symbol = market['symbol'];
}
if (Array.isArray (trade)) {
timestamp = this.safeTimestamp (trade, 2);
side = (trade[3] === 's') ? 'sell' : 'buy';
@@ -738,17 +834,27 @@ module.exports = class kraken extends Exchange {
if (tradeLength > 6) {
id = this.safeString (trade, 6); // artificially added as per #1794
}
} else if (typeof trade === 'string') {
id = trade;
} else if ('ordertxid' in trade) {
const marketId = this.safeString (trade, 'pair');
const foundMarket = this.findMarketByAltnameOrId (marketId);
if (foundMarket !== undefined) {
market = foundMarket;
} else if (marketId !== undefined) {
// delisted market ids go here
market = this.getDelistedMarketById (marketId);
}
order = trade['ordertxid'];
id = this.safeString2 (trade, 'id', 'postxid');
timestamp = this.safeTimestamp (trade, 'time');
side = trade['type'];
type = trade['ordertype'];
side = this.safeString (trade, 'type');
type = this.safeString (trade, 'ordertype');
price = this.safeFloat (trade, 'price');
amount = this.safeFloat (trade, 'vol');
if ('fee' in trade) {
let currency = undefined;
if (market) {
if (market !== undefined) {
currency = market['quote'];
}
fee = {
@@ -757,6 +863,14 @@ module.exports = class kraken extends Exchange {
};
}
}
if (market !== undefined) {
symbol = market['symbol'];
}
if (price !== undefined) {
if (amount !== undefined) {
cost = price * amount;
}
}
return {
'id': id,
'order': order,
@@ -769,7 +883,7 @@ module.exports = class kraken extends Exchange {
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': price * amount,
'cost': cost,
'fee': fee,
};
}
@@ -986,6 +1100,11 @@ module.exports = class kraken extends Exchange {
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const id = this.safeString (order, 'id');
const clientOrderId = this.safeString (order, 'userref');
const rawTrades = this.safeValue (order, 'trades');
let trades = undefined;
if (rawTrades !== undefined) {
trades = this.parseTrades (rawTrades, market, undefined, undefined, { 'order': id });
}
return {
'id': id,
'clientOrderId': clientOrderId,
@@ -1004,8 +1123,7 @@ module.exports = class kraken extends Exchange {
'average': average,
'remaining': remaining,
'fee': fee,
// 'trades': this.parseTrades (order['trades'], market),
'trades': undefined,
'trades': trades,
};
}
@@ -1026,16 +1144,129 @@ module.exports = class kraken extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.privatePostQueryOrders (this.extend ({
const clientOrderId = this.safeValue2 (params, 'userref', 'clientOrderId');
const request = {
'trades': true, // whether or not to include trades in output (optional, default false)
'txid': id, // do not comma separate a list of ids - use fetchOrdersByIds instead
// 'txid': id, // do not comma separate a list of ids - use fetchOrdersByIds instead
// 'userref': 'optional', // restrict results to given user reference id (optional)
}, params));
const orders = response['result'];
};
let query = params;
if (clientOrderId !== undefined) {
request['userref'] = clientOrderId;
query = this.omit (params, [ 'userref', 'clientOrderId' ]);
} else {
request['txid'] = id;
}
const response = await this.privatePostQueryOrders (this.extend (request, query));
//
// {
// "error":[],
// "result":{
// "OTLAS3-RRHUF-NDWH5A":{
// "refid":null,
// "userref":null,
// "status":"closed",
// "reason":null,
// "opentm":1586822919.3342,
// "closetm":1586822919.365,
// "starttm":0,
// "expiretm":0,
// "descr":{
// "pair":"XBTUSDT",
// "type":"sell",
// "ordertype":"market",
// "price":"0",
// "price2":"0",
// "leverage":"none",
// "order":"sell 0.21804000 XBTUSDT @ market",
// "close":""
// },
// "vol":"0.21804000",
// "vol_exec":"0.21804000",
// "cost":"1493.9",
// "fee":"3.8",
// "price":"6851.5",
// "stopprice":"0.00000",
// "limitprice":"0.00000",
// "misc":"",
// "oflags":"fciq",
// "trades":["TT5UC3-GOIRW-6AZZ6R"]
// }
// }
// }
//
const orders = this.safeValue (response, 'result', []);
const order = this.parseOrder (this.extend ({ 'id': id }, orders[id]));
return this.extend ({ 'info': response }, order);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
const orderTrades = this.safeValue (params, 'trades');
const tradeIds = [];
if (orderTrades === undefined) {
throw new ArgumentsRequired (this.id + " fetchOrderTrades requires a unified order structure in the params argument or a 'trades' param (an array of trade id strings)");
} else {
for (let i = 0; i < orderTrades.length; i++) {
const orderTrade = orderTrades[i];
if (typeof orderTrade === 'string') {
tradeIds.push (orderTrade);
} else {
tradeIds.push (orderTrade['id']);
}
}
}
await this.loadMarkets ();
const options = this.safeValue (this.options, 'fetchOrderTrades', {});
const batchSize = this.safeInteger (options, 'batchSize', 20);
let numBatches = parseInt (tradeIds / batchSize);
numBatches = this.sum (numBatches, 1);
const numTradeIds = tradeIds.length;
let result = [];
for (let j = 0; j < numBatches; j++) {
const requestIds = [];
for (let k = 0; k < batchSize; k++) {
const index = this.sum (j * batchSize, k);
if (index < numTradeIds) {
requestIds.push (tradeIds[index]);
}
}
const request = {
'txid': requestIds.join (','),
};
const response = await this.privatePostQueryTrades (request);
//
// {
// error: [],
// result: {
// 'TIMIRG-WUNNE-RRJ6GT': {
// ordertxid: 'OQRPN2-LRHFY-HIFA7D',
// postxid: 'TKH2SE-M7IF5-CFI7LT',
// pair: 'USDCUSDT',
// time: 1586340086.457,
// type: 'sell',
// ordertype: 'market',
// price: '0.99860000',
// cost: '22.16892001',
// fee: '0.04433784',
// vol: '22.20000000',
// margin: '0.00000000',
// misc: ''
// }
// }
// }
//
const rawTrades = this.safeValue (response, 'result');
const ids = Object.keys (rawTrades);
for (let i = 0; i < ids.length; i++) {
rawTrades[ids[i]]['id'] = ids[i];
}
const trades = this.parseTrades (rawTrades, undefined, since, limit);
const tradesFilteredBySymbol = this.filterBySymbol (trades, symbol);
result = this.arrayConcat (result, tradesFilteredBySymbol);
}
return result;
}
async fetchOrdersByIds (ids, symbol = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.privatePostQueryOrders (this.extend ({
@@ -1248,11 +1479,11 @@ module.exports = class kraken extends Exchange {
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
// https://www.kraken.com/en-us/help/api#deposit-status
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'asset': currency['id'],
@@ -1275,11 +1506,11 @@ module.exports = class kraken extends Exchange {
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
// https://www.kraken.com/en-us/help/api#withdraw-status
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'asset': currency['id'],
@@ -1328,7 +1559,7 @@ module.exports = class kraken extends Exchange {
}
method = this.options['depositMethods'][code][0]['method'];
} else {
throw new ExchangeError (this.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods ("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = true');
throw new ArgumentsRequired (this.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods ("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = true');
}
}
const request = {
+121 -41
View File
@@ -69,7 +69,9 @@ module.exports = class kucoin extends Exchange {
'public': {
'get': [
'timestamp',
'status',
'symbols',
'markets',
'market/allTickers',
'market/orderbook/level{level}',
'market/orderbook/level2',
@@ -81,6 +83,9 @@ module.exports = class kucoin extends Exchange {
'market/stats',
'currencies',
'currencies/{currency}',
'prices',
'mark-price/{symbol}/current',
'margin/config',
],
'post': [
'bullet-public',
@@ -92,6 +97,10 @@ module.exports = class kucoin extends Exchange {
'accounts/{accountId}',
'accounts/{accountId}/ledgers',
'accounts/{accountId}/holds',
'accounts/transferable',
'sub/user',
'sub-accounts',
'sub-accounts/{subUserId}',
'deposit-addresses',
'deposits',
'hist-deposits',
@@ -101,8 +110,20 @@ module.exports = class kucoin extends Exchange {
'withdrawals/quotas',
'orders',
'orders/{orderId}',
'limit/orders',
'fills',
'limit/fills',
'margin/account',
'margin/borrow',
'margin/borrow/outstanding',
'margin/borrow/borrow/repaid',
'margin/lend/active',
'margin/lend/done',
'margin/lend/trade/unsettled',
'margin/lend/trade/settled',
'margin/lend/assets',
'margin/market',
'margin/margin/trade/last',
],
'post': [
'accounts',
@@ -111,11 +132,19 @@ module.exports = class kucoin extends Exchange {
'deposit-addresses',
'withdrawals',
'orders',
'orders/multi',
'margin/borrow',
'margin/repay/all',
'margin/repay/single',
'margin/lend',
'margin/toggle-auto-lend',
'bullet-private',
],
'delete': [
'withdrawals/{withdrawalId}',
'orders',
'orders/{orderId}',
'margin/lend/{orderId}',
],
},
},
@@ -247,18 +276,20 @@ module.exports = class kucoin extends Exchange {
async fetchMarkets (params = {}) {
const response = await this.publicGetSymbols (params);
//
// { quoteCurrency: 'BTC',
// symbol: 'KCS-BTC',
// quoteMaxSize: '9999999',
// quoteIncrement: '0.000001',
// baseMinSize: '0.01',
// quoteMinSize: '0.00001',
// enableTrading: true,
// priceIncrement: '0.00000001',
// name: 'KCS-BTC',
// baseIncrement: '0.01',
// baseMaxSize: '9999999',
// baseCurrency: 'KCS' }
// {
// quoteCurrency: 'BTC',
// symbol: 'KCS-BTC',
// quoteMaxSize: '9999999',
// quoteIncrement: '0.000001',
// baseMinSize: '0.01',
// quoteMinSize: '0.00001',
// enableTrading: true,
// priceIncrement: '0.00000001',
// name: 'KCS-BTC',
// baseIncrement: '0.01',
// baseMaxSize: '9999999',
// baseCurrency: 'KCS'
// }
//
const data = response['data'];
const result = [];
@@ -344,20 +375,27 @@ module.exports = class kucoin extends Exchange {
async fetchAccounts (params = {}) {
const response = await this.privateGetAccounts (params);
//
// { code: "200000",
// data: [ { balance: "0.00009788",
// {
// code: "200000",
// data: [
// {
// balance: "0.00009788",
// available: "0.00009788",
// holds: "0",
// currency: "BTC",
// id: "5c6a4fd399a1d81c4f9cc4d0",
// type: "trade" },
// ...,
// { balance: "0.00000001",
// holds: "0",
// currency: "BTC",
// id: "5c6a4fd399a1d81c4f9cc4d0",
// type: "trade"
// },
// {
// balance: "0.00000001",
// available: "0.00000001",
// holds: "0",
// currency: "ETH",
// id: "5c6a49ec99a1d819392e8e9f",
// type: "trade" } ] }
// holds: "0",
// currency: "ETH",
// id: "5c6a49ec99a1d819392e8e9f",
// type: "trade"
// }
// ]
// }
//
const data = this.safeValue (response, 'data');
const result = [];
@@ -641,30 +679,72 @@ module.exports = class kucoin extends Exchange {
};
}
async fetchL3OrderBook (symbol, limit = undefined, params = {}) {
return await this.fetchOrderBook (symbol, limit, { 'level': 3 });
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
let level = '2';
if (limit !== undefined) {
if ((limit !== 20) && (limit !== 100)) {
throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100');
const level = this.safeInteger (params, 'level', 2);
let levelLimit = level.toString ();
if (level === '2') {
if (limit !== undefined) {
if ((limit !== 20) && (limit !== 100)) {
throw new ExchangeError (this.id + ' fetchOrderBook limit argument must be undefined, 20 or 100');
}
levelLimit += '_' + limit.toString ();
}
level += '_' + limit.toString ();
}
await this.loadMarkets ();
const marketId = this.marketId (symbol);
const request = this.extend ({ 'symbol': marketId, 'level': level }, params);
const response = await this.publicGetMarketOrderbookLevelLevel (request);
const request = { 'symbol': marketId, 'level': levelLimit };
const response = await this.publicGetMarketOrderbookLevelLevel (this.extend (request, params));
//
// { sequence: '1547731421688',
// asks: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ],
// bids: [ [ '5c419328ef83c75456bd615c', '0.9', '0.09' ], ... ], }
// 'market/orderbook/level2'
// 'market/orderbook/level2_20'
// 'market/orderbook/level2_100'
//
const data = response['data'];
// {
// "code":"200000",
// "data":{
// "sequence":"1583235112106",
// "asks":[
// // ...
// ["0.023197","12.5067468"],
// ["0.023194","1.8"],
// ["0.023191","8.1069672"]
// ],
// "bids":[
// ["0.02319","1.6000002"],
// ["0.023189","2.2842325"],
// ],
// "time":1586584067274
// }
// }
//
// 'market/orderbook/level3'
//
// {
// "code":"200000",
// "data":{
// "sequence":"1583731857120",
// "asks":[
// // id, price, size, timestamp in nanoseconds
// ["5e915f8acd26670009675300","6925.7","0.2","1586585482194286069"],
// ["5e915f8ace35a200090bba48","6925.7","0.001","1586585482229569826"],
// ["5e915f8a8857740009ca7d33","6926","0.00001819","1586585482149148621"],
// ],
// "bids":[
// ["5e915f8acca406000ac88194","6925.6","0.05","1586585482384384842"],
// ["5e915f93cd26670009676075","6925.6","0.08","1586585491334914600"],
// ["5e915f906aa6e200099b49f6","6925.4","0.2","1586585488941126340"],
// ],
// "time":1586585492487
// }
// }
//
const data = this.safeValue (response, 'data', {});
const timestamp = this.safeInteger (data, 'time');
// level can be a string such as 2_20 or 2_100
const levelString = this.safeString (request, 'level');
const levelParts = levelString.split ('_');
const offset = parseInt (levelParts[0]);
const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', offset - 2, offset - 1);
const orderbook = this.parseOrderBook (data, timestamp, 'bids', 'asks', level - 2, level - 1);
orderbook['nonce'] = this.safeInteger (data, 'sequence');
return orderbook;
}
@@ -715,7 +795,7 @@ module.exports = class kucoin extends Exchange {
'datetime': this.iso8601 (timestamp),
'fee': undefined,
'status': 'open',
'clientOid': clientOid,
'clientOrderId': clientOid,
'info': data,
};
if (!this.safeValue (params, 'quoteAmount')) {
+21 -1
View File
@@ -18,11 +18,12 @@ module.exports = class kuna extends acx {
'has': {
'CORS': false,
'fetchTickers': true,
'fetchOHLCV': false,
'fetchOHLCV': 'emulated',
'fetchOpenOrders': true,
'fetchMyTrades': true,
'withdraw': false,
},
'timeframes': undefined,
'urls': {
'referral': 'https://kuna.io?r=kunaid-gvfihe8az7o4',
'logo': 'https://user-images.githubusercontent.com/1294454/31697638-912824fa-b3c1-11e7-8c36-cf9606eb94ac.jpg',
@@ -191,4 +192,23 @@ module.exports = class kuna extends acx {
const response = await this.privateGetTradesMy (this.extend (request, params));
return this.parseTrades (response, market, since, limit);
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limits = undefined, params = {}) {
await this.loadMarkets ();
const trades = await this.fetchTrades (symbol, since, limits, params);
const ohlcvc = this.buildOHLCVC (trades, timeframe, since, limits);
const result = [];
for (let i = 0; i < ohlcvc.length; i++) {
const ohlcv = ohlcvc[i];
result.push ([
ohlcv[0],
ohlcv[1],
ohlcv[2],
ohlcv[3],
ohlcv[4],
ohlcv[5],
]);
}
return result;
}
};
+59 -22
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');
const { ExchangeError, AuthenticationError, BadSymbol, InvalidOrder, InsufficientFunds } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -14,6 +14,7 @@ module.exports = class lakebtc extends Exchange {
'name': 'LakeBTC',
'countries': [ 'US' ],
'version': 'api_v2',
'rateLimit': 1000,
'has': {
'CORS': true,
'createMarketOrder': false,
@@ -55,6 +56,14 @@ module.exports = class lakebtc extends Exchange {
'taker': 0.2 / 100,
},
},
'exceptions': {
'broad': {
'Signature': AuthenticationError,
'invalid symbol': BadSymbol,
'Volume doit': InvalidOrder,
'insufficient_balance': InsufficientFunds,
},
},
});
}
@@ -248,40 +257,68 @@ module.exports = class lakebtc extends Exchange {
} else {
this.checkRequiredCredentials ();
const nonce = this.nonce ();
const nonceAsString = nonce.toString ();
const requestId = this.seconds ();
let queryParams = '';
if ('params' in params) {
const paramsList = params['params'];
queryParams = paramsList.join (',');
const stringParams = [];
for (let i = 0; i < paramsList.length; i++) {
let param = paramsList[i];
if (typeof paramsList !== 'string') {
param = param.toString ();
}
stringParams.push (param);
}
queryParams = stringParams.join (',');
body = {
'method': path,
'params': params['params'],
'id': requestId,
};
} else {
body = {
'method': path,
'params': '',
'id': requestId,
};
}
const query = this.urlencode ({
'tonce': nonce,
'accesskey': this.apiKey,
'requestmethod': method.toLowerCase (),
'id': nonce,
'method': path,
'params': queryParams,
});
body = this.json ({
'method': path,
'params': queryParams,
'id': nonce,
});
body = this.json (body);
let query = [
'tonce=' + nonceAsString,
'accesskey=' + this.apiKey,
'requestmethod=' + method.toLowerCase (),
'id=' + requestId.toString (),
'method=' + path,
'params=' + queryParams,
];
query = query.join ('&');
const signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha1');
const auth = this.encode (this.apiKey + ':' + signature);
const signature64 = this.decode (this.stringToBase64 (auth));
headers = {
'Json-Rpc-Tonce': nonce.toString (),
'Authorization': 'Basic ' + this.decode (this.stringToBase64 (auth)),
'Json-Rpc-Tonce': nonceAsString,
'Authorization': 'Basic ' + signature64,
'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 ('error' in response) {
throw new ExchangeError (this.id + ' ' + this.json (response));
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to the default error handler
}
//
// {"error":"Failed to submit order: invalid symbol"}
// {"error":"Failed to submit order: La validation a échoué : Volume doit être supérieur ou égal à 1.0"}
// {"error":"Failed to submit order: insufficient_balance"}
//
const feedback = this.id + ' ' + body;
const error = this.safeString (response, 'error');
if (error !== undefined) {
this.throwBroadlyMatchedException (this.exceptions['broad'], error, feedback);
throw new ExchangeError (feedback); // unknown message
}
return response;
}
};
+3 -3
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder } = require ('./base/errors');
const { ExchangeError, DDoSProtection, AuthenticationError, InvalidOrder, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -339,10 +339,10 @@ module.exports = class lbank extends Exchange {
await this.loadMarkets ();
const market = this.market (symbol);
if (since === undefined) {
throw new ExchangeError (this.id + ' fetchOHLCV requires a `since` argument');
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `since` argument');
}
if (limit === undefined) {
throw new ExchangeError (this.id + ' fetchOHLCV requires a `limit` argument');
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a `limit` argument');
}
const request = {
'symbol': market['id'],
+64 -8
View File
@@ -177,7 +177,7 @@ module.exports = class liquid extends Exchange {
}
async fetchMarkets (params = {}) {
const markets = await this.publicGetProducts ();
const spot = await this.publicGetProducts (params);
//
// [
// {
@@ -208,20 +208,69 @@ module.exports = class liquid extends Exchange {
// },
// ]
//
const perpetual = await this.publicGetProducts ({ 'perpetual': '1' });
//
// [
// {
// "id": "603",
// "product_type": "Perpetual",
// "code": "CASH",
// "name": null,
// "market_ask": "1143900",
// "market_bid": "1143250",
// "currency": "JPY",
// "currency_pair_code": "P-BTCJPY",
// "pusher_channel": "product_cash_p-btcjpy_603",
// "taker_fee": "0.0",
// "maker_fee": "0.0",
// "low_market_bid": "1124450.0",
// "high_market_ask": "1151750.0",
// "volume_24h": "0.1756",
// "last_price_24h": "1129850.0",
// "last_traded_price": "1144700.0",
// "last_traded_quantity": "0.014",
// "quoted_currency": "JPY",
// "base_currency": "P-BTC",
// "tick_size": "50.0",
// "perpetual_enabled": true,
// "index_price": "1142636.03935",
// "mark_price": "1143522.18417",
// "funding_rate": "0.00033",
// "fair_price": "1143609.31009",
// "timestamp": "1581558659.195353100",
// },
// ]
//
const currencies = await this.fetchCurrencies ();
const currenciesByCode = this.indexBy (currencies, 'code');
const result = [];
const markets = this.arrayConcat (spot, perpetual);
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = market['id'].toString ();
const baseId = market['base_currency'];
const quoteId = market['quoted_currency'];
const id = this.safeString (market, 'id');
const baseId = this.safeString (market, 'base_currency');
const quoteId = this.safeString (market, 'quoted_currency');
const productType = this.safeString (market, 'product_type');
let type = 'spot';
let spot = true;
let swap = false;
if (productType === 'Perpetual') {
spot = false;
swap = true;
type = 'swap';
}
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
let symbol = undefined;
if (swap) {
symbol = this.safeString (market, 'currency_pair_code');
} else {
symbol = base + '/' + quote;
}
const maker = this.safeFloat (market, 'maker_fee');
const taker = this.safeFloat (market, 'taker_fee');
const active = !market['disabled'];
const disabled = this.safeValue (market, 'disabled', false);
const active = !disabled;
const baseCurrency = this.safeValue (currenciesByCode, base);
const quoteCurrency = this.safeValue (currenciesByCode, quote);
const precision = {
@@ -265,6 +314,9 @@ module.exports = class liquid extends Exchange {
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'type': type,
'spot': spot,
'swap': swap,
'maker': maker,
'taker': taker,
'limits': limits,
@@ -393,7 +445,7 @@ module.exports = class liquid extends Exchange {
return this.parseTicker (response, market);
}
parseTrade (trade, market) {
parseTrade (trade, market = undefined) {
// { id: 12345,
// quantity: "6.789",
// price: "98765.4321",
@@ -420,13 +472,17 @@ module.exports = class liquid extends Exchange {
}
}
const id = this.safeString (trade, 'id');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
return {
'info': trade,
'id': id,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': market['symbol'],
'symbol': symbol,
'type': undefined,
'side': side,
'takerOrMaker': takerOrMaker,
+19 -7
View File
@@ -439,7 +439,18 @@ module.exports = class livecoin extends Exchange {
}
}
let symbol = undefined;
if (market !== undefined) {
const marketId = this.safeString (trade, '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'];
}
return {
@@ -460,16 +471,17 @@ module.exports = class livecoin extends Exchange {
}
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 = {
'currencyPair': market['id'],
// orderDesc': 'true', // or 'false', if true then new orders will be first, otherwise old orders will be first.
// 'currencyPair': market['id'],
// 'orderDesc': 'true', // or 'false', if true then new orders will be first, otherwise old orders will be first.
// 'offset': 0, // page offset, position of the first item on the page
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['currencyPair'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit;
}
+3 -3
View File
@@ -399,17 +399,17 @@ module.exports = class mercado extends Exchange {
if (code === 'BRL') {
const account_ref = ('account_ref' in params);
if (!account_ref) {
throw new ExchangeError (this.id + ' requires account_ref parameter to withdraw ' + code);
throw new ArgumentsRequired (this.id + ' requires account_ref parameter to withdraw ' + code);
}
} else if (code !== 'LTC') {
const tx_fee = ('tx_fee' in params);
if (!tx_fee) {
throw new ExchangeError (this.id + ' requires tx_fee parameter to withdraw ' + code);
throw new ArgumentsRequired (this.id + ' requires tx_fee parameter to withdraw ' + code);
}
if (code === 'XRP') {
if (tag === undefined) {
if (!('destination_tag' in params)) {
throw new ExchangeError (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code);
throw new ArgumentsRequired (this.id + ' requires a tag argument or destination_tag parameter to withdraw ' + code);
}
} else {
request['destination_tag'] = tag;
+27 -40
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ExchangeNotAvailable, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors');
const { ExchangeError, ExchangeNotAvailable, OnMaintenance, ArgumentsRequired, BadRequest, AccountSuspended, InvalidAddress, PermissionDenied, DDoSProtection, InsufficientFunds, InvalidNonce, CancelPending, InvalidOrder, OrderNotFound, AuthenticationError, RequestTimeout, NotSupported, BadSymbol } = require ('./base/errors');
const { TICK_SIZE, TRUNCATE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -61,6 +61,9 @@ module.exports = class okex extends Exchange {
'doc': 'https://www.okex.com/docs/en/',
'fees': 'https://www.okex.com/pages/products/fees.html',
'referral': 'https://www.okex.com/join/1888677',
'test': {
'rest': 'https://testnet.okex.com',
},
},
'api': {
'general': {
@@ -348,8 +351,9 @@ module.exports = class okex extends Exchange {
'30034': ExchangeError, // { "code": 30034, "message": "exchange ID does not exist" }
'30035': ExchangeError, // { "code": 30035, "message": "trading is not supported in this website" }
'30036': ExchangeError, // { "code": 30036, "message": "no relevant data" }
'30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" }
'30037': ExchangeNotAvailable, // { "code": 30037, "message": "endpoint is offline or unavailable" }
// '30038': AuthenticationError, // { "code": 30038, "message": "user does not exist" }
'30038': OnMaintenance, // {"client_oid":"","code":"30038","error_code":"30038","error_message":"Matching engine is being upgraded. Please try in about 1 minute.","message":"Matching engine is being upgraded. Please try in about 1 minute.","order_id":"-1","result":false}
// futures
'32001': AccountSuspended, // { "code": 32001, "message": "futures account suspended" }
'32002': PermissionDenied, // { "code": 32002, "message": "futures account does not exist" }
@@ -792,14 +796,6 @@ module.exports = class okex extends Exchange {
'price': this.safeFloat (market, 'tick_size'),
};
const minAmount = this.safeFloat2 (market, 'min_size', 'base_min_size');
let minPrice = this.safeFloat (market, 'tick_size');
if (precision['price'] !== undefined) {
minPrice = Math.pow (10, -precision['price']);
}
let minCost = undefined;
if (minAmount !== undefined && minPrice !== undefined) {
minCost = minAmount * minPrice;
}
const active = true;
const fees = this.safeValue2 (this.fees, marketType, 'trading', {});
return this.extend (fees, {
@@ -823,11 +819,11 @@ module.exports = class okex extends Exchange {
'max': undefined,
},
'price': {
'min': minPrice,
'min': precision['price'],
'max': undefined,
},
'cost': {
'min': minCost,
'min': precision['price'],
'max': undefined,
},
},
@@ -1360,8 +1356,18 @@ module.exports = class okex extends Exchange {
'instrument_id': market['id'],
'granularity': this.timeframes[timeframe],
};
const duration = this.parseTimeframe (timeframe);
if (since !== undefined) {
if (limit !== undefined) {
request['end'] = this.iso8601 (this.sum (since, limit * duration * 1000));
}
request['start'] = this.iso8601 (since);
} else {
const now = this.milliseconds ();
if (limit !== undefined) {
request['start'] = this.iso8601 (now - limit * duration * 1000);
request['end'] = this.iso8601 (now);
}
}
const response = await this[method] (this.extend (request, params));
//
@@ -1841,28 +1847,7 @@ module.exports = class okex extends Exchange {
// "result":true
// }
//
const timestamp = this.milliseconds ();
const id = this.safeString (response, 'order_id');
return {
'info': response,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'status': undefined,
'symbol': symbol,
'type': type,
'side': side,
'price': price,
'amount': amount,
'filled': undefined,
'remaining': undefined,
'cost': undefined,
'trades': undefined,
'fee': undefined,
'clientOrderId': undefined,
'average': undefined,
};
return this.parseOrder (response, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
@@ -2302,9 +2287,11 @@ module.exports = class okex extends Exchange {
}
parseDepositAddresses (addresses) {
const result = [];
const result = {};
for (let i = 0; i < addresses.length; i++) {
result.push (this.parseDepositAddress (addresses[i]));
const address = this.parseDepositAddress (addresses[i]);
const code = address['currency'];
result[code] = address;
}
return result;
}
@@ -2350,11 +2337,11 @@ module.exports = class okex extends Exchange {
// ]
//
const addresses = this.parseDepositAddresses (response);
const numAddresses = addresses.length;
if (numAddresses < 1) {
const address = this.safeValue (addresses, code);
if (address === undefined) {
throw new InvalidAddress (this.id + ' fetchDepositAddress cannot return nonexistent addresses, you should create withdrawal addresses with the exchange website first');
}
return addresses[0];
return address;
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
@@ -2366,7 +2353,7 @@ module.exports = class okex extends Exchange {
}
const fee = this.safeString (params, 'fee');
if (fee === undefined) {
throw new ExchangeError (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee.");
throw new ArgumentsRequired (this.id + " withdraw() requires a `fee` string parameter, network transaction fee must be ≥ 0. Withdrawals to OKCoin or OKEx are fee-free, please set '0'. Withdrawing to external digital asset address requires network transaction fee.");
}
const request = {
'currency': currency['id'],
+18 -11
View File
@@ -27,6 +27,7 @@ module.exports = class paymium extends Exchange {
'https://github.com/Paymium/api-documentation',
'https://www.paymium.com/page/developers',
],
'referral': 'https://www.paymium.com/page/sign-up?referral=eDAzPoRQFMvaAB8sf-qj',
},
'api': {
'public': {
@@ -41,12 +42,12 @@ module.exports = class paymium extends Exchange {
},
'private': {
'get': [
'merchant/get_payment/{UUID}',
'merchant/get_payment/{uuid}',
'user',
'user/addresses',
'user/addresses/{btc_address}',
'user/orders',
'user/orders/{UUID}',
'user/orders/{uuid}',
'user/price_alerts',
],
'post': [
@@ -57,7 +58,7 @@ module.exports = class paymium extends Exchange {
'merchant/create_payment',
],
'delete': [
'user/orders/{UUID}/cancel',
'user/orders/{uuid}/cancel',
'user/price_alerts/{id}',
],
},
@@ -204,9 +205,9 @@ module.exports = class paymium extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
const request = {
'UUID': id,
'uuid': id,
};
return await this.privateDeleteUserOrdersUUIDCancel (this.extend (request, params));
return await this.privateDeleteUserOrdersUuidCancel (this.extend (request, params));
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
@@ -220,18 +221,24 @@ module.exports = class paymium extends Exchange {
this.checkRequiredCredentials ();
const nonce = this.nonce ().toString ();
let auth = nonce + url;
headers = {
'Api-Key': this.apiKey,
'Api-Nonce': nonce,
};
if (method === 'POST') {
if (Object.keys (query).length) {
body = this.json (query);
auth += body;
headers['Content-Type'] = 'application/json';
}
} else {
if (Object.keys (query).length) {
const queryString = this.urlencode (query);
auth += queryString;
url += '?' + queryString;
}
}
headers = {
'Api-Key': this.apiKey,
'Api-Signature': this.hmac (this.encode (auth), this.encode (this.secret)),
'Api-Nonce': nonce,
'Content-Type': 'application/json',
};
headers['Api-Signature'] = this.hmac (this.encode (auth), this.encode (this.secret));
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
+2 -1
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ExchangeNotAvailable, RequestTimeout, AuthenticationError, PermissionDenied, DDoSProtection, InsufficientFunds, OrderNotFound, OrderNotCached, InvalidOrder, AccountSuspended, CancelPending, InvalidNonce } = require ('./base/errors');
const { ExchangeError, ExchangeNotAvailable, RequestTimeout, AuthenticationError, PermissionDenied, DDoSProtection, InsufficientFunds, OrderNotFound, OrderNotCached, InvalidOrder, AccountSuspended, CancelPending, InvalidNonce, OnMaintenance } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -170,6 +170,7 @@ module.exports = class poloniex extends Exchange {
'Permission denied': PermissionDenied,
'Connection timed out. Please try again.': RequestTimeout,
'Internal error. Please try again.': ExchangeNotAvailable,
'Currently in maintenance mode.': OnMaintenance,
'Order not found, or you are not the person who placed it.': OrderNotFound,
'Invalid API key/secret pair.': AuthenticationError,
'Please do not make more than 8 API calls per second.': DDoSProtection,
-1
View File
@@ -17,7 +17,6 @@ module.exports = async (exchange, symbol) => {
'btcalpha', // issue with 404 on a documented endpoint https://travis-ci.org/ccxt/ccxt/builds/643930431#L2213
'bitmex', // an issue with null values,to be resolved later
'cex',
'mandala',
]
if (skippedExchanges.includes (exchange.id)) {
+1 -1
View File
@@ -13,7 +13,7 @@ const log = require ('ololog')
module.exports = async (exchange) => {
const randomSymbols = exchange.symbols.sort (() => 0.5 - Math.random ()).slice (0, 2)
const randomSymbols = exchange.symbols.slice ().sort (() => 0.5 - Math.random ()).slice (0, 2)
const customExchangeParams = ([
'yobit',
'tidex',
-1
View File
@@ -14,7 +14,6 @@ const log = require ('ololog')
module.exports = async (exchange, symbol) => {
const skippedExchanges = [
'mandala',
'rightbtc',
]
-1
View File
@@ -76,7 +76,6 @@ module.exports = (exchange, ticker, method, symbol) => {
'xbtce',
'coss',
'idex',
'mandala',
'mercado',
'okex',
'southxchange', // https://user-images.githubusercontent.com/1294454/59953532-314bea80-9489-11e9-85b3-2a711ca49aa7.png
+5
View File
@@ -25,6 +25,8 @@ assert (toWei ('0.001', 18) === '1000000000000000');
assert (toWei (0.1, 18) === '100000000000000000');
assert (toWei (0.01, 18) === '10000000000000000');
assert (toWei (0.001, 18) === '1000000000000000');
assert (toWei ('0.3323340739', 18) === '332334073900000000');
assert (toWei (0.3323340739, 18) === '332334073900000000');
// let us test that we get the inverse for all these test
assert (fromWei ('1000000000000000000', 18) === 1.0);
@@ -43,6 +45,8 @@ assert (fromWei ('1000000000000000', 18) === 0.001);
assert (fromWei (100000000000000000, 18) === 0.1);
assert (fromWei (10000000000000000, 18) === 0.01);
assert (fromWei (1000000000000000, 18) === 0.001);
assert (fromWei ('332334073900000000', 18) === 0.3323340739);
assert (fromWei (332334073900000000, 18) === 0.3323340739);
// ----------------------------------------------------------------------------
// numberToString
@@ -58,6 +62,7 @@ assert (numberToString (7.9e27) === '7900000000000000000000000000');
assert (numberToString (-12.345) === '-12.345');
assert (numberToString (12.345) === '12.345');
assert (numberToString (0) === '0');
assert (numberToString (7.35946e21) === '7359460000000000000000');
// the following line breaks the test
// see https://github.com/ccxt/ccxt/issues/5744
// assert (numberToString (0.00000001) === '0.00000001');
+3
View File
@@ -370,9 +370,12 @@ let tryAllProxies = async function (exchange, proxies) {
;(async function test () {
if (exchangeSymbol) {
await loadExchange (exchange)
await testSymbol (exchange, exchangeSymbol)
} else {
await tryAllProxies (exchange, proxies)
}
+3 -3
View File
@@ -180,6 +180,9 @@ module.exports = class theocean extends Exchange {
}
async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) {
if (since === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
@@ -187,9 +190,6 @@ module.exports = class theocean extends Exchange {
'quoteTokenAddress': market['quoteId'],
'interval': this.timeframes[timeframe],
};
if (since === undefined) {
throw new ExchangeError (this.id + ' fetchOHLCV requires a since argument');
}
since = parseInt (since);
request['startTime'] = since;
const response = await this.publicGetCandlesticks (this.extend (request, params));
+2 -2
View File
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, InsufficientFunds, OrderNotFound } = require ('./base/errors');
const { ExchangeError, InsufficientFunds, OrderNotFound, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -460,7 +460,7 @@ module.exports = class tidebit extends Exchange {
const currency = this.currency (code);
const id = this.safeString (params, 'id');
if (id === undefined) {
throw new ExchangeError (this.id + ' withdraw() requires an extra `id` param (withdraw account id according to withdraws/bind_account_list endpoint');
throw new ArgumentsRequired (this.id + ' withdraw() requires an extra `id` param (withdraw account id according to withdraws/bind_account_list endpoint');
}
const request = {
'id': id,
+30 -13
View File
@@ -42,6 +42,7 @@ module.exports = class timex extends Exchange {
'api': 'https://plasma-relay-backend.timex.io',
'www': 'https://timex.io',
'doc': 'https://docs.timex.io',
'referral': 'https://timex.io/?refcode=1x27vNkTbP1uwkCck',
},
'api': {
'custody': {
@@ -892,7 +893,21 @@ module.exports = class timex extends Exchange {
// "active": true,
// "withdrawalFee": "50000000000000000",
// "purchaseCommissions": []
// },
// }
//
// https://github.com/ccxt/ccxt/issues/6878
//
// {
// "symbol":"XRP",
// "name":"Ripple",
// "address":"0x0dc8882914f3ddeebf4cec6dc20edb99df3def6c",
// "decimals":6,
// "tradeDecimals":16,
// "depositEnabled":true,
// "withdrawalEnabled":true,
// "transferEnabled":true,
// "active":true
// }
//
const id = this.safeString (currency, 'symbol');
const code = this.safeCurrencyCode (id);
@@ -901,20 +916,22 @@ module.exports = class timex extends Exchange {
const active = this.safeValue (currency, 'active');
// const fee = this.safeFloat (currency, 'withdrawalFee');
const feeString = this.safeString (currency, 'withdrawalFee');
const feeStringLen = feeString.length;
const tradeDecimals = this.safeInteger (currency, 'tradeDecimals');
let fee = undefined;
const dotIndex = feeStringLen - tradeDecimals;
if (dotIndex > 0) {
const whole = feeString.slice (0, dotIndex);
const fraction = feeString.slice (-dotIndex);
fee = parseFloat (whole + '.' + fraction);
} else {
let fraction = '.';
for (let i = 0; i < -dotIndex; i++) {
fraction += '0';
if ((feeString !== undefined) && (tradeDecimals !== undefined)) {
const feeStringLen = feeString.length;
const dotIndex = feeStringLen - tradeDecimals;
if (dotIndex > 0) {
const whole = feeString.slice (0, dotIndex);
const fraction = feeString.slice (-dotIndex);
fee = parseFloat (whole + '.' + fraction);
} else {
let fraction = '.';
for (let i = 0; i < -dotIndex; i++) {
fraction += '0';
}
fee = parseFloat (fraction + feeString);
}
fee = parseFloat (fraction + feeString);
}
return {
'id': code,
@@ -1105,7 +1122,7 @@ module.exports = class timex extends Exchange {
this.safeFloat (ohlcv, 'high'),
this.safeFloat (ohlcv, 'low'),
this.safeFloat (ohlcv, 'close'),
this.safeFloat (ohlcv, 'volumeQuote'),
this.safeFloat (ohlcv, 'volume'),
];
}
+4 -2
View File
@@ -1324,9 +1324,11 @@ module.exports = class upbit extends Exchange {
}
parseDepositAddresses (addresses) {
const result = [];
const result = {};
for (let i = 0; i < addresses.length; i++) {
result.push (this.parseDepositAddress (addresses[i]));
const address = this.parseDepositAddress (addresses[i]);
const code = address['currency'];
result[code] = address;
}
return result;
}
+2 -1
View File
@@ -167,11 +167,12 @@ module.exports = class yobit extends Exchange {
'api key dont have trade permission': AuthenticationError,
'invalid parameter': InvalidOrder,
'invalid order': InvalidOrder,
'The given order has already been cancelled': InvalidOrder,
'Requests too often': DDoSProtection,
'not available': ExchangeNotAvailable,
'data unavailable': ExchangeNotAvailable,
'external service unavailable': ExchangeNotAvailable,
'Total transaction amount': ExchangeError, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"}
'Total transaction amount': InvalidOrder, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"}
'Insufficient funds': InsufficientFunds,
'invalid key': AuthenticationError,
'invalid nonce': InvalidNonce, // {"success":0,"error":"invalid nonce (has already been used)"}'
Loaded 100 of 117 files, more files were not shown because too many files have changed in this diff. Show more