Merge branch 'develop' of https://github.com/Superalgos/Superalgos into develop

This commit is contained in:
cozed-gh committed 2021-03-16 08:58:37 +01:00
commit 72f135d983
284 files changed
+47777 -12121

No files matched your search

+2 -2
View File
@@ -56,7 +56,7 @@
onresize="onResize()">
<div id="contributors" class="credits">
<p id="credits-page-status-paragraph" class="credits-yellow">The UI is loading...</p>
<p>Superalgos is <span class="credits-red">forever free</span> and <span class="credits-red">open-source</span>, and it's
<p>Superalgos is <span class="credits-red">open source</span> and <span class="credits-red">free</span>, for <span class="credits-red">everyone</span>, <span class="credits-red">forever</span>, and it's
<span class="credits-red">relentlessly improving</span> thanks to the involvement of contributors:</p>
<p>Luis Fernando Molina, Julian Molina, Andreja Cobeljic, Matías Benitez, Ira Miller, Eduardo Remis,
Jeff Braun, rico4dev, 9808us, Barry Low, Nikola Bjelogrlic, Hirajin Koizuko, Francisco J. Santillán, Norman,
@@ -203,4 +203,4 @@
</body>
</html>
</html>
+6
View File
@@ -257,6 +257,9 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
webhookMessages = []
}
console.log('[INFO] httpInterface -> Webhook -> Fetch-Messages -> Exchange-Market = ' + exchange + '-' + market)
console.log('[INFO] httpInterface -> Webhook -> Fetch-Messages -> Messeges Fetched by Webhooks Sensor Bot = ' + webhookMessages.length)
respondWithContent(JSON.stringify(webhookMessages), httpResponse)
webhookMessages = []
@@ -299,6 +302,9 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
webhookMessages.push([timestamp, source, messageReceived])
webhook.set(key, webhookMessages)
console.log('[INFO] httpInterface -> Webhook -> New-Message -> Exchange-Market = ' + exchange + '-' + market)
console.log('[INFO] httpInterface -> Webhook -> New-Message -> messageReceived = ' + messageReceived)
console.log('[INFO] httpInterface -> Webhook -> New-Message -> Messeges waiting to be Fetched by Webhooks Sensor Bot = ' + webhookMessages.length)
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), httpResponse)
}
break
@@ -1 +1 @@
{"type":"Workspace","name":"Tested Exchanges","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[],"id":"40af91d9-431c-4b7d-addc-aed37bc2f3fe","savedPayload":{"position":{"x":25089.356993272086,"y":30767.11115747996},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"learningTasks":{"type":"Learning Tasks","name":"New Learning Tasks","project":"Superalgos","projectLearningTasks":[],"id":"0818dfbe-61ea-40b8-8544-bee25d7f45be","savedPayload":{"position":{"x":25389.356993272086,"y":30767.11115747996},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"testingTradingTasks":{"type":"Testing Trading Tasks","name":"New Testing Trading Tasks","project":"Superalgos","projectTradingTasks":[],"id":"8117db0a-e418-4f56-8fe4-fda2ddac1cbb","savedPayload":{"position":{"x":25539.356993272086,"y":31026.918778615294},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"productionTradingTasks":{"type":"Production Trading Tasks","name":"New Production Trading Tasks","project":"Superalgos","projectTradingTasks":[],"id":"673caa5f-511d-4688-9305-e5f0f0f780cd","savedPayload":{"position":{"x":25389.356993272086,"y":31286.726399750627},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"dataStorage":{"type":"Data Storage","name":"New Data Storage","project":"Superalgos","dataMinesData":{"type":"Data Mines Data","name":"New Data Mines Data","project":"Superalgos","projectDataProducts":[],"id":"32e7c122-fa5a-4d00-9d71-d54e890e32de","savedPayload":{"position":{"x":25070.19913560607,"y":31588.15985816277},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"tradingMinesData":{"type":"Trading Mines Data","name":"New Trading Mines Data","project":"Superalgos","projectTradingProducts":[],"id":"e4e268f3-a677-4f90-90a1-d87244e647e5","savedPayload":{"position":{"x":24959.356993272086,"y":31550.53402088596},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"learningMinesData":{"type":"Learning Mines Data","name":"New Learning Mines Data","project":"Superalgos","projectLearningProducts":[],"id":"2845a687-becc-4c3b-bc79-b2293ba876ba","savedPayload":{"position":{"x":24871.350991184714,"y":31473.354828453244},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"e2697759-6038-4943-b90c-5a68651ff9a8","savedPayload":{"position":{"x":25089.356993272086,"y":31286.726399750627},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":"d"}}},"id":"e452cf65-2b2b-44f2-9e67-bdd9d3c008e4","savedPayload":{"position":{"x":25239.356993272086,"y":31026.918778615294},"targetPosition":{"x":24939.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":""}}}],"id":"3c165071-7558-4708-9999-8ffe71d346b1","savedPayload":{"position":{"x":24939.356993272086,"y":31026.918778615294},"targetPosition":{"x":24939.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":"s"}}},{"type":"Crypto Ecosystem","name":"My Crypto Ecosystem","project":"Superalgos","cryptoExchanges":[{"type":"Crypto Exchanges","name":"Tested","project":"Superalgos","exchanges":[Line truncated
{"type":"Workspace","name":"Tested Exchanges","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[],"id":"40af91d9-431c-4b7d-addc-aed37bc2f3fe","savedPayload":{"position":{"x":25089.356993272086,"y":30767.11115747996},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"learningTasks":{"type":"Learning Tasks","name":"New Learning Tasks","project":"Superalgos","projectLearningTasks":[],"id":"0818dfbe-61ea-40b8-8544-bee25d7f45be","savedPayload":{"position":{"x":25389.356993272086,"y":30767.11115747996},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"testingTradingTasks":{"type":"Testing Trading Tasks","name":"New Testing Trading Tasks","project":"Superalgos","projectTradingTasks":[],"id":"8117db0a-e418-4f56-8fe4-fda2ddac1cbb","savedPayload":{"position":{"x":25539.356993272086,"y":31026.918778615294},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"productionTradingTasks":{"type":"Production Trading Tasks","name":"New Production Trading Tasks","project":"Superalgos","projectTradingTasks":[],"id":"673caa5f-511d-4688-9305-e5f0f0f780cd","savedPayload":{"position":{"x":25389.356993272086,"y":31286.726399750627},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"dataStorage":{"type":"Data Storage","name":"New Data Storage","project":"Superalgos","dataMinesData":{"type":"Data Mines Data","name":"New Data Mines Data","project":"Superalgos","projectDataProducts":[],"id":"32e7c122-fa5a-4d00-9d71-d54e890e32de","savedPayload":{"position":{"x":25070.19913560607,"y":31588.15985816277},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"tradingMinesData":{"type":"Trading Mines Data","name":"New Trading Mines Data","project":"Superalgos","projectTradingProducts":[],"id":"e4e268f3-a677-4f90-90a1-d87244e647e5","savedPayload":{"position":{"x":24959.356993272086,"y":31550.53402088596},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"learningMinesData":{"type":"Learning Mines Data","name":"New Learning Mines Data","project":"Superalgos","projectLearningProducts":[],"id":"2845a687-becc-4c3b-bc79-b2293ba876ba","savedPayload":{"position":{"x":24871.350991184714,"y":31473.354828453244},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"e2697759-6038-4943-b90c-5a68651ff9a8","savedPayload":{"position":{"x":25089.356993272086,"y":31286.726399750627},"targetPosition":{"x":25239.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":"d"}}},"id":"e452cf65-2b2b-44f2-9e67-bdd9d3c008e4","savedPayload":{"position":{"x":25239.356993272086,"y":31026.918778615294},"targetPosition":{"x":24939.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":""}}}],"id":"3c165071-7558-4708-9999-8ffe71d346b1","savedPayload":{"position":{"x":24939.356993272086,"y":31026.918778615294},"targetPosition":{"x":24939.356993272086,"y":31026.918778615294},"floatingObject":{"isPinned":true,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":"s"}}},{"type":"Crypto Ecosystem","name":"My Crypto Ecosystem","project":"Superalgos","cryptoExchanges":[{"type":"Crypto Exchanges","name":"Tested","project":"Superalgos","exchanges":[Line truncated
+11 -12
View File
@@ -3242,8 +3242,7 @@
"compatibleTypes": "->Prediction->"
},
"propertyNameAtParent": "predictionValue",
"childrenNodesProperties": [
],
"childrenNodesProperties": [],
"icon": "bitcoin"
},
{
@@ -3415,8 +3414,7 @@
"compatibleTypes": "->Feature->"
},
"propertyNameAtParent": "featureValue",
"childrenNodesProperties": [
],
"childrenNodesProperties": [],
"icon": "bitcoin"
},
{
@@ -3588,8 +3586,7 @@
"compatibleTypes": "->Label->"
},
"propertyNameAtParent": "labelValue",
"childrenNodesProperties": [
],
"childrenNodesProperties": [],
"icon": "bitcoin"
},
{
@@ -5961,7 +5958,7 @@
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "This",
"label": "Delete",
"iconPathOn": "delete-entity",
"iconPathOff": "delete-entity",
"actionFunction": "payload.executeAction"
@@ -15083,7 +15080,8 @@
"childType": "Signals Provider",
"autoAdd": true
}
]
],
"icon": "bitcoin"
},
{
"type": "Signals Provider",
@@ -15124,10 +15122,11 @@
"config": true
},
"initialValues": {
"config": "{ \n\"webhookURL\": \"Webhook/New-Message/Trading-View/Binance/BTC-USDT\",\n\"testMessage\": \"\" \n}"
"config": "{ \n\"webhookURL\": \"Webhook/New-Message/Trading-View/binance/BTC-USDT\",\n\"testMessage\": \"\" \n}"
},
"propertyNameAtParent": "signalsProviders",
"childrenNodesProperties": []
"childrenNodesProperties": [],
"icon": "bitcoin"
},
{
"type": "Crypto Exchanges",
@@ -15937,7 +15936,7 @@
"config": true
},
"initialValues": {
"config": "{\"node\":{\"distancePercentage\":60,\"radiusPercentage\":85,\"massPercentage\":100,\"animationSteps\":2,\"menuItem\":{\"widthPercentage\":100,\"heightPercentage\":60,\"radiusPercentage\":90}},\"physics\":false}"
"config": "{\"node\":{\"distancePercentage\":60,\"radiusPercentage\":85,\"massPercentage\":100,\"animationSteps\":2,\"menuItem\":{\"widthPercentage\":100,\"heightPercentage\":60,\"radiusPercentage\":90}},\"physics\":false,\"detachUsingMouse\":false }"
},
"menuItems": [
{
@@ -19794,4 +19793,4 @@
}
]
}
]
]
@@ -37,8 +37,8 @@
},
{
"style": "Text",
"text": "After issuing the token in March 2021, the funds were deposited at the project's Treasury Account, from where it is distributed to each year's Budget Account, each Release Account and finally to contributors accounts.",
"updated": 1614853686491
"text": "After issuing the token in March 2021, the funds were deposited at the project's Treasury Account, from where it is distributed to contributors accounts.",
"updated": 1615564586741
},
{
"style": "List",
@@ -31,6 +31,14 @@
"style": "List",
"text": "Bitfinex Test Status"
},
{
"style": "List",
"text": "Bittrex Test Status"
},
{
"style": "List",
"text": "CoinbasePro Test Status"
},
{
"style": "List",
"text": "FTX Test Status",
@@ -38,7 +46,8 @@
},
{
"style": "List",
"text": "Kraken Tested Status"
"text": "Kraken Test Status",
"updated": 1615453046113
},
{
"style": "List",
@@ -55,6 +55,10 @@
{
"style": "List",
"text": "physics determines wether the environment is static (false) or dynamic (true). When physics is set to true, nodes in the design space are affected by th gravitational pull and repulsion of other nodes."
},
{
"style": "List",
"text": "detachUsingMouse defines if you can detach nodes from it's parent or reference parents only using the mouse (when true) or if you can not use the mouse for that and can only do it using the detach buttons on the icon's menu (when false)."
}
]
}
@@ -7,15 +7,36 @@
"updated": 1610909304702
},
"paragraphs": [
{
"style": "Text",
"text": "This command will send all the changes you have made both at Docs pages but also at any file within the Superalgos Repository to your fork at Github.com and from there to the main Superalgos Repository. It will do it specifically from the Current Branch to the Contributions Branch mentioned at the footer of this page. ",
"updated": 1615288848213
},
{
"style": "Important",
"text": "Contributions in most cases needs to come from the develop branch to the develop branch unless you have previously agreed with team members that you will send a contribution from master to master."
},
{
"style": "Text",
"text": "Master is our stable branch and it is very unlikely that we are going to merge contributions there. Instead all contributions are expected to go to the develop branch, where there are many users testing that branch every day, and the contribution will reach the master branch at the next relrease.",
"updated": 1615288915134
},
{
"style": "Note",
"text": "It is also very unlikely that contributions coming from the master branch will be merged into the develop branch, since that can usually lead to merging conflicts. "
},
{
"style": "Text",
"text": "So be sure that your Current Branch is develop and the Contribution Branch is develop before submitting your contribution. "
},
{
"style": "Title",
"text": "Syntax",
"updated": 1609943337280
"text": "Syntax"
},
{
"style": "Javascript",
"text": "docs.contribute Message",
"updated": 1609943744230
"text": "app.contribute [Message]",
"updated": 1615461375106
},
{
"style": "Title",
@@ -33,8 +54,8 @@
},
{
"style": "Javascript",
"text": "docs.contribute Fixed typo.",
"updated": 1610742512175
"text": "app.contribute Fixed typo.",
"updated": 1615461382913
},
{
"style": "Title",
@@ -14,8 +14,8 @@
},
{
"style": "Json",
"text": "docs.update",
"updated": 1610710006400
"text": "app.update",
"updated": 1615612258411
},
{
"style": "Title",
@@ -9,33 +9,37 @@
{
"language": "ID",
"text": "Proses pembaruan berhasil dilaksanakan. Versi terbaru Superalgos telah selesai diunduh dan mengganti versi yang sekarang. Agar perubahan diterapkan dengan baik, anda harus memulai ulang Client ini dan merefresh halaman UI ini.",
"updated": 1613891855110
"updated": 1613891855110,
"style": "Definition"
}
]
},
"paragraphs": [
{
"style": "Success",
"text": "You now have the latest version of Superalgos. Just remeber to restart the Client and the UI. If you have Tasks running, you should first stop the tasks, and then restart the Client, so that you can run those task again using the updated codebase.",
"updated": 1611160648343,
"text": "You now have the latest version of Superalgos. Just remember to restart the Client and the UI. If you have Tasks running, you should first stop the tasks, and then restart the Client, so that you can run those task again using the updated codebase.",
"updated": 1615458633005,
"translations": [
{
"language": "ID",
"text": "Sekarang anda memiliki Superalgos versi terbaru. Perlu diingat untuk selalu memulai ulang Client dan UI. Jika memiliki Task yang berjalan, maka anda harus menghentikan Task tersebut terlebih dahulu, lalu memulai ulang Client, sehingga anda dapat menjalankan Task tersebut lagi menggunakan codebase yang terupdate.",
"updated": 1613892095568
"updated": 1613892095568,
"style": "Success"
}
]
},
{
"style": "Note",
"text": "The update you made, only afffects the Client which you are connected to via http using your browser. If you have more Clients running as part of a Network, you will need to update them too, etither manually using Git or by navigating with your browser to their URL and doing running the App Update Command there.",
"text": "The update you made, only affects the Client which you are connected to via http using your browser. If you have more Clients running as part of a Network, you will need to update them too, etither manually using Git or by navigating with your browser to their URL and doing running the App Update Command there.",
"translations": [
{
"language": "ID",
"text": "Pembaruan yang Anda buat, hanya mempengaruhi Client yang terhubung melalui http menggunakan browser ini. Jika Anda memiliki lebih banyak Client yang berjalan sebagai bagian dari Network, anda juga perlu memperbaruinya, baik secara manual menggunakan Git atau dengan menavigasikan browser anda ke URL tujuan dan menjalankan App Update Command di sana.",
"updated": 1613892300538
"updated": 1613892300538,
"style": "Note"
}
]
],
"updated": 1615458652244
}
]
}
@@ -3,13 +3,14 @@
"pageNumber": "5",
"type": "App Message - Update Done - Already Up-To-Date",
"definition": {
"text": "The update process run successfully but no changes where made since your installation is up-to-date with the current branch. No further action required.",
"updated": 1611128829429,
"text": "The update process run successfully but no changes were made since your installation is up-to-date with the current branch. No further action required.",
"updated": 1615458687718,
"translations": [
{
"language": "ID",
"text": "Proses pembaruan berjalan dengan lancar namun tidak ada perubahan yang terjadi karena versi Superalgos anda sudah diversi paling baru dengan cabang saat ini. Tidak perlu tindakan lebih lanjut.",
"updated": 1613892507215
"updated": 1613892507215,
"style": "Definition"
}
]
},
@@ -22,7 +23,8 @@
{
"language": "ID",
"text": "Anda sudah diversi paling up-to-date. Tidak perlu me-reboot Client atau merefresh halaman UI ini.",
"updated": 1613892633200
"updated": 1613892633200,
"style": "Success"
}
]
}
@@ -19,7 +19,8 @@
},
{
"style": "Link",
"text": "Docs Group->https://t.me/superalgosdocs"
"text": "Docs Group->t.me/superalgosdocs",
"updated": 1615385427537
},
{
"style": "Text",
@@ -3,8 +3,8 @@
"pageNumber": 21,
"type": "Docs Anchor Style",
"definition": {
"text": "Amchor paragraphs are used together with the Docs Goto Command. When you want to generate a Docs Link to an specific part of a Docs Page, you first need to create an Anchor paragraph in order to be able to create the link to that part of the page.",
"updated": 1609258745310
"text": "Anchor paragraphs are used together with the Docs Goto Command. When you want to generate a Docs Link to an specific part of a Docs Page, you first need to create an Anchor paragraph in order to be able to create the link to that part of the page.",
"updated": 1615591597546
},
"paragraphs": [
{
@@ -58,11 +58,6 @@
"text": "ABSOLUTE_DUST_IN_QUOTED_ASSET: is a numeric value that represents the distance that you allow to be between the Size Filled and the Target Side for a stage, measured in Base Asset. It's default value is Zero and you can define it at the config of a Stage node using the absoluteDustInQuotedAsset property.",
"updated": 1615054151393
},
{
"style": "Note",
"text": "The parameter that will actually be used from the two previous ones will depend on how you dedined your Target Size, either in Base Asset or Quoted Asset.",
"updated": 1615054261790
},
{
"style": "Subtitle",
"text": "Implementation",
@@ -74,8 +69,8 @@
},
{
"style": "Javascript",
"text": "switch (tradingEngineStage.stageDefinedIn.value) {\n case 'Base Asset': {\n if (\n tradingEngineStage.stageBaseAsset.sizeFilled.value\n * \n ROUNDING_ERROR_CORRECTION_FACTOR\n +\n ABSOLUTE_DUST_IN_BASE_ASSET\n >=\n tradingEngineStage.stageBaseAsset.targetSize.value\n ) {\n positionFilled()\n } else {\n checkCloseStageEvent(tradingSystemStage)\n }\n break\n }\n case 'Quoted Asset': {\n if (\n tradingEngineStage.stageQuotedAsset.sizeFilled.value\n * \n ROUNDING_ERROR_CORRECTION_FACTOR\n +\n ABSOLUTE_DUST_IN_QUOTED_ASSET\n >=\n tradingEngineStage.stageQuotedAsset.targetSize.value\n ) {\n positionFilled()\n } else {\n checkCloseStageEvent(tradingSystemStage)\n }\n break\n }\n }",
"updated": 1615054391439
"text": " if (\n tradingEngineStage.stageBaseAsset.sizeFilled.value\n *\n ROUNDING_ERROR_CORRECTION_FACTOR\n +\n ABSOLUTE_DUST_IN_BASE_ASSET\n >=\n tradingEngineStage.stageBaseAsset.targetSize.value\n ) {\n positionFilled()\n } else if (\n tradingEngineStage.stageQuotedAsset.sizeFilled.value\n *\n ROUNDING_ERROR_CORRECTION_FACTOR\n +\n ABSOLUTE_DUST_IN_QUOTED_ASSET\n >=\n tradingEngineStage.stageQuotedAsset.targetSize.value\n ) {\n positionFilled()\n } else {\n checkCloseStageEvent(tradingSystemStage)\n }",
"updated": 1615296774086
}
]
}
@@ -3,8 +3,8 @@
"pageNumber": "2",
"type": "Webhook Notifications",
"definition": {
"text": "Receive webhook notifications from other systems and use them as signals in your strategies.",
"updated": 1611486052936
"text": "Receive webhook notifications from other systems like Trading View, and use them as signals in your strategies.",
"updated": 1615365927821
},
"paragraphs": [
{
@@ -17,8 +17,29 @@
"text": "Superalgos allows you to receive webhook notifications from TradingView or any other system supporting webhooks. You may use this feature to turn notifications into signals upon which you may act from within your strategies."
},
{
"style": "Text",
"text": "The system’s Web Server receives notifications and serves them to a sensor bot running as a data mining task. The sensor creates a data product with several properties which are made available to be used from within strategies."
"style": "Title",
"text": "How does it Work?",
"updated": 1615366465462
},
{
"style": "List",
"text": "Receving Data: the Client receives via it's http interface notifications from external sources (like Trading View) and keep the data received in-memory. This happens independently of the Webhooks Sensor Bot, meaning that it does not matter if the Webhook Sensor Bot is running or not.",
"updated": 1615366502234
},
{
"style": "List",
"text": "Retrieving Data: The Webhooks Sensor Bot, when it is running, queries the Client and ask it for the in-memory data it holds.",
"updated": 1615366526381
},
{
"style": "List",
"text": "Saving Data: The Webhooks Sensor Bot saves this data to disk.",
"updated": 1615366538404
},
{
"style": "List",
"text": "Consuming Data: A Trading Bot running a Strategy, might consume the data saved by the Webhooks Sensor Bot.",
"updated": 1615366547425
},
{
"style": "Text",
@@ -26,19 +47,11 @@
},
{
"style": "Title",
"text": "Set-up the Webhooks Sensor Bot Tasks"
"text": "Set-up the Client for Webhooks"
},
{
"style": "Text",
"text": "The Masters data mine features the definition of the Webhooks sensor bot used to monitor incoming notifications. You need to run an instance of this bot in the data mining section of the exchange and market of your choice."
},
{
"style": "Text",
"text": "The default workspace does not feature these tasks. To create a task for the Webhooks sensor bot, you need to uninstall and re-install the corresponding market."
},
{
"style": "Text",
"text": "Once the Webhooks sensor task is set up and running, you are all set at the application level. It’s that simple."
"text": "There is nothing to configure in order for the Client to receive webhooks calls from external systems. The only thing you might need to do is to change the http interface port number at the Environment.js file if and only if your signals provider has any constraints regarding the port number it can send the notifications to."
},
{
"style": "Title",
@@ -54,21 +67,143 @@
},
{
"style": "Json",
"text": "{\n\"webhookURL\": \"Webhook/New-Message/Trading-View/Binance/BTC-USDT\",\n\"testMessage\": \"\"\n}",
"updated": 1611486196742
"text": "{\n\"webhookURL\": \"Webhook/New-Message/Trading-View/binance/BTC-USDT\",\n\"testMessage\": \"\"\n}",
"updated": 1615370606690
},
{
"style": "List",
"text": "webhookURL: This is the URL you need to use for testing purposes. Make sure you refer to the correct exchange and market."
"text": "webhookURL: This is the URL you need to use for testing purposes. Make sure you refer to the correct exchange and market. Something very similar to this, you will configure later at Trading View or other Signal Provider. ",
"updated": 1615368679683
},
{
"style": "List",
"text": "testMessage: Enter any message, for testing purposes."
"text": "testMessage: Enter any message, for testing purposes. This message will be what you will use at your Trading System conditions to know which type of signal you received. ",
"updated": 1615368846219
},
{
"style": "Subtitle",
"text": "testMessage Examples"
},
{
"style": "Javascript",
"text": "BUY"
},
{
"style": "Javascript",
"text": "SELL"
},
{
"style": "Javascript",
"text": "TRIGGER ON"
},
{
"style": "Javascript",
"text": "TRIGGER OFF"
},
{
"style": "Javascript",
"text": "TAKE POSITION"
},
{
"style": "Subtitle",
"text": "Send the Test Message to the Client",
"updated": 1615368890166
},
{
"style": "Text",
"text": "Once the test message is configured, select Send Test Message on the menu."
},
{
"style": "Success",
"text": "By doing this, you are simulating being Trading View's server and sending the message in the exact same way they would do it, to the Superalgos Client you are running. "
},
{
"style": "Text",
"text": "The Client does not know you are sending a Test Message, and it does not know it is you and not the real Trading View. For the Client's point of view someone sent a Webhook message to it and it will do with it what it will do with any other message of this type. Store it in-memory waiting for the Webhook Sensor Bot to fetch the pending messages, in which case the Client will delete them upon delivery to the Sensor Bot.",
"updated": 1615369237281
},
{
"style": "Note",
"text": "At the Signals Provider node you will see a message of failure or success depending if the message was succesfuly delivered to the Client or not."
},
{
"style": "Note",
"text": "At the Client 's console, you will see 3 lines of logs, aknolidging that the message was received or if something was wrong, an error message with the cause."
},
{
"style": "Text",
"text": "The success logs looks like this:"
},
{
"style": "Javascript",
"text": "[INFO] httpInterface -> Webhook -> New-Message -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> New-Message -> messageReceived = Sell\n[INFO] httpInterface -> Webhook -> New-Message -> Messeges waiting to be Ferched by Webhooks Sensor Bot = 1",
"updated": 1615370618823
},
{
"style": "Title",
"text": "Set-up the Webhooks Sensor Bot Tasks",
"updated": 1615370044893
},
{
"style": "Text",
"text": "The Masters Data Mine features the definition of the Webhooks sensor bot used to monitor incoming notifications. You need to run an instance of this bot in the data mining section of the exchange and market of your choice.",
"updated": 1615370092995
},
{
"style": "Text",
"text": "The default workspace does not feature these tasks. To create a task for the Webhooks sensor bot, you need to uninstall and re-install the corresponding market."
},
{
"style": "Success",
"text": "Once the Webhooks sensor task is set up, you are all set at the application level. It’s that simple.",
"updated": 1615370160742
},
{
"style": "Title",
"text": "Running the Webhooks Sensor Bot"
},
{
"style": "Text",
"text": "Now that you have verified that the Client is receiving the signal messages and storing them for you, the next thing to do is to run the Webhooks Sensor Bot so that it fetches the signals from the Client and saves them on disk in the format that later can be consumed from strategies.",
"updated": 1615369878800
},
{
"style": "Text",
"text": "By default, this bot will run every 30 seconds. Each time it runs you will see logs entries at the Client 's console similar to this:"
},
{
"style": "Javascript",
"text": "2021-03-10T09:57:40.111Z Binance BTC/USDT Masters Webhooks Check-Webhook Main Loop # 3\n[INFO] httpInterface -> Webhook -> Fetch-Messages -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> Fetch-Messages -> Messeges Fetched by Webhooks Sensor Bot = 0",
"updated": 1615370294905
},
{
"style": "Title",
"text": "Send Test Messages while Sensor Bot is Running",
"updated": 1615371461320
},
{
"style": "Text",
"text": "Now that you know how to send test messages, try doing that while the Sensor Bot is running. By looking at the Client 's console you will be able to see what is going on:",
"updated": 1615370466427
},
{
"style": "List",
"text": "The Client accumulates the signas received.",
"updated": 1615370477786
},
{
"style": "List",
"text": "The Sensor Bot fetches all the accumulated signals every time it runs."
},
{
"style": "Text",
"text": "In the example below I sent 3 signal messages from the UI to the Client, and then the Sensor Bot fetch them all."
},
{
"style": "Javascript",
"text": "[INFO] httpInterface -> Webhook -> New-Message -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> New-Message -> messageReceived = Sell\n[INFO] httpInterface -> Webhook -> New-Message -> Messeges waiting to be Fetched by Webhooks Sensor Bot = 1\n[INFO] httpInterface -> Webhook -> New-Message -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> New-Message -> messageReceived = Sell\n[INFO] httpInterface -> Webhook -> New-Message -> Messeges waiting to be Fetched by Webhooks Sensor Bot = 2\n[INFO] httpInterface -> Webhook -> New-Message -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> New-Message -> messageReceived = Sell\n[INFO] httpInterface -> Webhook -> New-Message -> Messeges waiting to be Fetched by Webhooks Sensor Bot = 3\n2021-03-10T10:04:40.472Z Binance BTC/USDT Masters Webhooks Check-Webhook Main Loop # 17\n[INFO] httpInterface -> Webhook -> Fetch-Messages -> Exchange-Market = binance-BTC-USDT\n[INFO] httpInterface -> Webhook -> Fetch-Messages -> Messeges Fetched by Webhooks Sensor Bot = 3",
"updated": 1615370778562
},
{
"style": "Title",
"text": "Webhooks Sensor Bot Output"
@@ -80,8 +215,18 @@
},
{
"style": "Json",
"text": "\\Data-Storage\\Exchange\\Market\\Masters\\Webhooks\\Output\\External-Signals\\Single-File\\",
"updated": 1611486278677
"text": " Data-Storage\\Project\\Superalgos\\Data-Mine\\Masters\\Webhooks\\Exchange-Id\\Market\\Output\\External-Signals\\Single-File\n",
"updated": 1615371100856
},
{
"style": "Text",
"text": "Based on our tests, the location you would find the file would be:",
"updated": 1615371008258
},
{
"style": "Json",
"text": "Data-Storage\\Project\\Superalgos\\Data-Mine\\Masters\\Webhooks\\binance\\BTC-USDT\\Output\\External-Signals\\Single-File",
"updated": 1615371017985
},
{
"style": "Text",
@@ -94,11 +239,13 @@
},
{
"style": "Text",
"text": "This is what the file may look like, assuming you sent four different test messages:"
"text": "This is what the file may look like after the 3 test messages we sent:",
"updated": 1615370943470
},
{
"style": "Json",
"text": "[[1587998459260,\"Trading-View\",\"Sell\"],[1587998475409,\"Trading-View\",\"Buy\"],[1587998530190,\"Trading-View\",\"A custom signal I just configured\"],[1588000300940,\"Trading-View\",\"This is my new message\"]]"
"text": "[[1615370665064,\"Trading-View\",\"Sell\"],[1615370665655,\"Trading-View\",\"Sell\"],[1615370666472,\"Trading-View\",\"Sell\"]]\n",
"updated": 1615370924302
},
{
"style": "Text",
@@ -106,15 +253,18 @@
},
{
"style": "List",
"text": "timestamp: the Unix datetime of the message."
"text": "timestamp: the Unix datetime of the message. This is the moment in time the Client received the signal from the external Signals provider.",
"updated": 1615371169447
},
{
"style": "List",
"text": "source: the name of the signal provider."
"text": "source: the name of the Signal Provider. This comes as part of the message sent by the provider, because is part of the URL you will specify later at the Signal Provider's system. In our example we used Trading View, but in fact it could be any other provider. You will have this property available at your Trading System, meaning that you will be able to write conditions that distingish the signal by Signal Provider.",
"updated": 1615371322994
},
{
"style": "List",
"text": "message: the message set up at the provider’s system."
"text": "message: the message set up at the provider’s system (BUY, SELL, etc.).",
"updated": 1615371373274
},
{
"style": "Title",
@@ -142,7 +292,8 @@
},
{
"style": "Javascript",
"text": "chart.atAnyTimeFrame.externalSignal.source === \"Trading-View\" && chart.atAnyTimeFrame.externalSignal.message === \"Buy\""
"text": "chart.atAnyTimeFrame.externalSignal.source === \"Trading-View\" && \nchart.atAnyTimeFrame.externalSignal.message === \"BUY\"",
"updated": 1615371544991
},
{
"style": "Text",
@@ -150,7 +301,8 @@
},
{
"style": "Javascript",
"text": "chart.atAnyTimeFrame.externalSignal.previous.message === \"Buy\""
"text": "chart.atAnyTimeFrame.externalSignal.previous.message === \"BUY\"",
"updated": 1615371536367
},
{
"style": "Text",
@@ -164,9 +316,227 @@
"style": "Text",
"text": "In the above example, the first parameter chart.atAnyTimeFrame.externalSignal is the signal object you wish to check, and the second parameter chart.at01hs.candle is any other object that is delimited in time. In this case, the condition validates true if the last message falls within the last 1-hour candle."
},
{
"style": "Warning",
"text": "The reason to make this validation is to avoid situations where the Webhook sensor bot was not running and it retrieved from the Client signals that are already old. If you do not check the age of the signal you might get into the situation where you are triggering a buy or sell based on old data."
},
{
"style": "Title",
"text": "Set-up Alerts/Messages at the Provider"
"text": "Test that the Signal is Taken by your Strategy",
"updated": 1615377044554
},
{
"style": "Text",
"text": "We will make ourselves sure that from within a strategy we can react to an external Signal. To test this, plese take the following steps. All of the following goes into the current Lighting Strike Backtesting Session / Task.",
"updated": 1615379790606
},
{
"style": "Subtitle",
"text": "Paper Trading Session",
"updated": 1615380274411
},
{
"style": "Text",
"text": "Rigth click over the Backtesting Session node and select switch to Paper Trading so that we can simulate live trading without connecting to the exchange."
},
{
"style": "Subtitle",
"text": "Set-up Session Parameters"
},
{
"style": "Text",
"text": "Since you have reinstalled the market in order for the task of the Webhooks bot to appear at the Master Trading Mine Tasks, the setup for the Lighting Strike strategy needs to be fixed. (Assuming you are at the Getting Started Workspace). This is what we are going to do:",
"updated": 1615379719899
},
{
"style": "Text",
"text": "Apply this config to your Session Base Asset",
"updated": 1615379980416
},
{
"style": "Json",
"text": "{\n \"initialBalance\": 0\n}",
"updated": 1615382028446
},
{
"style": "Text",
"text": "Apply this config to the Session Quoted Asset",
"updated": 1615379973089
},
{
"style": "Json",
"text": "{\n \"initialBalance\": 1\n}",
"updated": 1615380003924
},
{
"style": "Text",
"text": "Apply this config to the Time Range",
"updated": 1615380023496
},
{
"style": "Json",
"text": "{\n \"initialDatetime\": \"2020-09-01\",\n \"finalDatetime\": \"2030-09-02\"\n}",
"updated": 1615380126836
},
{
"style": "Text",
"text": "The initialDatetime will be ingoned because we are running a Paper Trading Session. The important thing is that the finalDatetime is in the future.",
"updated": 1615380159343
},
{
"style": "Text",
"text": "Apply this config to the Time Frame",
"updated": 1615380180549
},
{
"style": "Json",
"text": "{\n \"label\": \"01-min\"\n}",
"updated": 1615380196254
},
{
"style": "Subtitle",
"text": "Set-up the Strategy for Webhooks Signals",
"updated": 1615380571676
},
{
"style": "Text",
"text": "Inside the Lighting Strike trading system locate the Trigger On Event, and paste the following into the Javascript Node of the only condition it has.",
"updated": 1615380721252
},
{
"style": "Javascript",
"text": "chart.atAnyTimeFrame.externalSignal.message === \"TRIGGER ON\"",
"updated": 1615380737459
},
{
"style": "Text",
"text": "What we are doing here is setting the strategy to be triggered once an external signal with the message TRIGGER ON is received via the Webhook Sensor Bot."
},
{
"style": "Subtitle",
"text": "Run the Data Mining Tasks"
},
{
"style": "Text",
"text": "For this test, since we are Paper Trading, we will need a live stream of data from the exchange. We don't need to run all the Task of Mater Data Mining, only the ones needed by the Lighting Strike strategy:"
},
{
"style": "List",
"text": "Exchange Raw Data"
},
{
"style": "List",
"text": "Candles Volumens"
},
{
"style": "List",
"text": "Resistances and Supports"
},
{
"style": "List",
"text": "Webhooks "
},
{
"style": "Text",
"text": ""
},
{
"style": "Subtitle",
"text": "Run Task and Session"
},
{
"style": "Text",
"text": "Run Task and the Session, and monitor the chart that was called Backtesting (now that is our Papertrading session), at the end of the market at the 1 minute time frame. Turn on the Episodes layer and wait for a few minutes until you see the Episode icons appear at the charts.",
"updated": 1615380482184
},
{
"style": "Text",
"text": "Before running the task and session, delete the data at "
},
{
"style": "Javascript",
"text": "Data-Storage\\Project\\Superalgos\\Data-Mine\\Masters\\Webhooks",
"updated": 1615381701627
},
{
"style": "Important",
"text": "This is not necesary the first time you run this, but if you are repeating this test, you will avoid confusions since the file of signals will be accumulating the test messages you send, and at the strategy we are not checking for the age of the messages, so it is a good idea to delete this data every time you try this procedure.",
"updated": 1615381726583
},
{
"style": "Subtitle",
"text": "Set-up the Signals Provider",
"updated": 1615380812487
},
{
"style": "Text",
"text": "At the Crypto Ecosystem, paste the following config to the Signals Provider.",
"updated": 1615380855069
},
{
"style": "Javascript",
"text": "{\n \"webhookURL\": \"Webhook/New-Message/Trading-View/binance/BTC-USDT\",\n \"testMessage\": \"TRIGGER ON\"\n}",
"updated": 1615380896874
},
{
"style": "Text",
"text": "This means that when you send this test message, you will be delivering the TRIGGER ON signal expected by the strategy."
},
{
"style": "Subtitle",
"text": "Ready to Send the Test Message TRIGGER ON",
"updated": 1615381087734
},
{
"style": "Text",
"text": "Go to the charts again an turn on the Strategies layer. You should not see anything at the charts since no strategy has been triggered.",
"updated": 1615381066133
},
{
"style": "Success",
"text": "Now you can send the test message."
},
{
"style": "Text",
"text": "After you see the confirmation at the UI that the message was sent, check the Client's console to verify the message was received. Then wait until the message is picked up by the Webhooks Sensor Bot. Then wait until the Trading Bot runs again."
},
{
"style": "Text",
"text": "Go to the charts and wait until it plots the icons of the strategy, confirming that the strategy was triggered by the test message you sent."
},
{
"style": "Note",
"text": "If all this worked, it means that it will work if the message comes from Trading View or other Signal Provider too."
},
{
"style": "Text",
"text": ""
},
{
"style": "Text",
"text": ""
},
{
"style": "Title",
"text": "Set-up your Firewall / Rounter to accept Incoming Connections"
},
{
"style": "Text",
"text": "As you might have imagined, you will need to open a port at your rounter / firewall to accept incomming connections from the signal provider's servers (like Trading View for instance). This is what allows that server to connect to the server where the Client is running, enabling the Client to receive those signals."
},
{
"style": "Important",
"text": "How you configure your Rounter / Firewall is up to you, and it depends on your network setup. You will need to do your research for details on how to do it, because it depends on which rounter / firewall you are using. We suggest you to include on the rules of your Firewall / Rounter the domain name or IP address of the Signal provider, so that nobody else can connect to the Client's http interface, which has, beyond Webhooks other features you don't want to expose openly to the world without restrictions.",
"updated": 1615368540044
},
{
"style": "Text",
"text": "What we can tell you regarding this, is that the Superalgos Client is running a very simple http server at the port configured at the Environment.js file."
},
{
"style": "Title",
"text": "Set-up Alerts / Messages at the Signals Provider",
"updated": 1615368585310
},
{
"style": "Text",
@@ -182,15 +552,8 @@
},
{
"style": "Json",
"text": "http://182.45.73.1:34247/Webhook/New-Message/Trading-View/Binance/BTC-USDT"
},
{
"style": "Text",
"text": "Set-up Your Network to Accept Incoming Connections"
},
{
"style": "Text",
"text": "Needless to say, you need to configure your network / router / firewall to allow incoming webhook messages on the corresponding port."
"text": "http://182.45.73.1:34247/Webhook/New-Message/Trading-View/binance/BTC-USDT",
"updated": 1615371762205
},
{
"style": "Text",
@@ -3,8 +3,8 @@
"pageNumber": "6",
"type": "Luno Test Status",
"definition": {
"text": "Luno is tested for data mining but not for live trading",
"updated": 1615204721150
"text": "Luno is tested for data mining and for live trading",
"updated": 1615550142553
},
"paragraphs": [
{
@@ -24,23 +24,23 @@
},
{
"style": "List",
"text": "Trading Capability [**]: Unknown",
"updated": 1615204866750
"text": "Trading Capability [**]: Yes",
"updated": 1615550165906
},
{
"style": "List",
"text": "Comments: Raw Data mining is extremely slow due to exchange restrictions on their API that limit the number of data it can be requested via Rest API. One day of Raw Data takes about 5 minutes to download. Keep this in mind when setting a start date",
"updated": 1615205045729
"text": "Comments: Raw Data mining is extremely slow due to exchange restrictions on their API that limit the number of data it can be requested via Rest API. One day of Raw Data takes about 5 minutes to download. Keep this in mind when setting a start date. The reason for this is because they do not support fetching of OHCLV data - this data has to be compiled by fetching individual trades. The fetching of trades is limited to 100 trades per request, and the rate limit is 1 second.",
"updated": 1615456853257
},
{
"style": "Text",
"text": "[*] The ability to fetch historic data for backtesting purposes has been verified",
"updated": 1615211359233
"text": "[*] The ability to fetch historic data for backtesting purposes has been verified.",
"updated": 1615456911815
},
{
"style": "Text",
"text": "[**] The ability to run strategies in live-trading mode has not been verified",
"updated": 1615211364516
"text": "[**] The ability to run strategies in live-trading mode is now possible. Ensure that the version of the CCXT library being used by Superalgos is 1.42.77 or better - earlier versions of this library did not correctly apply the precision settings of the exchange in the amounts specified for creating orders.",
"updated": 1615550355806
},
{
"style": "Title",
@@ -0,0 +1,59 @@
{
"topic": "Tested Exchanges",
"pageNumber": "8",
"type": "Bittrex Test Status",
"definition": {
"text": "Bittrex is not fully tested for data mining nor for live trading",
"updated": 1615450372178
},
"paragraphs": [
{
"style": "Title",
"text": "Properties",
"updated": 1615143100216
},
{
"style": "List",
"text": "1-min OHLCVs Capability [*]: Yes",
"updated": 1615204617519
},
{
"style": "List",
"text": "Market History: Unknown",
"updated": 1615450231832
},
{
"style": "List",
"text": "Trading Capability [**]: Unknown",
"updated": 1615204866750
},
{
"style": "List",
"text": "Comments: Raw Data mining it's extremely slow due to API restriction",
"updated": 1615450349464
},
{
"style": "Text",
"text": "[*] The ability to fetch historic data for backtesting purposes has not been verified",
"updated": 1615211296452
},
{
"style": "Text",
"text": "[**] The ability to run strategies in live-trading mode has not been verified",
"updated": 1615211304255
},
{
"style": "Title",
"text": "Configuration"
},
{
"style": "Json",
"text": "{\n \"codeName\": \"bittrex\",\n \"API\": [\n {\n \"method\": \"fetch_ohlcv\",\n \"limit\": 2\n }\n ]\n}",
"updated": 1615450253869
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,59 @@
{
"topic": "Tested Exchanges",
"pageNumber": "9",
"type": "CoinbasePro Test Status",
"definition": {
"text": "Coinbase Pro is not fully tested for data mining nor for live trading",
"updated": 1615453123690
},
"paragraphs": [
{
"style": "Title",
"text": "Properties",
"updated": 1615143100216
},
{
"style": "List",
"text": "1-min OHLCVs Capability [*]: Yes",
"updated": 1615204617519
},
{
"style": "List",
"text": "Market History: Unknown",
"updated": 1615453086807
},
{
"style": "List",
"text": "Trading Capability [**]: Unknown",
"updated": 1615204866750
},
{
"style": "List",
"text": "Comments: Raw Data fetching extremely slow due to API restrictions, see config",
"updated": 1615453110186
},
{
"style": "Text",
"text": "[*] The ability to fetch historic data for backtesting purposes has not been fully verified",
"updated": 1615453130798
},
{
"style": "Text",
"text": "[**] The ability to run strategies in live-trading mode has not been verified",
"updated": 1615211304255
},
{
"style": "Title",
"text": "Configuration"
},
{
"style": "Json",
"text": "{\n \"codeName\": \"coinbasepro\",\n \"API\": [\n {\n \"method\": \"fetch_ohlcv\",\n \"limit\": 300\n }\n ]\n}",
"updated": 1615453067170
},
{
"style": "Text",
"text": ""
}
]
}
@@ -32,8 +32,8 @@
},
{
"style": "Javascript",
"text": "\nlet trailingStopLoss = 3 // Set the percentage you wish your Trailing Stop Loss to trail\n\nif ( chart.at01min.candle.close * ( 1 + (trailingStopLoss / 100) ) > tradingEngine.tradingCurrent.position.stopLoss.value ) {\nchart.at01min.candle.close * ( 1 + (trailingStopLoss / 100) )\n}\nelse {\ntradingEngine.tradingCurrent.position.stopLoss.value\n}",
"updated": 1615286314820
"text": "let trailingStopLoss = 3 // Set the percentage you wish your Trailing Stop Loss to trail\n\nif ( chart.at01min.candle.close * ( 1 + (trailingStopLoss / 100) ) < tradingEngine.tradingCurrent.position.stopLoss.value ) {\nchart.at01min.candle.close * ( 1 + (trailingStopLoss / 100) )\n}\nelse {\ntradingEngine.tradingCurrent.position.stopLoss.value\n}",
"updated": 1615297661487
},
{
"style": "Important",
@@ -39,8 +39,8 @@
},
{
"style": "Javascript",
"text": "\nlet riskPercentage = 2\n\nif ( tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value)) > tradingEngine.tradingCurrent.position.stopLoss.value ) \n\n{\ntradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value))\n}\n\nelse {\ntradingEngine.tradingCurrent.position.stopLoss.value\n}",
"updated": 1615287799360
"text": "let riskPercentage = 2\n\nif ( tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value)) < tradingEngine.tradingCurrent.position.stopLoss.value ) \n\n{\ntradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value))\n}\n\nelse {\ntradingEngine.tradingCurrent.position.stopLoss.value\n}",
"updated": 1615297434458
}
]
}
@@ -0,0 +1,43 @@
{
"topic": "User Examples for Stop Loss ",
"pageNumber": 3,
"type": " Trailing Stop Loss - Initial Stop Loss, Risk and Targets Combo",
"definition": {
"text": "Setting an initial Risk Percentage of your Total Quoted Balance, after reaching the first Profit Target your first Trailing Stop Loss takes place. After reaching second Profit Target, we'll set the Trailing Stop Loss to a fixed percentage.",
"updated": 1615298179984
},
"paragraphs": [
{
"style": "Title",
"text": "Initial Stop Loss, Risk and Target Combo",
"updated": 1615297964577
},
{
"style": "Text",
"text": "The idea of this is to initially give the position some space to move."
},
{
"style": "Text",
"text": "When ever the position reaches the first Profit Target, it will use a Trailing Stop Loss set to the Profit divided by your Profit Divider. After reaching the second Profit Target, the Trailing Stop Loss will use a fixed percentage."
},
{
"style": "Subtitle",
"text": "Example for a Long Position"
},
{
"style": "Javascript",
"text": "\nriskPercentage = 1 // Initial Stop Loss is set by this Risk Percentage out of your Total Quoted Balance\n\nprofitTarget1 = 0.5 // First Percent Profit\nprofitDivider = 2 // The profit will be divided with this value and set your Stop Loss to the Entry Rate + Half Profit\n\nprofitTarget2 = 5 // Second Percent Profit\ntrailingStopLoss = 2.5 // Fixed Trailing Percentage\n\nif ( chart.at01min.candle.close > (tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (profitTarget2 / 100))) && tradingEngine.tradingCurrent.position.stopLoss.value < (chart.at01min.candle.close * (1 - (trailingStopLoss2 / 100))) ) {\n chart.at01min.candle.close * (1 - (trailingStopLoss / 100))\n}\n\nelse if ( chart.at01min.candle.close > (tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (profitTarget1 / 100))) && tradingEngine.tradingCurrent.position.stopLoss.value < chart.at01min.candle.close - ( (chart.at01min.candle.close - tradingEngine.tradingCurrent.position.entryTargetRate.value) / profitDivider) ) {\n chart.at01min.candle.close - ( (chart.at01min.candle.close - tradingEngine.tradingCurrent.position.entryTargetRate.value) / profitDivider)\n}\n\nelse {\n tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 - (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value))\n}",
"updated": 1615298020120
},
{
"style": "Subtitle",
"text": "Example for a Short Position",
"updated": 1615298191803
},
{
"style": "Javascript",
"text": "\nriskPercentage = 1 // Initial Stop Loss is set by this Risk Percentage out of your Total Quoted Balance\n\nprofitTarget1 = 0.5 // First Percent Profit\nprofitDivider = 2 // The profit will be divided with this value and set your Stop Loss to the Entry Rate + Half Profit\n\nprofitTarget2 = 5 // Second Percent Profit\ntrailingStopLoss = 2.5 // Fixed Trailing Percentage\n\nif ( chart.at01min.candle.close < (tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 - (profitTarget2 / 100))) && tradingEngine.tradingCurrent.position.stopLoss.value > (chart.at01min.candle.close * (1 + (trailingStopLoss2 / 100))) ) {\n chart.at01min.candle.close * (1 + (trailingStopLoss / 100))\n}\n\nelse if ( chart.at01min.candle.close < (tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 - (profitTarget1 / 100))) && tradingEngine.tradingCurrent.position.stopLoss.value > chart.at01min.candle.close + ( (chart.at01min.candle.close - tradingEngine.tradingCurrent.position.entryTargetRate.value) / profitDivider) ) {\n chart.at01min.candle.close + ( (chart.at01min.candle.close + tradingEngine.tradingCurrent.position.entryTargetRate.value) / profitDivider)\n}\n\nelse {\n tradingEngine.tradingCurrent.position.entryTargetRate.value * (1 + (( tradingEngine.tradingCurrent.tradingEpisode.episodeQuotedAsset.balance.value * (riskPercentage / 100)) / tradingEngine.tradingCurrent.strategyOpenStage.stageQuotedAsset.sizeFilled.value))\n}",
"updated": 1615298026442
}
]
}
@@ -11,10 +11,14 @@
"updated": 1610815737032
},
"paragraphs": [
{
"style": "Note",
"text": "Github API is currently at the \"Getting Started\" workspace, under APIs.",
"updated": 1615593559409
},
{
"style": "Callout",
"text": "Copy the token, open the Github API node config, and paste it between the double quotes of the token parameter. Fill in the username parameter with your GitHub.com user, not your email!",
"updated": 1610984794891
"text": "Copy the token, open the Github API node config, and paste it between the double quotes of the token parameter. Fill in the username parameter with your GitHub.com user, not your email!"
},
{
"style": "Note",
@@ -17,8 +17,8 @@
},
{
"style": "Link",
"text": "as defined by John Bollinger himself!->https://www.bollingerbands.com/bollinger-band-rules",
"updated": 1610452258290
"text": "as defined by John Bollinger himself!->www.bollingerbands.com/bollinger-band-rules",
"updated": 1615384340218
},
{
"style": "Callout",
@@ -237,7 +237,7 @@ exports.newSuperalgosBotModulesHistoricOHLCVs = function (processIndex) {
}
}
if (mustLoadRawData) { // there is raw data to load
getRawDataArray() // so fetch it from the file wehere it was saved on the last run of this process
getRawDataArray() // so fetch it from the file where it was saved on the last run of this process
}
function getRawDataArray() {
mustLoadRawData = false
@@ -260,11 +260,8 @@ exports.newSuperalgosBotModulesHistoricOHLCVs = function (processIndex) {
rawDataArray = JSON.parse(text);
let dataLength = rawDataArray.length
if (dataLength > 1) {
let lastData = rawDataArray[dataLength - 1]
let lastTimeStamp = lastData[0]
rawDataArray.pop() // ditch the last ohlcv since it may be incomplete
dataLength = rawDataArray.length
since = lastTimeStamp // we will now start again from the last full ohlvc we had (instead of the beginning of the day)
since = rawDataArray[dataLength - 2][0] // set the beginning of the fetch back to the start of the second last ohlcv received
rawDataArray.pop() // ditch the last ohlcv received since it may be incomplete
} else {
rawDataArrayFile = [] // we got less than two ohlcvs so we might as well start again from the beginning of the day
}
@@ -506,9 +503,9 @@ exports.newSuperalgosBotModulesHistoricOHLCVs = function (processIndex) {
break // end of the market
} else {
if (ohlcvcArray.length > limit) {
break // get enough ohlcvs for this process loop
break // got enough ohlcvs for this process loop
} else {
await new Promise(resolve => setTimeout(resolve, rateLimit)) // wait for exchanges rateLimit before requesting again
await new Promise(resolve => setTimeout(resolve, rateLimit)) // wait for exchange's rateLimit before requesting again
}
}
}
@@ -0,0 +1 @@
build/*
@@ -1,4 +1,5 @@
*.* text eol=lf
php/test/*.php linguist-generated=true
php/*.php linguist-generated=true
python/ccxt/*.py linguist-generated=true
python/ccxt/__init__.py linguist-generated=false
@@ -246,7 +246,7 @@ These files containing derived exchange classes are transpiled from JS into Pyth
- `js/[_a-z].js` → `python/ccxt/async/[_a-z].py`
- `python/ccxt/async[_a-z].py` → `python/ccxt/[_a-z].py` (Python 3 asyncio → Python 2 sync transpilation stage)
- `python/test/test_async.py` → `python/test/test.py` (the sync test is generated from the async test)
- `python/ccxt/test/test_async.py` → `python/ccxt/test/test_sync.py` (the sync test is generated from the async test)
These Python base classes and files are not transpiled:
@@ -868,14 +868,6 @@ npm run git-unignore-generated-files
## Financial Contributions
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/ccxt).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
```
ETH 0x26a3CB49578F07000575405a57888681249c35Fd (ETH only!)
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LbT8mkAqQBphc4yxLXEDgYDfEax74et3bP
```
## Credits
@@ -1,11 +1,11 @@
# CCXT – CryptoCurrency eXchange Trading Library
[![Build Status](https://travis-ci.com/ccxt/ccxt.svg?branch=master)](https://travis-ci.com/ccxt/ccxt) [![npm](https://img.shields.io/npm/v/ccxt.svg)](https://npmjs.com/package/ccxt) [![PyPI](https://img.shields.io/pypi/v/ccxt.svg)](https://pypi.python.org/pypi/ccxt) [![NPM Downloads](https://img.shields.io/npm/dm/ccxt.svg)](https://www.npmjs.com/package/ccxt) [![Discord](https://img.shields.io/discord/690203284119617602?logo=discord&logoColor=white)](https://discord.gg/dhzSKYU) [![Supported Exchanges](https://img.shields.io/badge/exchanges-123-blue.svg)](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [![Open Collective](https://opencollective.com/ccxt/backers/badge.svg)](https://opencollective.com/ccxt)
[![Build Status](https://travis-ci.com/ccxt/ccxt.svg?branch=master)](https://travis-ci.com/ccxt/ccxt) [![npm](https://img.shields.io/npm/v/ccxt.svg)](https://npmjs.com/package/ccxt) [![PyPI](https://img.shields.io/pypi/v/ccxt.svg)](https://pypi.python.org/pypi/ccxt) [![NPM Downloads](https://img.shields.io/npm/dy/ccxt.svg)](https://www.npmjs.com/package/ccxt) [![Discord](https://img.shields.io/discord/690203284119617602?logo=discord&logoColor=white)](https://discord.gg/dhzSKYU) [![Supported Exchanges](https://img.shields.io/badge/exchanges-126-blue.svg)](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [![Open Collective](https://opencollective.com/ccxt/backers/badge.svg)](https://opencollective.com/ccxt)
[![Twitter Follow](https://img.shields.io/twitter/follow/ccxt_official.svg?style=social&label=CCXT)](https://twitter.com/ccxt_official)
A JavaScript / Python / PHP library for cryptocurrency trading and e-commerce with support for many bitcoin/ether/altcoin exchange markets and merchant APIs.
### [Install](#install) · [Usage](#usage) · [Manual](https://github.com/ccxt/ccxt/wiki) · [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) · [Examples](https://github.com/ccxt/ccxt/tree/master/examples) · [Contributing](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) · [Social](#social)
### [Install](#install) · [Usage](#usage) · [Manual](https://github.com/ccxt/ccxt/wiki) · [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) · [Examples](https://github.com/ccxt/ccxt/tree/master/examples) · [Contributing](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) · [Social](#social) · [CCXT Pro](https://ccxt.pro)
The **CCXT** library is used to connect and trade with cryptocurrency exchanges and payment processing services worldwide. It provides quick access to market data for storage, analysis, visualization, indicator development, algorithmic trading, strategy backtesting, bot programming, and related software engineering.
@@ -21,42 +21,44 @@ Current feature list:
## Sponsored Promotion
[![CCXT Pro – A JavaScript / Python / PHP cryptocurrency exchange trading WebSocket API for professionals](https://user-images.githubusercontent.com/1294454/93346896-22dfb080-f83d-11ea-957f-6b8ae008f62a.png)](https://ccxt.pro) **[A JavaScript / Python / PHP cryptocurrency exchange trading WebSocket API for professionals](https://ccxt.pro)**
[![CCXT Pro – A JavaScript / Python / PHP cryptocurrency exchange trading WebSocket API for professionals](https://user-images.githubusercontent.com/1294454/106092533-865ce180-613f-11eb-8d67-0830771fde32.png)](https://ccxt.pro)
## See Also
- <sub>[![Quadency](https://user-images.githubusercontent.com/1294454/69334382-a0618100-0c6b-11ea-9ba9-40aa97440cfa.png)](https://quadency.com?utm_source=ccxt)</sub>&nbsp; **[Quadency](https://quadency.com?utm_source=ccxt)**&nbsp;&mdash;&nbsp;trading automation terminal for leading exchanges with streaming APIs + Python IDE.
- <sub>[![TabTrader](https://user-images.githubusercontent.com/1294454/66755907-9c3e8880-eea1-11e9-846e-0bff349ceb87.png)](https://tab-trader.com/?utm_source=ccxt)</sub>&nbsp; **[TabTrader](https://tab-trader.com/?utm_source=ccxt)**&nbsp;&mdash;&nbsp;trading on all exchanges in one app. Avaliable on [Android](https://play.google.com/store/apps/details?id=com.tabtrader.android&referrer=utm_source%3Dccxt) and [iOS](https://itunes.apple.com/app/apple-store/id1095716562?mt=8).
- <sub>[![Pionex](https://user-images.githubusercontent.com/1294454/106112806-c2ec0580-615e-11eb-870c-0b7aef021db2.png)](https://www.pionex.com/en-US/sign?r=8wSFCbNk&utm_source=ad&utm_medium=banner&utm_campaign=ccxt-2)</sub>&nbsp; **[Pionex](https://www.pionex.com/en-US/sign?r=8wSFCbNk&utm_source=ad&utm_medium=banner&utm_campaign=ccxt-2)**&nbsp;&mdash;&nbsp;6 grid bots, spot-futures arb bot, smart trade and DCA bots. Free for [Android](https://play.google.com/store/apps/details?id=com.pionex.client&referrer=utm_source%3Dccxt) and [iOS](https://apps.apple.com/us/app/pionex/id1485348891?pt=ccxt&ct=inline)!
- <sub>[![AAX](https://user-images.githubusercontent.com/1294454/106509642-ef957980-64de-11eb-92e0-8323cda7874e.png)](https://www.aaxpro.com/en-US/ad-sign-up/?utm_source=ccxt&utm_medium=one-liner&utm_campaign=futures)</sub>&nbsp; **[AAX](https://www.aaxpro.com/en-US/ad-sign-up/?utm_source=ccxt&utm_medium=one-liner&utm_campaign=futures)**&nbsp;&mdash;&nbsp;trade bitcoin futures with fees as low as 0.02% and up to 100x leverage!
## Certified Cryptocurrency Exchanges
| logo | id | name | ver | doc | certified | pro |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------|:---:|:----------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [![binance](https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg)](https://www.binance.com/?ref=10205187) | binance | [Binance](https://www.binance.com/?ref=10205187) | * | [API](https://binance-docs.github.io/apidocs/spot/en) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 1 | [API](https://docs.bitfinex.com/v1/docs) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bittrex](https://user-images.githubusercontent.com/51840849/87153921-edf53180-c2c0-11ea-96b9-f2a9a95a455b.jpg)](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | bittrex | [Bittrex](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | 3 | [API](https://bittrex.github.io/api/v3) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitvavo](https://user-images.githubusercontent.com/1294454/83165440-2f1cf200-a116-11ea-9046-a255d09fb2ed.jpg)](https://bitvavo.com/?a=24F34952F7) | bitvavo | [Bitvavo](https://bitvavo.com/?a=24F34952F7) | 2 | [API](https://docs.bitvavo.com/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bytetrade](https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg)](https://www.byte-trade.com) | bytetrade | [ByteTrade](https://www.byte-trade.com) | * | [API](https://docs.byte-trade.com/#description) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![currencycom](https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg)](https://currency.com/trading/signup?c=362jaimv&pid=referral) | currencycom | [Currency.com](https://currency.com/trading/signup?c=362jaimv&pid=referral) | 1 | [API](https://currency.com/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![eterbase](https://user-images.githubusercontent.com/1294454/82067900-faeb0f80-96d9-11ea-9f22-0071cfcb9871.jpg)](https://eterbase.exchange/invite/1wjjh4Pe) | eterbase | [Eterbase](https://eterbase.exchange/invite/1wjjh4Pe) | 1 | [API](https://developers.eterbase.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![ftx](https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg)](https://ftx.com/#a=1623029) | ftx | [FTX](https://ftx.com/#a=1623029) | * | [API](https://github.com/ftexchange/ftx) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![idex](https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg)](https://idex.io) | idex | [IDEX](https://idex.io) | 2 | [API](https://docs.idex.io/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![kraken](https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg)](https://www.kraken.com) | kraken | [Kraken](https://www.kraken.com) | 0 | [API](https://www.kraken.com/features/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![upbit](https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg)](https://upbit.com) | upbit | [Upbit](https://upbit.com) | 1 | [API](https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![wavesexchange](https://user-images.githubusercontent.com/1294454/84547058-5fb27d80-ad0b-11ea-8711-78ac8b3c7f31.jpg)](https://waves.exchange) | wavesexchange | [Waves.Exchange](https://waves.exchange) | * | [API](https://docs.waves.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![xena](https://user-images.githubusercontent.com/51840849/87489843-bb469280-c64c-11ea-91aa-69c6326506af.jpg)](https://xena.exchange) | xena | [Xena Exchange](https://xena.exchange) | * | [API](https://support.xena.exchange/support/solutions/44000808700) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| logo | id | name | ver | doc | certified | pro |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------|:---:|:------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [![aax](https://user-images.githubusercontent.com/1294454/104140087-a27f2580-53c0-11eb-87c1-5d9e81208fe9.jpg)](https://www.aaxpro.com/invite/sign-up?inviteCode=JXGm5Fy7R2MB) | aax | [AAX](https://www.aaxpro.com/invite/sign-up?inviteCode=JXGm5Fy7R2MB) | 2 | [API](https://www.aaxpro.com/apidoc/index.html) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![binance](https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg)](https://www.binance.com/?ref=10205187) | binance | [Binance](https://www.binance.com/?ref=10205187) | * | [API](https://binance-docs.github.io/apidocs/spot/en) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bittrex](https://user-images.githubusercontent.com/51840849/87153921-edf53180-c2c0-11ea-96b9-f2a9a95a455b.jpg)](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | bittrex | [Bittrex](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | 3 | [API](https://bittrex.github.io/api/v3) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitvavo](https://user-images.githubusercontent.com/1294454/83165440-2f1cf200-a116-11ea-9046-a255d09fb2ed.jpg)](https://bitvavo.com/?a=24F34952F7) | bitvavo | [Bitvavo](https://bitvavo.com/?a=24F34952F7) | 2 | [API](https://docs.bitvavo.com/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bytetrade](https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg)](https://www.byte-trade.com) | bytetrade | [ByteTrade](https://www.byte-trade.com) | * | [API](https://docs.byte-trade.com/#description) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![currencycom](https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg)](https://currency.com/trading/signup?c=362jaimv&pid=referral) | currencycom | [Currency.com](https://currency.com/trading/signup?c=362jaimv&pid=referral) | 1 | [API](https://currency.com/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![eterbase](https://user-images.githubusercontent.com/1294454/82067900-faeb0f80-96d9-11ea-9f22-0071cfcb9871.jpg)](https://eterbase.exchange/invite/1wjjh4Pe) | eterbase | [Eterbase](https://eterbase.exchange/invite/1wjjh4Pe) | 1 | [API](https://developers.eterbase.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![ftx](https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg)](https://ftx.com/#a=1623029) | ftx | [FTX](https://ftx.com/#a=1623029) | * | [API](https://github.com/ftexchange/ftx) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![gopax](https://user-images.githubusercontent.com/1294454/102897212-ae8a5e00-4478-11eb-9bab-91507c643900.jpg)](https://www.gopax.co.kr) | gopax | [GOPAX](https://www.gopax.co.kr) | 1 | [API](https://gopax.github.io/API/index.en.html) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![idex](https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg)](https://idex.io) | idex | [IDEX](https://idex.io) | 2 | [API](https://docs.idex.io/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![kraken](https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg)](https://www.kraken.com) | kraken | [Kraken](https://www.kraken.com) | 0 | [API](https://www.kraken.com/features/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![wavesexchange](https://user-images.githubusercontent.com/1294454/84547058-5fb27d80-ad0b-11ea-8711-78ac8b3c7f31.jpg)](https://waves.exchange) | wavesexchange | [Waves.Exchange](https://waves.exchange) | * | [API](https://docs.waves.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![xena](https://user-images.githubusercontent.com/51840849/87489843-bb469280-c64c-11ea-91aa-69c6326506af.jpg)](https://xena.exchange) | xena | [Xena Exchange](https://xena.exchange) | * | [API](https://support.xena.exchange/support/solutions/44000808700) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
## Supported Cryptocurrency Exchange Markets
The CCXT library currently supports the following 123 cryptocurrency exchange markets and trading APIs:
The CCXT library currently supports the following 126 cryptocurrency exchange markets and trading APIs:
| logo | id | name | ver | doc | certified | pro |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-----------------------------------------------------------------------------------------|:---:|:-------------------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [![aax](https://user-images.githubusercontent.com/1294454/104140087-a27f2580-53c0-11eb-87c1-5d9e81208fe9.jpg)](https://www.aaxpro.com/invite/sign-up?inviteCode=JXGm5Fy7R2MB) | aax | [AAX](https://www.aaxpro.com/invite/sign-up?inviteCode=JXGm5Fy7R2MB) | 2 | [API](https://www.aaxpro.com/apidoc/index.html) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![acx](https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg)](https://acx.io) | acx | [ACX](https://acx.io) | 2 | [API](https://acx.io/documents/api_v2) | | |
| [![aofex](https://user-images.githubusercontent.com/51840849/77670271-056d1080-6f97-11ea-9ac2-4268e9ed0c1f.jpg)](https://aofex.com/#/register?key=9763840) | aofex | [AOFEX](https://aofex.com/#/register?key=9763840) | * | [API](https://aofex.zendesk.com/hc/en-us/sections/360005576574-API) | | |
| [![bcex](https://user-images.githubusercontent.com/51840849/77231516-851c6900-6bac-11ea-8fd6-ee5c23eddbd4.jpg)](https://www.bcex.top/register?invite_code=758978&lang=en) | bcex | [BCEX](https://www.bcex.top/register?invite_code=758978&lang=en) | 1 | [API](https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface) | | |
| [![bequant](https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg)](https://bequant.io) | bequant | [Bequant](https://bequant.io) | 2 | [API](https://api.bequant.io/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bibox](https://user-images.githubusercontent.com/51840849/77257418-3262b000-6c85-11ea-8fb8-20bdf20b3592.jpg)](https://w2.bibox365.com/login/register?invite_code=05Kj3I) | bibox | [Bibox](https://w2.bibox365.com/login/register?invite_code=05Kj3I) | 1 | [API](https://biboxcom.github.io/en/) | | |
| [![bigone](https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg)](https://b1.run/users/new?code=D3LLBVFT) | bigone | [BigONE](https://b1.run/users/new?code=D3LLBVFT) | 3 | [API](https://open.big.one/docs/api.html) | | |
@@ -66,14 +68,14 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![bitbank](https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg)](https://bitbank.cc/) | bitbank | [bitbank](https://bitbank.cc/) | 1 | [API](https://docs.bitbank.cc/) | | |
| [![bitbay](https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg)](https://auth.bitbay.net/ref/jHlbB4mIkdS1) | bitbay | [BitBay](https://auth.bitbay.net/ref/jHlbB4mIkdS1) | * | [API](https://bitbay.net/public-api) | | |
| [![bitcoincom](https://user-images.githubusercontent.com/1294454/97296144-514fa300-1861-11eb-952b-3d55d492200b.jpg)](https://exchange.bitcoin.com/referral/da948b21d6c92d69) | bitcoincom | [bitcoin.com](https://exchange.bitcoin.com/referral/da948b21d6c92d69) | 2 | [API](https://api.exchange.bitcoin.com/api/2/explore) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 1 | [API](https://docs.bitfinex.com/v1/docs) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 1 | [API](https://docs.bitfinex.com/v1/docs) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex2](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex2 | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 2 | [API](https://docs.bitfinex.com/v2/docs/) | | |
| [![bitflyer](https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg)](https://bitflyer.com) | bitflyer | [bitFlyer](https://bitflyer.com) | 1 | [API](https://lightning.bitflyer.com/docs?lang=en) | | |
| [![bitforex](https://user-images.githubusercontent.com/51840849/87295553-1160ec00-c50e-11ea-8ea0-df79276a9646.jpg)](https://www.bitforex.com/en/invitationRegister?inviterId=1867438) | bitforex | [Bitforex](https://www.bitforex.com/en/invitationRegister?inviterId=1867438) | 1 | [API](https://github.com/githubdev2020/API_Doc_en/wiki) | | |
| [![bitget](https://user-images.githubusercontent.com/51840849/88317935-a8a21c80-cd22-11ea-8e2b-4b9fac5975eb.jpg)](https://www.bitget.com/expressly?languageType=0&channelCode=ccxt&vipCode=tg9j) | bitget | [Bitget](https://www.bitget.com/expressly?languageType=0&channelCode=ccxt&vipCode=tg9j) | 3 | [API](https://bitgetlimited.github.io/apidoc/en/swap) | | |
| [![bithumb](https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg)](https://www.bithumb.com) | bithumb | [Bithumb](https://www.bithumb.com) | * | [API](https://apidocs.bithumb.com) | | |
| [![bitkk](https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg)](https://www.bitkk.com) | bitkk | [bitkk](https://www.bitkk.com) | 1 | [API](https://www.bitkk.com/i/developer) | | |
| [![bitmart](https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg)](http://www.bitmart.com/?r=rQCFLh) | bitmart | [BitMart](http://www.bitmart.com/?r=rQCFLh) | 1 | [API](https://github.com/bitmartexchange/bitmart-official-api-docs) | | |
| [![bitmart](https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg)](http://www.bitmart.com/?r=rQCFLh) | bitmart | [BitMart](http://www.bitmart.com/?r=rQCFLh) | 1 | [API](https://developer-pro.bitmart.com/) | | |
| [![bitmax](https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg)](https://bitmax.io/#/register?inviteCode=EL6BXBQM) | bitmax | [BitMax](https://bitmax.io/#/register?inviteCode=EL6BXBQM) | 1 | [API](https://bitmax-exchange.github.io/bitmax-pro-api/#bitmax-pro-api-documentation) | | |
| [![bitmex](https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg)](https://www.bitmex.com/register/upZpOX) | bitmex | [BitMEX](https://www.bitmex.com/register/upZpOX) | 1 | [API](https://www.bitmex.com/app/apiOverview) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitpanda](https://user-images.githubusercontent.com/51840849/87591171-9a377d80-c6f0-11ea-94ac-97a126eac3bc.jpg)](https://www.bitpanda.com/en/pro) | bitpanda | [Bitpanda Pro](https://www.bitpanda.com/en/pro) | 1 | [API](https://developers.bitpanda.com/exchange/) | | |
@@ -95,6 +97,7 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![bw](https://user-images.githubusercontent.com/1294454/69436317-31128c80-0d52-11ea-91d1-eb7bb5818812.jpg)](https://www.bw.com/regGetCommission/N3JuT1R3bWxKTE0) | bw | [BW](https://www.bw.com/regGetCommission/N3JuT1R3bWxKTE0) | 1 | [API](https://github.com/bw-exchange/api_docs_en/wiki) | | |
| [![bybit](https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg)](https://www.bybit.com/app/register?ref=X7Prm) | bybit | [Bybit](https://www.bybit.com/app/register?ref=X7Prm) | 2 | [API](https://bybit-exchange.github.io/docs/inverse/) | | |
| [![bytetrade](https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg)](https://www.byte-trade.com) | bytetrade | [ByteTrade](https://www.byte-trade.com) | * | [API](https://docs.byte-trade.com/#description) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![cdax](https://user-images.githubusercontent.com/1294454/102157692-fd406280-3e90-11eb-8d46-4511b617cd17.jpg)](https://cdax.io/invite?invite_code=esc74) | cdax | [CDAX](https://cdax.io/invite?invite_code=esc74) | 1 | [API](https://github.com/cloudapidoc/API_Docs) | | |
| [![cex](https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg)](https://cex.io/r/0/up105393824/0/) | cex | [CEX.IO](https://cex.io/r/0/up105393824/0/) | * | [API](https://cex.io/cex-api) | | |
| [![chilebit](https://user-images.githubusercontent.com/1294454/27991414-1298f0d8-647f-11e7-9c40-d56409266336.jpg)](https://chilebit.net) | chilebit | [ChileBit](https://chilebit.net) | 1 | [API](https://blinktrade.com/docs) | | |
| [![coinbase](https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg)](https://www.coinbase.com/join/58cbe25a355148797479dbd2) | coinbase | [Coinbase](https://www.coinbase.com/join/58cbe25a355148797479dbd2) | 2 | [API](https://developers.coinbase.com/api/v2) | | |
@@ -112,9 +115,11 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![coinspot](https://user-images.githubusercontent.com/1294454/28208429-3cacdf9a-6896-11e7-854e-4c79a772a30f.jpg)](https://www.coinspot.com.au/register?code=PJURCU) | coinspot | [CoinSpot](https://www.coinspot.com.au/register?code=PJURCU) | * | [API](https://www.coinspot.com.au/api) | | |
| [![crex24](https://user-images.githubusercontent.com/1294454/47813922-6f12cc00-dd5d-11e8-97c6-70f957712d47.jpg)](https://crex24.com/?refid=slxsjsjtil8xexl9hksr) | crex24 | [CREX24](https://crex24.com/?refid=slxsjsjtil8xexl9hksr) | 2 | [API](https://docs.crex24.com/trade-api/v2) | | |
| [![currencycom](https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg)](https://currency.com/trading/signup?c=362jaimv&pid=referral) | currencycom | [Currency.com](https://currency.com/trading/signup?c=362jaimv&pid=referral) | 1 | [API](https://currency.com/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![delta](https://user-images.githubusercontent.com/1294454/99450025-3be60a00-2931-11eb-9302-f4fd8d8589aa.jpg)](https://www.delta.exchange/app/signup/?code=IULYNB) | delta | [Delta Exchange](https://www.delta.exchange/app/signup/?code=IULYNB) | 2 | [API](https://docs.delta.exchange) | | |
| [![deribit](https://user-images.githubusercontent.com/1294454/41933112-9e2dd65a-798b-11e8-8440-5bab2959fcb8.jpg)](https://www.deribit.com/reg-1189.4038) | deribit | [Deribit](https://www.deribit.com/reg-1189.4038) | 2 | [API](https://docs.deribit.com/v2) | | |
| [![digifinex](https://user-images.githubusercontent.com/51840849/87443315-01283a00-c5fe-11ea-8628-c2a0feaf07ac.jpg)](https://www.digifinex.com/en-ww/from/DhOzBg?channelCode=ljaUPp) | digifinex | [DigiFinex](https://www.digifinex.com/en-ww/from/DhOzBg?channelCode=ljaUPp) | 3 | [API](https://docs.digifinex.com) | | |
| [![dsx](https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg)](https://dsxglobal.com) | dsx | [DSX](https://dsxglobal.com) | 3 | [API](https://dsxglobal.com/developers/publicApi) | | |
| [![equos](https://user-images.githubusercontent.com/1294454/107758499-05edd180-6d38-11eb-9e09-0b69602a7a15.jpg)](https://equos.io?referredByCode=zpa8kij4ouvBFup3) | equos | [EQUOS](https://equos.io?referredByCode=zpa8kij4ouvBFup3) | * | [API](https://developer.equos.io) | | |
| [![eterbase](https://user-images.githubusercontent.com/1294454/82067900-faeb0f80-96d9-11ea-9f22-0071cfcb9871.jpg)](https://eterbase.exchange/invite/1wjjh4Pe) | eterbase | [Eterbase](https://eterbase.exchange/invite/1wjjh4Pe) | 1 | [API](https://developers.eterbase.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![exmo](https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg)](https://exmo.me/?ref=131685) | exmo | [EXMO](https://exmo.me/?ref=131685) | 1.1 | [API](https://exmo.me/en/api_doc?ref=131685) | | |
| [![exx](https://user-images.githubusercontent.com/1294454/37770292-fbf613d0-2de4-11e8-9f79-f2dc451b8ccb.jpg)](https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1) | exx | [EXX](https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1) | * | [API](https://www.exx.com/help/restApi) | | |
@@ -125,12 +130,12 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![ftx](https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg)](https://ftx.com/#a=1623029) | ftx | [FTX](https://ftx.com/#a=1623029) | * | [API](https://github.com/ftexchange/ftx) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![gateio](https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg)](https://www.gate.io/signup/2436035) | gateio | [Gate.io](https://www.gate.io/signup/2436035) | 2 | [API](https://gate.io/api2) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![gemini](https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg)](https://gemini.com/) | gemini | [Gemini](https://gemini.com/) | 1 | [API](https://docs.gemini.com/rest-api) | | |
| [![gopax](https://user-images.githubusercontent.com/1294454/102897212-ae8a5e00-4478-11eb-9bab-91507c643900.jpg)](https://www.gopax.co.kr) | gopax | [GOPAX](https://www.gopax.co.kr) | 1 | [API](https://gopax.github.io/API/index.en.html) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![hbtc](https://user-images.githubusercontent.com/51840849/80134449-70663300-85a7-11ea-8942-e204cdeaab5d.jpg)](https://www.hbtc.com/register/O2S8NS) | hbtc | [HBTC](https://www.hbtc.com/register/O2S8NS) | 1 | [API](https://github.com/bhexopen/BHEX-OpenApi/tree/master/doc) | | |
| [![hitbtc](https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg)](https://hitbtc.com/?ref_id=5a5d39a65d466) | hitbtc | [HitBTC](https://hitbtc.com/?ref_id=5a5d39a65d466) | 2 | [API](https://api.hitbtc.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![hollaex](https://user-images.githubusercontent.com/1294454/75841031-ca375180-5ddd-11ea-8417-b975674c23cb.jpg)](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | hollaex | [HollaEx](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | 1 | [API](https://apidocs.hollaex.com) | | |
| [![huobijp](https://user-images.githubusercontent.com/1294454/85734211-85755480-b705-11ea-8b35-0b7f1db33a2f.jpg)](https://www.huobi.co.jp/register/?invite_code=znnq3) | huobijp | [Huobi Japan](https://www.huobi.co.jp/register/?invite_code=znnq3) | 1 | [API](https://api-doc.huobi.co.jp) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![huobipro](https://user-images.githubusercontent.com/1294454/76137448-22748a80-604e-11ea-8069-6e389271911d.jpg)](https://www.huobi.com/en-us/topic/invited/?invite_code=rwrd3) | huobipro | [Huobi Pro](https://www.huobi.com/en-us/topic/invited/?invite_code=rwrd3) | 1 | [API](https://huobiapi.github.io/docs/spot/v1/cn/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![huobiru](https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg)](https://www.huobi.com.ru/invite?invite_code=esc74) | huobiru | [Huobi Russia](https://www.huobi.com.ru/invite?invite_code=esc74) | 1 | [API](https://github.com/cloudapidoc/API_Docs_en) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![ice3x](https://user-images.githubusercontent.com/51840849/87460809-1dd06c00-c616-11ea-98ad-7d5e1cb7fcdd.jpg)](https://ice3x.com?ref=14341802) | ice3x | [ICE3X](https://ice3x.com?ref=14341802) | 1 | [API](https://ice3x.co.za/ice-cubed-bitcoin-exchange-api-documentation-1-june-2017) | | |
| [![idex](https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg)](https://idex.io) | idex | [IDEX](https://idex.io) | 2 | [API](https://docs.idex.io/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![independentreserve](https://user-images.githubusercontent.com/51840849/87182090-1e9e9080-c2ec-11ea-8e49-563db9a38f37.jpg)](https://www.independentreserve.com) | independentreserve | [Independent Reserve](https://www.independentreserve.com) | * | [API](https://www.independentreserve.com/API) | | |
@@ -143,11 +148,11 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![latoken](https://user-images.githubusercontent.com/1294454/61511972-24c39f00-aa01-11e9-9f7c-471f1d6e5214.jpg)](https://latoken.com) | latoken | [Latoken](https://latoken.com) | 1 | [API](https://api.latoken.com) | | |
| [![lbank](https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg)](https://www.lbex.io/invite?icode=7QCY) | lbank | [LBank](https://www.lbex.io/invite?icode=7QCY) | 1 | [API](https://github.com/LBank-exchange/lbank-official-api-docs) | | |
| [![liquid](https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg)](https://www.liquid.com/sign-up/?affiliate=SbzC62lt30976) | liquid | [Liquid](https://www.liquid.com/sign-up/?affiliate=SbzC62lt30976) | 2 | [API](https://developers.liquid.com) | | |
| [![livecoin](https://user-images.githubusercontent.com/1294454/27980768-f22fc424-638a-11e7-89c9-6010a54ff9be.jpg)](https://livecoin.net/?from=Livecoin-CQ1hfx44) | livecoin | [LiveCoin](https://livecoin.net/?from=Livecoin-CQ1hfx44) | * | [API](https://www.livecoin.net/api?lang=en) | | |
| [![luno](https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg)](https://www.luno.com/invite/44893A) | luno | [luno](https://www.luno.com/invite/44893A) | 1 | [API](https://www.luno.com/en/api) | | |
| [![lykke](https://user-images.githubusercontent.com/1294454/34487620-3139a7b0-efe6-11e7-90f5-e520cef74451.jpg)](https://www.lykke.com) | lykke | [Lykke](https://www.lykke.com) | 1 | [API](https://hft-api.lykke.com/swagger/ui/) | | |
| [![mercado](https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg)](https://www.mercadobitcoin.com.br) | mercado | [Mercado Bitcoin](https://www.mercadobitcoin.com.br) | 3 | [API](https://www.mercadobitcoin.com.br/api-doc) | | |
| [![mixcoins](https://user-images.githubusercontent.com/51840849/87460810-1dd06c00-c616-11ea-9276-956f400d6ffa.jpg)](https://mixcoins.com) | mixcoins | [MixCoins](https://mixcoins.com) | 1 | [API](https://mixcoins.com/help/api/) | | |
| [![ndax](https://user-images.githubusercontent.com/1294454/108623144-67a3ef00-744e-11eb-8140-75c6b851e945.jpg)](https://one.ndax.io/bfQiSL) | ndax | [NDAX](https://one.ndax.io/bfQiSL) | * | [API](https://apidoc.ndax.io/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![novadax](https://user-images.githubusercontent.com/1294454/92337550-2b085500-f0b3-11ea-98e7-5794fb07dd3b.jpg)](https://www.novadax.com.br/?s=ccxt) | novadax | [NovaDAX](https://www.novadax.com.br/?s=ccxt) | 1 | [API](https://doc.novadax.com/pt-BR/) | | |
| [![oceanex](https://user-images.githubusercontent.com/1294454/58385970-794e2d80-8001-11e9-889c-0567cd79b78e.jpg)](https://oceanex.pro/signup?referral=VE24QX) | oceanex | [OceanEx](https://oceanex.pro/signup?referral=VE24QX) | 1 | [API](https://api.oceanex.pro/doc/v1) | | |
| [![okcoin](https://user-images.githubusercontent.com/51840849/87295551-102fbf00-c50e-11ea-90a9-462eebba5829.jpg)](https://www.okcoin.com/account/register?flag=activity&channelId=600001513) | okcoin | [OKCoin](https://www.okcoin.com/account/register?flag=activity&channelId=600001513) | 3 | [API](https://www.okcoin.com/docs/en/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
@@ -166,7 +171,7 @@ The CCXT library currently supports the following 123 cryptocurrency exchange ma
| [![tidebit](https://user-images.githubusercontent.com/51840849/87460811-1e690280-c616-11ea-8652-69f187305add.jpg)](http://bit.ly/2IX0LrM) | tidebit | [TideBit](http://bit.ly/2IX0LrM) | 2 | [API](https://www.tidebit.com/documents/api/guide) | | |
| [![tidex](https://user-images.githubusercontent.com/1294454/30781780-03149dc4-a12e-11e7-82bb-313b269d24d4.jpg)](https://tidex.com/exchange/?ref=57f5638d9cd7) | tidex | [Tidex](https://tidex.com/exchange/?ref=57f5638d9cd7) | 3 | [API](https://tidex.com/exchange/public-api) | | |
| [![timex](https://user-images.githubusercontent.com/1294454/70423869-6839ab00-1a7f-11ea-8f94-13ae72c31115.jpg)](https://timex.io/?refcode=1x27vNkTbP1uwkCck) | timex | [TimeX](https://timex.io/?refcode=1x27vNkTbP1uwkCck) | 1 | [API](https://docs.timex.io) | | |
| [![upbit](https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg)](https://upbit.com) | upbit | [Upbit](https://upbit.com) | 1 | [API](https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![upbit](https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg)](https://upbit.com) | upbit | [Upbit](https://upbit.com) | 1 | [API](https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![vaultoro](https://user-images.githubusercontent.com/1294454/27766880-f205e870-5ee9-11e7-8fe2-0d5b15880752.jpg)](https://www.vaultoro.com) | vaultoro | [Vaultoro](https://www.vaultoro.com) | 1 | [API](https://api.vaultoro.com) | | |
| [![vbtc](https://user-images.githubusercontent.com/1294454/27991481-1f53d1d8-6481-11e7-884e-21d17e7939db.jpg)](https://vbtc.exchange) | vbtc | [VBTC](https://vbtc.exchange) | 1 | [API](https://blinktrade.com/docs) | | |
| [![vcc](https://user-images.githubusercontent.com/1294454/100545356-8427f500-326c-11eb-9539-7d338242d61b.jpg)](https://vcc.exchange?ref=l4xhrH) | vcc | [VCC Exchange](https://vcc.exchange?ref=l4xhrH) | 3 | [API](https://vcc.exchange/api) | | |
@@ -224,13 +229,13 @@ console.log (ccxt.exchanges) // print all available exchanges
All-in-one browser bundle (dependencies included), served from a CDN of your choice:
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@1.38.76/dist/ccxt.browser.js
* unpkg: https://unpkg.com/ccxt@1.38.76/dist/ccxt.browser.js
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@1.42.77/dist/ccxt.browser.js
* unpkg: https://unpkg.com/ccxt@1.42.77/dist/ccxt.browser.js
CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
```HTML
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@1.38.76/dist/ccxt.browser.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@1.42.77/dist/ccxt.browser.js"></script>
```
Creates a global `ccxt` object:
@@ -275,6 +280,8 @@ include "ccxt.php";
var_dump (\ccxt\Exchange::$exchanges); // print a list of all available exchange classes
```
The library supports concurrent asynchronous mode using tools from [RecoilPHP](https://github.com/recoilphp/recoil) and [ReactPHP](https://reactphp.org/) in PHP 7.1+. Read the [Manual](https://github.com/ccxt/ccxt/wiki) for more details.
### Docker
You can get CCXT installed in a container along with all the supported languages and dependencies. This may be useful if you want to contribute to CCXT (e.g. run the build scripts and tests — please see the [Contributing](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) document for the details on that).
@@ -529,15 +536,6 @@ Thank you to all our backers! [[Become a backer](https://opencollective.com/ccxt
<a href="https://opencollective.com/ccxt#backers" target="_blank"><img src="https://opencollective.com/ccxt/tiers/backer.svg?width=890"></a>
### Crypto
```
ETH 0x26a3CB49578F07000575405a57888681249c35Fd (ETH only)
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LgM5hkvPzytEFfQGGDFGKYkE743jwUA7qv
```
Thank you!
## Social
@@ -274,6 +274,7 @@ class Transpiler {
[ /(?<=[^\s#]) \]/g, ']' ], // PEP8 E202 remove whitespaces before right ] square bracket
[ /(?<=[^\s#]) \}/g, '}' ], // PEP8 E202 remove whitespaces before right } bracket
[ /([^a-z])(elif|if|or|else)\(/g, '$1$2 \(' ], // a correction for PEP8 E225 side-effect for compound and ternary conditionals
[ /\!\=\sTrue/g, 'is not True' ], // a correction for PEP8 E712, it likes "is not True", not "!= True"
[ /\=\=\sTrue/g, 'is True' ], // a correction for PEP8 E712, it likes "is True", not "== True"
[ /\sdelete\s/g, ' del ' ],
[ /(?<!#.+)null/, 'None' ],
@@ -286,6 +287,12 @@ class Transpiler {
]
}
getSyncPHPRegexes () {
return [
[ /\byield /g, '' ]
]
}
getPHPRegexes () {
return [
[ /\{([a-zA-Z0-9_-]+?)\}/g, '~$1~' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets)
@@ -359,7 +366,7 @@ class Transpiler {
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 $4;$5) {' ],
[ /([^\s]+)\.length\;/g, 'is_array($1) ? count($1) : 0;' ],
[ /\.push\s*\(([\s\S]+?)\)\;/g, '[] = $1;' ],
[ /(\s)await(\s)/g, '$1' ],
[ /(\b)await(\b)/g, 'yield' ],
[ /([\S])\: /g, '$1 => ' ],
// add {}-array syntax conversions up to 20 levels deep
@@ -441,7 +448,7 @@ class Transpiler {
"import os",
"import sys",
"",
"root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))",
"root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))",
"sys.path.append(root)",
"",
"# ----------------------------------------------------------------------------",
@@ -598,11 +605,11 @@ class Transpiler {
return 'class ' + className + ' extends ' + baseClass + ' {'
}
createPHPClassHeader (className, baseClass, bodyAsString) {
createPHPClassHeader (className, baseClass, bodyAsString, namespace) {
return [
"<?php",
"",
"namespace ccxt;",
"namespace " + namespace + ";",
"",
"// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
@@ -611,11 +618,11 @@ class Transpiler {
]
}
createPHPClass (className, baseClass, body, methods) {
createPHPClass (className, baseClass, body, methods, async = false) {
let bodyAsString = body.join ("\n")
let header = this.createPHPClassHeader (className, baseClass, bodyAsString)
let header = this.createPHPClassHeader (className, baseClass, bodyAsString, async ? 'ccxt\\async' : 'ccxt')
const errorImports = []
@@ -693,6 +700,15 @@ class Transpiler {
// ------------------------------------------------------------------------
transpileAsyncPHPToSyncPHP (php) {
// remove yield from php body
return this.regexAll (php, this.getSyncPHPRegexes ())
}
// ------------------------------------------------------------------------
transpileJavaScriptToPHP ({ js, variables }) {
// match all local variables (let, const or var)
@@ -756,32 +772,35 @@ class Transpiler {
// remove await from Python 2 body (transpile Python 3 → Python 2)
let python2Body = this.transpilePython3ToPython2 (python3Body)
// transpile JS → PHP
let phpBody = this.transpileJavaScriptToPHP (args)
// transpile JS → Async PHP
let phpAsyncBody = this.transpileJavaScriptToPHP (args)
return { python3Body, python2Body, phpBody }
// transpile async PHP -> sync PHP
let phpBody = this.transpileAsyncPHPToSyncPHP (phpAsyncBody)
return { python3Body, python2Body, phpBody, phpAsyncBody }
}
//-----------------------------------------------------------------------------
transpilePythonAsyncToSync () {
const async = './python/test/test_async.py'
const sync = './python/test/test.py'
const async = './python/ccxt/test/test_async.py'
const sync = './python/ccxt/test/test_sync.py'
log.magenta ('Transpiling ' + async .yellow + ' → ' + sync.yellow)
const fileContents = fs.readFileSync (async, 'utf8')
let lines = fileContents.split ("\n")
lines = lines.filter (line => ![ 'import asyncio' ].includes (line))
.map (line => {
return (
line.replace ('asyncio.get_event_loop().run_until_complete(main())', 'main()')
.replace ('import ccxt.async_support as ccxt', 'import ccxt')
.replace (/.*token\_bucket.*/g, '')
.replace ('await asyncio.sleep', 'time.sleep')
.replace ('async ', '')
.replace ('await ', ''))
})
.map (line => {
return (
line.replace ('asyncio.get_event_loop().run_until_complete(main())', 'main()')
.replace ('import ccxt.async_support as ccxt', 'import ccxt')
.replace (/.*token\_bucket.*/g, '')
.replace ('await asyncio.sleep', 'time.sleep')
.replace ('async ', '')
.replace ('await ', ''))
})
// lines.forEach (line => log (line))
@@ -803,22 +822,47 @@ class Transpiler {
fs.writeFileSync (sync, newContents)
}
//-----------------------------------------------------------------------------
transpilePhpAsyncToSync () {
const async = './php/test/test_async.php'
const sync = './php/test/test_sync.php'
log.magenta ('Transpiling ' + async .yellow + ' → ' + sync.yellow)
const fileContents = fs.readFileSync (async, 'utf8')
const syncBody = this.transpileAsyncPHPToSyncPHP (fileContents)
const phpSyncRegexes = [
[ /\$kernel->execute\(\$main\)/, '\$main()' ],
[ /\$kernel->run\(\);\n/, '' ],
[ /\$kernel = async\\Exchange::get_kernel\(\);\n/, '' ],
[ /ccxt\\\\async/, 'ccxt' ],
]
const newContents = this.regexAll (syncBody, phpSyncRegexes)
fs.truncateSync (sync)
fs.writeFileSync (sync, newContents)
}
// ------------------------------------------------------------------------
getExchangeClassDeclarationMatches (contents) {
return contents.match (/^module\.exports\s*=\s*class\s+([\S]+)\s+extends\s+([\S]+)\s+{([\s\S]+?)^};*/m)
}
// ------------------------------------------------------------------------
transpileDerivedExchangeClass (contents, methodNames = undefined) {
let exchangeClassDeclarationMatches = contents.match (/^module\.exports\s*=\s*class\s+([\S]+)\s+extends\s+([\S]+)\s+{([\s\S]+?)^};*/m)
const [ _, className, baseClass, methodMatches ] = this.getExchangeClassDeclarationMatches (contents)
// log.green (file, exchangeClassDeclarationMatches[3])
let className = exchangeClassDeclarationMatches[1]
let baseClass = exchangeClassDeclarationMatches[2]
let methods = exchangeClassDeclarationMatches[3].trim ().split (/\n\s*\n/)
const methods = methodMatches.trim ().split (/\n\s*\n/)
let python2 = []
let python3 = []
let php = []
let phpAsync = []
methodNames = [] // methodNames || []
@@ -838,14 +882,6 @@ class Transpiler {
// async or not
let keyword = matches[1]
// try {
// keyword = matches[1]
// } catch (e) {
// log.red (e)
// log.green (methods[i])
// log.yellow (exchangeClassDeclarationMatches[3].trim ().split (/\n\s*\n/))
// process.exit ()
// }
// method name
let method = matches[2]
@@ -869,17 +905,17 @@ class Transpiler {
// remove excessive spacing from argument defaults in Python method signature
let pythonArgs = args.map (x => x.replace (' = ', '='))
.join (', ')
.replace (/undefined/g, 'None')
.replace (/false/g, 'False')
.replace (/true/g, 'True')
.join (', ')
.replace (/undefined/g, 'None')
.replace (/false/g, 'False')
.replace (/true/g, 'True')
// method body without the signature (first line)
// and without the closing bracket (last line)
let js = lines.slice (1, -1).join ("\n")
// transpile everything
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, className, variables, removeEmptyLines: true })
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, className, variables, removeEmptyLines: true })
// compile the final Python code for the method signature
let pythonString = 'def ' + method + '(self' + (pythonArgs.length ? ', ' + pythonArgs : '') + '):'
@@ -900,14 +936,19 @@ class Transpiler {
php.push (phpBody);
php.push (' }')
phpAsync.push ('');
phpAsync.push (' public function ' + method + '(' + phpArgs + ') {');
phpAsync.push (phpAsyncBody);
phpAsync.push (' }')
}
return {
// altogether in PHP, Python 2 and 3 (async)
python2: this.createPythonClass (className, baseClass, python2, methodNames),
python3: this.createPythonClass (className, baseClass, python3, methodNames, true),
php: this.createPHPClass (className, baseClass, php, methodNames),
// altogether in PHP, async PHP, Python 2 and 3 (async)
python2: this.createPythonClass (className, baseClass, python2, methodNames),
python3: this.createPythonClass (className, baseClass, python3, methodNames, true),
php: this.createPHPClass (className, baseClass, php, methodNames),
phpAsync: this.createPHPClass (className, baseClass, phpAsync, methodNames, true),
className,
baseClass,
@@ -916,46 +957,58 @@ class Transpiler {
// ========================================================================
transpileDerivedExchangeFile (jsFolder, filename, options) {
transpileDerivedExchangeFile (jsFolder, filename, options, force = false) {
// todo normalize jsFolder and other arguments
try {
const { python2Folder, python3Folder, phpFolder } = options
const path = jsFolder + filename
const contents = fs.readFileSync (path, 'utf8')
const { python2Folder, python3Folder, phpFolder, phpAsyncFolder } = options
const pythonFilename = filename.replace ('.js', '.py')
const phpFilename = filename.replace ('.js', '.php')
// function getMethodNames (object) {
// let functions = []
// let o = object
// do {
// functions = functions.concat (Object.getOwnPropertyNames (o))
// } while (o = Object.getPrototypeOf (o))
// return unique (functions.filter (f => (typeof object[f] === 'function')))
// }
// const exchangeClass = require ('.' + path)
// const exchange = new exchangeClass ()
// const methodNames = getMethodNames (exchange)
// const { python2, python3, php, className, baseClass } =
// this.transpileDerivedExchangeClass (contents, methodNames)
const jsMtime = fs.statSync (jsFolder + filename).mtime.getTime ()
const { python2, python3, php, className, baseClass } =
this.transpileDerivedExchangeClass (contents)
const python2Path = python2Folder ? (python2Folder + pythonFilename) : undefined
const python3Path = python3Folder ? (python3Folder + pythonFilename) : undefined
const phpPath = phpFolder ? (phpFolder + phpFilename) : undefined
const phpAsyncPath = phpAsyncFolder ? (phpAsyncFolder + phpFilename) : undefined
log.cyan ('Transpiling from', filename.yellow)
const python2Mtime = python2Folder ? (fs.existsSync (python2Path) ? fs.statSync (python2Path).mtime.getTime () : 0) : undefined
const python3Mtime = python3Path ? (fs.existsSync (python3Path) ? fs.statSync (python3Path).mtime.getTime () : 0) : undefined
const phpAsyncMtime = phpAsyncFolder ? (fs.existsSync (phpAsyncPath) ? fs.statSync (phpAsyncPath).mtime.getTime () : 0) : undefined
const phpMtime = phpPath ? (fs.existsSync (phpPath) ? fs.statSync (phpPath).mtime.getTime () : 0) : undefined
;[
[ python2Folder, filename.replace ('.js', '.py'), python2 ],
[ python3Folder, filename.replace ('.js', '.py'), python3 ],
[ phpFolder, filename.replace ('.js', '.php'), php ],
].forEach (([ folder, filename, code ]) => {
if (folder) {
overwriteFile (folder + filename, code)
}
})
const contents = fs.readFileSync (jsFolder + filename, 'utf8')
return { className, baseClass }
if (force ||
(python3Folder && (jsMtime > python3Mtime)) ||
(phpFolder && (jsMtime > phpMtime)) ||
(phpAsyncFolder && (jsMtime > phpAsyncMtime)) ||
(python2Folder && (jsMtime > python2Mtime))) {
const { python2, python3, php, phpAsync, className, baseClass } = this.transpileDerivedExchangeClass (contents)
log.cyan ('Transpiling from', filename.yellow)
;[
[ python2Folder, pythonFilename, python2 ],
[ python3Folder, pythonFilename, python3 ],
[ phpFolder, phpFilename, php ],
[ phpAsyncFolder, phpFilename, phpAsync ],
].forEach (([ folder, filename, code ]) => {
if (folder) {
overwriteFile (folder + filename, code)
fs.utimesSync (folder + filename, new Date (), new Date (jsMtime))
}
})
return { className, baseClass }
} else {
const [ _, className, baseClass ] = this.getExchangeClassDeclarationMatches (contents)
log.green ('Already transpiled', filename.yellow)
return { className, baseClass }
}
} catch (e) {
@@ -967,11 +1020,11 @@ class Transpiler {
//-------------------------------------------------------------------------
transpileDerivedExchangeFiles (jsFolder, options, pattern = '.js') {
transpileDerivedExchangeFiles (jsFolder, options, pattern = '.js', force = false) {
// todo normalize jsFolder and other arguments
const { python2Folder, python3Folder, phpFolder } = options
const { python2Folder, python3Folder, phpFolder, phpAsyncFolder } = options
// exchanges.json accounts for ids included in exchanges.cfg
let ids = undefined
@@ -984,7 +1037,7 @@ class Transpiler {
const classNames = fs.readdirSync (jsFolder)
.filter (file => file.match (regex) && (!ids || ids.includes (basename (file, '.js'))))
.map (file => this.transpileDerivedExchangeFile (jsFolder, file, options))
.map (file => this.transpileDerivedExchangeFile (jsFolder, file, options, force))
const classes = {}
@@ -1013,6 +1066,7 @@ class Transpiler {
[ python2Folder, /\.pyc?$/ ],
[ python3Folder, /\.pyc?$/ ],
[ phpFolder, /\.php$/ ],
[ phpAsyncFolder, /\.php$/ ],
].forEach (([ folder, pattern ]) => {
if (folder) {
deleteOldTranspiledFiles (folder, pattern)
@@ -1026,19 +1080,37 @@ class Transpiler {
// ========================================================================
exportTypeScriptDeclarations (file, classes) {
exportTypeScriptClassNames (file, classes) {
log.bright.cyan ('Exporting TypeScript declarations →', file.yellow)
log.bright.cyan ('Exporting TypeScript class names →', file.yellow)
const regex = /\/[\n]{2}(?: export class [^\s]+ extends [^\s]+ \{\}[\r]?[\n])+/
const replacement = "/\n\n" + Object.keys (classes).map (className => {
const baseClass = classes[className]
const baseClass = classes[className].replace (/ccxt\.[a-z_]+/, 'Exchange')
return ' export class ' + className + ' extends ' + baseClass + " {}"
}).join ("\n") + "\n"
replaceInFile (file, regex, replacement)
}
exportTypeScriptExchangeIds (file, classes) {
log.bright.cyan ('Exporting TypeScript exchange ids →', file.yellow)
const regex = /\/[\n]{2} export type ExchangeId =\n(?: \| \'[a-z0-9_]+\'[\r]?[\n])+/
const replacement = "/\n\n export type ExchangeId =\n" + Object.keys (classes).map (className => {
return " | '" + className + "'"
}).join ("\n") + "\n"
replaceInFile (file, regex, replacement)
}
exportTypeScriptDeclarations (file, classes) {
this.exportTypeScriptClassNames (file, classes)
this.exportTypeScriptExchangeIds (file, classes)
}
// ========================================================================
transpileErrorHierarchy () {
@@ -1092,7 +1164,7 @@ class Transpiler {
const quote = (s) => "'" + s + "'" // helper to add quotes around class names
const pythonExports = [ 'error_hierarchy' ]
const pythonExports = [ 'error_hierarchy', 'BaseError' ]
const pythonErrors = intellisense (root, 'BaseError', pythonDeclareErrorClass, pythonExports)
const pythonAll = '__all__ = [\n ' + pythonExports.map (quote).join (',\n ') + '\n]'
@@ -1157,7 +1229,7 @@ class Transpiler {
transpileDateTimeTests () {
const jsFile = './js/test/base/functions/test.datetime.js'
const pyFile = './python/test/test_exchange_datetime_functions.py'
const pyFile = './python/ccxt/test/test_exchange_datetime_functions.py'
const phpFile = './php/test/test_exchange_datetime_functions.php'
log.magenta ('Transpiling from', jsFile.yellow)
@@ -1169,7 +1241,7 @@ class Transpiler {
[/^\/\*.*\s+/mg, ''],
])
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
// phpBody = phpBody.replace (/exchange\./g, 'Exchange::')
@@ -1198,7 +1270,7 @@ class Transpiler {
transpilePrecisionTests () {
const jsFile = './js/test/base/functions/test.number.js'
const pyFile = './python/test/test_decimal_to_precision.py'
const pyFile = './python/ccxt/test/test_decimal_to_precision.py'
const phpFile = './php/test/decimal_to_precision.php'
log.magenta ('Transpiling from', jsFile.yellow)
@@ -1212,7 +1284,7 @@ class Transpiler {
[ /numberToString/g, 'number_to_string' ],
])
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
const pythonHeader = [
"",
@@ -1283,7 +1355,7 @@ class Transpiler {
transpileCryptoTests () {
const jsFile = './js/test/base/functions/test.crypto.js'
const pyFile = './python/test/test_crypto.py'
const pyFile = './python/ccxt/test/test_crypto.py'
const phpFile = './php/test/test_crypto.php'
log.magenta ('Transpiling from', jsFile.yellow)
@@ -1295,7 +1367,7 @@ class Transpiler {
[ /function equals \([\S\s]+?return true\n}\n/g, '' ],
])
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
const pythonHeader = [
"",
@@ -1352,22 +1424,22 @@ class Transpiler {
const tests = [
{
'jsFile': './js/test/Exchange/test.trade.js',
'pyFile': './python/test/test_trade.py',
'pyFile': './python/ccxt/test/test_trade.py',
'phpFile': './php/test/test_trade.php',
},
{
'jsFile': './js/test/Exchange/test.order.js',
'pyFile': './python/test/test_order.py',
'pyFile': './python/ccxt/test/test_order.py',
'phpFile': './php/test/test_order.php',
},
{
'jsFile': './js/test/Exchange/test.transaction.js',
'pyFile': './python/test/test_transaction.py',
'pyFile': './python/ccxt/test/test_transaction.py',
'phpFile': './php/test/test_transaction.php',
},
{
'jsFile': './js/test/Exchange/test.ohlcv.js',
'pyFile': './python/test/test_ohlcv.py',
'pyFile': './python/ccxt/test/test_ohlcv.py',
'phpFile': './php/test/test_ohlcv.php',
},
]
@@ -1389,7 +1461,6 @@ class Transpiler {
])
const pythonHeader = [
'',
'import numbers # noqa: E402',
'try:',
' basestring # basestring was removed in Python 3',
@@ -1400,7 +1471,7 @@ class Transpiler {
].join('\n')
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
const python = this.getPythonPreamble () + pythonHeader + python3Body;
const python = pythonHeader + python3Body;
const php = this.getPHPPreamble (false) + phpBody;
log.magenta ('→', test.pyFile.yellow)
@@ -1423,22 +1494,53 @@ class Transpiler {
// ============================================================================
transpileEverything () {
transpilePhpBaseClassMethods () {
const baseMethods = this.getPHPBaseMethods ()
const indent = 4
const space = ' '.repeat (indent)
const result = [
'public static $camelcase_methods = array(',
]
for (const method of baseMethods) {
const underscoreCase = unCamelCase (method)
if (underscoreCase !== method) {
result.push (space.repeat (2) + '\'' + method + '\' => ' + '\'' + underscoreCase + '\',')
}
}
result.push (space + ');')
const string = result.join ('\n')
const phpBaseClass = './php/base/Exchange.php';
const phpBody = fs.readFileSync (phpBaseClass, 'utf8')
const regex = /public static \$camelcase_methods = array\([\s\S]+?\);/g
const bodyArray = phpBody.split (regex)
const newBody = bodyArray[0] + string + bodyArray[1]
log.magenta ('Transpiling from ', phpBaseClass.yellow, '→', phpBaseClass.yellow)
overwriteFile (phpBaseClass, newBody)
}
// ============================================================================
transpileEverything (force = false) {
// default pattern is '.js'
const [ /* node */, /* script */, pattern ] = process.argv
, python2Folder = './python/ccxt/'
, python3Folder = './python/ccxt/async_support/'
, phpFolder = './php/'
, options = { python2Folder, python3Folder, phpFolder }
const [ /* node */, /* script */, pattern ] = process.argv.filter (x => !x.startsWith ('--'))
, python2Folder = './python/ccxt/'
, python3Folder = './python/ccxt/async_support/'
, phpFolder = './php/'
, phpAsyncFolder = './php/async/'
, options = { python2Folder, python3Folder, phpFolder, phpAsyncFolder }
createFolderRecursively (python2Folder)
createFolderRecursively (python3Folder)
createFolderRecursively (phpFolder)
createFolderRecursively (phpAsyncFolder)
//*
const classes = this.transpileDerivedExchangeFiles ('./js/', options, pattern)
const classes = this.transpileDerivedExchangeFiles ('./js/', options, pattern, force)
if (classes === null) {
log.bright.yellow ('0 files transpiled.')
@@ -1457,6 +1559,10 @@ class Transpiler {
this.transpilePythonAsyncToSync ()
this.transpilePhpAsyncToSync ()
this.transpilePhpBaseClassMethods ()
log.bright.green ('Transpiled successfully.')
}
}
@@ -1469,12 +1575,14 @@ if (require.main === module) { // called directly like `node module`
const transpiler = new Transpiler ()
const test = process.argv.includes ('--test') || process.argv.includes ('--tests')
const errors = process.argv.includes ('--error') || process.argv.includes ('--errors')
const force = process.argv.includes ('--force')
log.bright.green ({ force })
if (test) {
transpiler.transpileTests ()
} else if (errors) {
transpiler.transpileErrorHierarchy ()
} else {
transpiler.transpileEverything ()
transpiler.transpileEverything (force)
}
} else { // if required as a module
@@ -52,6 +52,8 @@ function vssEverything () {
vss ('./ccxt.js', "const version = '{version}'", version)
vss ('./php/base/Exchange.php', "$version = '{version}'", version)
vss ('./php/async/base/Exchange.php', "VERSION = '{version}'", version)
vss ('./php/async/base/Exchange.php', "$version = '{version}'", version)
vss ('./php/base/Exchange.php', "VERSION = '{version}'", version)
vss ('./python/ccxt/__init__.py', "__version__ = '{version}'", version)
vss ('./python/ccxt/base/exchange.py', "__version__ = '{version}'", version)
@@ -56,16 +56,21 @@ declare module 'ccxt' {
symbol: string;
base: string;
quote: string;
baseId: string,
quoteId: string,
baseId: string;
quoteId: string;
type?: string;
spot?: boolean;
margin?: boolean;
swap?: boolean;
future?: boolean;
active: boolean;
precision: { base: number, quote: number, amount: number, price: number };
limits: { amount: MinMax, price: MinMax, cost?: MinMax };
tierBased: boolean,
percentage: boolean,
taker: number,
maker: number,
info: any,
tierBased: boolean;
percentage: boolean;
taker: number;
maker: number;
info: any;
}
export interface Order {
@@ -417,9 +422,9 @@ declare module 'ccxt' {
/* tslint:disable */
export class aax extends Exchange {}
export class acx extends Exchange {}
export class aofex extends Exchange {}
export class bcex extends Exchange {}
export class bequant extends hitbtc {}
export class bibox extends Exchange {}
export class bigone extends Exchange {}
@@ -458,6 +463,7 @@ declare module 'ccxt' {
export class bw extends Exchange {}
export class bybit extends Exchange {}
export class bytetrade extends Exchange {}
export class cdax extends huobipro {}
export class cex extends Exchange {}
export class chilebit extends foxbit {}
export class coinbase extends Exchange {}
@@ -475,9 +481,11 @@ declare module 'ccxt' {
export class coinspot extends Exchange {}
export class crex24 extends Exchange {}
export class currencycom extends Exchange {}
export class delta extends Exchange {}
export class deribit extends Exchange {}
export class digifinex extends Exchange {}
export class dsx extends Exchange {}
export class equos extends Exchange {}
export class eterbase extends Exchange {}
export class exmo extends Exchange {}
export class exx extends Exchange {}
@@ -488,12 +496,12 @@ declare module 'ccxt' {
export class ftx extends Exchange {}
export class gateio extends Exchange {}
export class gemini extends Exchange {}
export class gopax extends Exchange {}
export class hbtc extends Exchange {}
export class hitbtc extends Exchange {}
export class hollaex extends Exchange {}
export class huobijp extends huobipro {}
export class huobipro extends Exchange {}
export class huobiru extends huobipro {}
export class ice3x extends Exchange {}
export class idex extends Exchange {}
export class independentreserve extends Exchange {}
@@ -506,11 +514,11 @@ declare module 'ccxt' {
export class latoken extends Exchange {}
export class lbank extends Exchange {}
export class liquid extends Exchange {}
export class livecoin extends Exchange {}
export class luno extends Exchange {}
export class lykke extends Exchange {}
export class mercado extends Exchange {}
export class mixcoins extends Exchange {}
export class ndax extends Exchange {}
export class novadax extends Exchange {}
export class oceanex extends Exchange {}
export class okcoin extends okex {}
@@ -543,4 +551,132 @@ declare module 'ccxt' {
/* tslint:enable */
export type ExchangeId =
| 'aax'
| 'acx'
| 'aofex'
| 'bequant'
| 'bibox'
| 'bigone'
| 'binance'
| 'binanceus'
| 'bit2c'
| 'bitbank'
| 'bitbay'
| 'bitcoincom'
| 'bitfinex'
| 'bitfinex2'
| 'bitflyer'
| 'bitforex'
| 'bitget'
| 'bithumb'
| 'bitkk'
| 'bitmart'
| 'bitmax'
| 'bitmex'
| 'bitpanda'
| 'bitso'
| 'bitstamp'
| 'bitstamp1'
| 'bittrex'
| 'bitvavo'
| 'bitz'
| 'bl3p'
| 'bleutrade'
| 'braziliex'
| 'btcalpha'
| 'btcbox'
| 'btcmarkets'
| 'btctradeua'
| 'btcturk'
| 'buda'
| 'bw'
| 'bybit'
| 'bytetrade'
| 'cdax'
| 'cex'
| 'chilebit'
| 'coinbase'
| 'coinbaseprime'
| 'coinbasepro'
| 'coincheck'
| 'coinegg'
| 'coinex'
| 'coinfalcon'
| 'coinfloor'
| 'coingi'
| 'coinmarketcap'
| 'coinmate'
| 'coinone'
| 'coinspot'
| 'crex24'
| 'currencycom'
| 'delta'
| 'deribit'
| 'digifinex'
| 'dsx'
| 'equos'
| 'eterbase'
| 'exmo'
| 'exx'
| 'fcoin'
| 'fcoinjp'
| 'flowbtc'
| 'foxbit'
| 'ftx'
| 'gateio'
| 'gemini'
| 'gopax'
| 'hbtc'
| 'hitbtc'
| 'hollaex'
| 'huobijp'
| 'huobipro'
| 'ice3x'
| 'idex'
| 'independentreserve'
| 'indodax'
| 'itbit'
| 'kraken'
| 'kucoin'
| 'kuna'
| 'lakebtc'
| 'latoken'
| 'lbank'
| 'liquid'
| 'luno'
| 'lykke'
| 'mercado'
| 'mixcoins'
| 'ndax'
| 'novadax'
| 'oceanex'
| 'okcoin'
| 'okex'
| 'paymium'
| 'phemex'
| 'poloniex'
| 'probit'
| 'qtrade'
| 'rightbtc'
| 'ripio'
| 'southxchange'
| 'stex'
| 'surbitcoin'
| 'therock'
| 'tidebit'
| 'tidex'
| 'timex'
| 'upbit'
| 'vaultoro'
| 'vbtc'
| 'vcc'
| 'wavesexchange'
| 'whitebit'
| 'xbtce'
| 'xena'
| 'yobit'
| 'zaif'
| 'zb'
}
@@ -35,16 +35,16 @@ const Exchange = require ('./js/base/Exchange')
//-----------------------------------------------------------------------------
// this is updated by vss.js when building
const version = '1.38.76'
const version = '1.42.77'
Exchange.ccxtVersion = version
//-----------------------------------------------------------------------------
const exchanges = {
'aax': require ('./js/aax.js'),
'acx': require ('./js/acx.js'),
'aofex': require ('./js/aofex.js'),
'bcex': require ('./js/bcex.js'),
'bequant': require ('./js/bequant.js'),
'bibox': require ('./js/bibox.js'),
'bigone': require ('./js/bigone.js'),
@@ -83,6 +83,7 @@ const exchanges = {
'bw': require ('./js/bw.js'),
'bybit': require ('./js/bybit.js'),
'bytetrade': require ('./js/bytetrade.js'),
'cdax': require ('./js/cdax.js'),
'cex': require ('./js/cex.js'),
'chilebit': require ('./js/chilebit.js'),
'coinbase': require ('./js/coinbase.js'),
@@ -100,9 +101,11 @@ const exchanges = {
'coinspot': require ('./js/coinspot.js'),
'crex24': require ('./js/crex24.js'),
'currencycom': require ('./js/currencycom.js'),
'delta': require ('./js/delta.js'),
'deribit': require ('./js/deribit.js'),
'digifinex': require ('./js/digifinex.js'),
'dsx': require ('./js/dsx.js'),
'equos': require ('./js/equos.js'),
'eterbase': require ('./js/eterbase.js'),
'exmo': require ('./js/exmo.js'),
'exx': require ('./js/exx.js'),
@@ -113,12 +116,12 @@ const exchanges = {
'ftx': require ('./js/ftx.js'),
'gateio': require ('./js/gateio.js'),
'gemini': require ('./js/gemini.js'),
'gopax': require ('./js/gopax.js'),
'hbtc': require ('./js/hbtc.js'),
'hitbtc': require ('./js/hitbtc.js'),
'hollaex': require ('./js/hollaex.js'),
'huobijp': require ('./js/huobijp.js'),
'huobipro': require ('./js/huobipro.js'),
'huobiru': require ('./js/huobiru.js'),
'ice3x': require ('./js/ice3x.js'),
'idex': require ('./js/idex.js'),
'independentreserve': require ('./js/independentreserve.js'),
@@ -131,11 +134,11 @@ const exchanges = {
'latoken': require ('./js/latoken.js'),
'lbank': require ('./js/lbank.js'),
'liquid': require ('./js/liquid.js'),
'livecoin': require ('./js/livecoin.js'),
'luno': require ('./js/luno.js'),
'lykke': require ('./js/lykke.js'),
'mercado': require ('./js/mercado.js'),
'mixcoins': require ('./js/mixcoins.js'),
'ndax': require ('./js/ndax.js'),
'novadax': require ('./js/novadax.js'),
'oceanex': require ('./js/oceanex.js'),
'okcoin': require ('./js/okcoin.js'),
@@ -0,0 +1,2 @@
#!/bin/bash
git checkout HEAD package.json README.md ccxt.d.ts ccxt.js wiki python php dist doc
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -346,6 +346,7 @@ module.exports = class acx extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': this.safeFloat (order, 'price'),
'stopPrice': undefined,
@@ -132,6 +132,9 @@ module.exports = class aofex extends Exchange {
'show_all': '0', // '1' to show zero balances
},
},
'commonCurrencies': {
'CPC': 'Consensus Planet Coin',
},
});
}
@@ -798,6 +801,7 @@ module.exports = class aofex extends Exchange {
'symbol': market['symbol'],
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -411,20 +411,20 @@ module.exports = class Exchange {
if (!!enabled) { // eslint-disable-line no-extra-boolean-cast
if ('test' in this.urls) {
if (typeof this.urls['api'] === 'string') {
this.urls['api_backup'] = this.urls['api']
this.urls['apiBackup'] = this.urls['api']
this.urls['api'] = this.urls['test']
} else {
this.urls['api_backup'] = clone (this.urls['api'])
this.urls['apiBackup'] = clone (this.urls['api'])
this.urls['api'] = clone (this.urls['test'])
}
} else {
throw new NotSupported (this.id + ' does not have a sandbox URL')
}
} else if ('api_backup' in this.urls) {
} else if ('apiBackup' in this.urls) {
if (typeof this.urls['api'] === 'string') {
this.urls['api'] = this.urls['api_backup']
this.urls['api'] = this.urls['apiBackup']
} else {
this.urls['api'] = clone (this.urls['api_backup'])
this.urls['api'] = clone (this.urls['apiBackup'])
}
}
}
@@ -576,34 +576,31 @@ module.exports = class Exchange {
handleRestResponse (response, url, method = 'GET', requestHeaders = undefined, requestBody = undefined) {
return response.text ().then ((responseBody) => {
const json = this.parseJson (responseBody)
const responseHeaders = this.getResponseHeaders (response)
const bodyText = this.onRestResponse (response.status, response.statusText, url, method, responseHeaders, responseBody, requestHeaders, requestBody);
const json = this.parseJson (bodyText)
if (this.enableLastResponseHeaders) {
this.last_response_headers = responseHeaders
}
if (this.enableLastHttpResponse) {
this.last_http_response = responseBody
}
if (this.enableLastJsonResponse) {
this.last_json_response = json
}
if (this.verbose) {
this.print ("handleRestResponse:\n", this.id, method, url, response.status, response.statusText, "\nResponse:\n", responseHeaders, "\n", responseBody, "\n")
}
this.handleErrors (response.status, response.statusText, url, method, responseHeaders, responseBody, json, requestHeaders, requestBody)
this.handleHttpStatusCode (response.status, response.statusText, url, method, responseBody)
return json || responseBody
})
}
onRestResponse (statusCode, statusText, url, method, responseHeaders, responseBody, requestHeaders, requestBody) {
return responseBody.trim ().replace (/:(\d{15,}),/g, ':"$1",');
}
setMarkets (markets, currencies = undefined) {
const values = Object.values (markets).map ((market) => deepExtend ({
'limits': this.limits,
@@ -903,6 +900,10 @@ module.exports = class Exchange {
return symbols.map ((symbol) => this.marketId (symbol))
}
currencyIds (codes) {
return codes.map ((code) => this.currencyId (code))
}
symbol (symbol) {
return this.market (symbol).symbol || symbol
}
@@ -1036,7 +1037,7 @@ module.exports = class Exchange {
array = array.filter ((entry) => entry[key] >= since)
}
if (limit !== undefined && limit !== null) {
array = (tail && !sinceIsDefined) ? array.slice (-limit) : array.slice (0, limit)
array = tail ? array.slice (-limit) : array.slice (0, limit)
}
return array
}
@@ -1054,18 +1055,18 @@ module.exports = class Exchange {
}
if (limit !== undefined && limit !== null) {
array = (tail && !sinceIsDefined) ? array.slice (-limit) : array.slice (0, limit)
array = tail ? array.slice (-limit) : array.slice (0, limit)
}
return array
}
filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined) {
return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit)
filterBySymbolSinceLimit (array, symbol = undefined, since = undefined, limit = undefined, tail = false) {
return this.filterByValueSinceLimit (array, 'symbol', symbol, since, limit, 'timestamp', tail)
}
filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined) {
return this.filterByValueSinceLimit (array, 'currency', code, since, limit)
filterByCurrencySinceLimit (array, code = undefined, since = undefined, limit = undefined, tail = false) {
return this.filterByValueSinceLimit (array, 'currency', code, since, limit, 'timestamp', tail)
}
filterByArray (objects, key, values = undefined, indexed = true) {
@@ -1095,7 +1096,8 @@ module.exports = class Exchange {
let result = Object.values (trades || []).map ((trade) => this.extend (this.parseTrade (trade, market), params))
result = sortBy (result, 'timestamp')
const symbol = (market !== undefined) ? market['symbol'] : undefined
return this.filterBySymbolSinceLimit (result, symbol, since, limit)
const tail = since === undefined
return this.filterBySymbolSinceLimit (result, symbol, since, limit, tail)
}
parseTransactions (transactions, currency = undefined, since = undefined, limit = undefined, params = {}) {
@@ -1106,7 +1108,8 @@ module.exports = class Exchange {
let result = Object.values (transactions || []).map ((transaction) => this.extend (this.parseTransaction (transaction, currency), params))
result = this.sortBy (result, 'timestamp');
const code = (currency !== undefined) ? currency['code'] : undefined;
return this.filterByCurrencySinceLimit (result, code, since, limit);
const tail = since === undefined;
return this.filterByCurrencySinceLimit (result, code, since, limit, tail);
}
parseLedger (data, currency = undefined, since = undefined, limit = undefined, params = {}) {
@@ -1124,7 +1127,8 @@ module.exports = class Exchange {
}
result = this.sortBy (result, 'timestamp');
const code = (currency !== undefined) ? currency['code'] : undefined;
return this.filterByCurrencySinceLimit (result, code, since, limit);
const tail = since === undefined;
return this.filterByCurrencySinceLimit (result, code, since, limit, tail);
}
parseOrders (orders, market = undefined, since = undefined, limit = undefined, params = {}) {
@@ -1154,7 +1158,8 @@ module.exports = class Exchange {
Object.entries (orders).map (([ id, order ]) => this.extend (this.parseOrder (this.extend ({ 'id': id }, order), market), params))
result = sortBy (result, 'timestamp')
const symbol = (market !== undefined) ? market['symbol'] : undefined
return this.filterBySymbolSinceLimit (result, symbol, since, limit)
const tail = since === undefined
return this.filterBySymbolSinceLimit (result, symbol, since, limit, tail)
}
safeCurrency (currencyId, currency = undefined) {
@@ -1225,19 +1230,10 @@ module.exports = class Exchange {
// if (!this.isArray (ohlcvs)) {
// throw new ExchangeError (this.id + ' parseOHLCVs expected an array in the ohlcvs argument, but got ' + typeof ohlcvs);
// }
ohlcvs = Object.values (ohlcvs || [])
const result = []
for (let i = 0; i < ohlcvs.length; i++) {
if (limit && (result.length >= limit)) {
break;
}
const ohlcv = this.parseOHLCV (ohlcvs[i], market)
if (since && (ohlcv[0] < since)) {
continue
}
result.push (ohlcv)
}
return this.sortBy (result, 0)
const parsed = ohlcvs.map ((ohlcv) => this.parseOHLCV (ohlcv, market))
const sorted = this.sortBy (parsed, 0)
const tail = since === undefined
return this.filterBySinceLimit (sorted, since, limit, 0, tail)
}
editLimitBuyOrder (id, symbol, ...args) {
@@ -1260,20 +1256,20 @@ module.exports = class Exchange {
return this.createOrder (symbol, ...args)
}
createLimitOrder (symbol, ...args) {
return this.createOrder (symbol, 'limit', ...args)
createLimitOrder (symbol, side, amount, price = undefined, params = {}) {
return this.createOrder (symbol, 'limit', side, amount, price, params)
}
createMarketOrder (symbol, ...args) {
return this.createOrder (symbol, 'market', ...args)
createMarketOrder (symbol, side, amount, price = undefined, params = {}) {
return this.createOrder (symbol, 'market', side, amount, price, params)
}
createLimitBuyOrder (symbol, ...args) {
return this.createOrder (symbol, 'limit', 'buy', ...args)
createLimitBuyOrder (symbol, amount, price = undefined, params = {}) {
return this.createOrder (symbol, 'limit', 'buy', amount, price, params)
}
createLimitSellOrder (symbol, ...args) {
return this.createOrder (symbol, 'limit', 'sell', ...args)
createLimitSellOrder (symbol, amount, price = undefined, params = {}) {
return this.createOrder (symbol, 'limit', 'sell', amount, price, params)
}
createMarketBuyOrder (symbol, amount, params = {}) {
@@ -1285,7 +1281,7 @@ module.exports = class Exchange {
}
costToPrecision (symbol, cost) {
return decimalToPrecision (cost, ROUND, this.markets[symbol].precision.price, this.precisionMode, this.paddingMode)
return decimalToPrecision (cost, TRUNCATE, this.markets[symbol].precision.price, this.precisionMode, this.paddingMode)
}
priceToPrecision (symbol, price) {
@@ -1372,7 +1368,7 @@ module.exports = class Exchange {
// still takes the input as a hex string
// same as above but returns a string instead of an object
const signature = this.signMessage (message, privateKey)
return signature['r'] + this.remove0xPrefix (signature['s']) + this.binaryToBase16 (this.numberToBE (signature['v']));
return signature['r'] + this.remove0xPrefix (signature['s']) + this.binaryToBase16 (this.numberToBE (signature['v']))
}
oath () {
@@ -1396,5 +1392,74 @@ module.exports = class Exchange {
integerPow (a, b) {
return new BN (a).pow (new BN (b))
}
}
safeOrder (order) {
// Cost
// Remaining
// Average
// Price
// Amount
// Filled
//
// First we try to calculate filled from the trades
const parseFilled = order['filled'] === undefined;
const parseCost = order['cost'] === undefined;
if (parseFilled) {
order['filled'] = 0
}
if (parseCost) {
order['cost'] = 0
}
if (parseFilled || parseCost) {
if (Array.isArray (order['trades'])) {
for (let i = 0; i < order['trades'].length; i++) {
const trade = order['trades'][i]
if (parseFilled) {
order['filled'] = this.sum (order['filled'], trade['amount'])
}
if (parseCost) {
order['cost'] = this.sum (order['cost'], trade['cost'])
}
}
}
}
// We ensure amount = filled + remaining
if (order['amount'] === undefined) {
if (order['filled'] !== undefined && order['remaining'] !== undefined) {
order['amount'] = this.sum (order['filled'], order['remaining'])
}
}
if (order['filled'] === undefined) {
if (order['amount'] !== undefined && order['remaining'] !== undefined) {
order['filled'] = Math.max (this.sum (order['amount'], -order['remaining']), 0)
}
}
if (order['remaining'] === undefined) {
if (order['amount'] !== undefined && order['filled'] !== undefined) {
order['remaining'] = Math.max (this.sum (order['amount'], -order['filled']), 0)
}
}
// We ensure that the average field is calculated correctly
if (order['average'] === undefined) {
if (order['filled'] !== undefined && order['cost'] !== undefined && order['cost'] > 0) {
order['average'] = order['filled'] / order['cost']
}
}
// We also ensure the cost field is calculated correctly
const costPriceExists = (order['average'] !== undefined) || (order['price'] !== undefined)
if ((order['filled'] !== undefined) && costPriceExists) {
let costPrice = undefined;
if (order['average'] === undefined) {
costPrice = order['price']
} else {
costPrice = order['average']
}
order['cost'] = costPrice * order['filled']
}
// We add support for market orders
if (order['price'] === undefined && order['type'] === 'market') {
order['price'] = order['average']
}
return order
}
}
@@ -30,6 +30,10 @@ function subclass (BaseClass, classes, namespace = {}) {
this.__proto__ = Class.prototype
this.name = className
this.message = message
// https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work
Object.setPrototypeOf (this, Class.prototype)
}
}
@@ -1,21 +1,18 @@
"use strict";
/* ------------------------------------------------------------------------ */
// ----------------------------------------------------------------------------
const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16)
: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid)
const uuid = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16) : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace (/[018]/g, uuid)
const uuid22 = a => a ? (a ^ Math.random () * 16 >> a / 4).toString (16) : ([1e7]+1e3+4e3+8e5).replace (/[018]/g, uuid22)
module.exports =
// ----------------------------------------------------------------------------
{ uuid
module.exports = {
// hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response
uuid
, uuid22
// hasFetchOHLCV → has_fetch_ohlcv; parseHTTPResponse → parse_http_response
, unCamelCase: s => s.match (/^[A-Z0-9_]+$/) ? s : (s.replace (/[a-z0-9][A-Z]/g, x => x[0] + '_' + x[1]).replace(/[A-Z0-9][A-Z0-9][a-z]/g, x => x[0] + '_' + x[1] + x[2]).toLowerCase ())
, capitalize: s => s.length
? (s.charAt (0).toUpperCase () + s.slice (1))
: s
, capitalize: s => s.length ? (s.charAt (0).toUpperCase () + s.slice (1)) : s
, strip: s => s.replace(/^\s+|\s+$/g, '')
}
/* ------------------------------------------------------------------------ */
}
@@ -1,687 +0,0 @@
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, AuthenticationError, InsufficientFunds, InvalidOrder, OrderNotFound } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class bcex extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'bcex',
'name': 'BCEX',
'countries': [ 'CN', 'HK' ],
'version': '1',
'has': {
'cancelOrder': true,
'createOrder': true,
'fetchBalance': true,
'fetchClosedOrders': 'emulated',
'fetchMarkets': true,
'fetchMyTrades': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrders': true,
'fetchOrderBook': true,
'fetchTicker': true,
'fetchTickers': false,
'fetchTrades': true,
'fetchTradingLimits': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/77231516-851c6900-6bac-11ea-8fd6-ee5c23eddbd4.jpg',
'api': 'https://www.bcex.top',
'www': 'https://www.bcex.top',
'doc': 'https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface',
'fees': 'https://bcex.udesk.cn/hc/articles/57085',
'referral': 'https://www.bcex.top/register?invite_code=758978&lang=en',
},
'status': {
'status': 'error',
'updated': undefined,
'eta': undefined,
'url': undefined,
},
'api': {
'public': {
'get': [
'Api_Market/getPriceList', // tickers
'Api_Order/ticker', // last ohlcv candle (ticker)
'Api_Order/depth', // orderbook
'Api_Market/getCoinTrade', // ticker
'Api_Order/marketOrder', // trades...
],
'post': [
'Api_Market/getPriceList', // tickers
'Api_Order/ticker', // last ohlcv candle (ticker)
'Api_Order/depth', // orderbook
'Api_Market/getCoinTrade', // ticker
'Api_Order/marketOrder', // trades...
],
},
'private': {
'post': [
'Api_Order/cancel',
'Api_Order/coinTrust', // limit order
'Api_Order/orderList', // open / all orders (my trades?)
'Api_Order/orderInfo',
'Api_Order/tradeList', // open / all orders
'Api_Order/trustList', // ?
'Api_User/userBalance',
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.1 / 100,
'taker': 0.2 / 100,
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {
'ckusd': 0.0,
'other': 0.05 / 100,
},
'deposit': {},
},
},
'exceptions': {
'该币不存在,非法操作': ExchangeError, // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
'公钥不合法': AuthenticationError, // { code: 1, msg: '公钥不合法' } - wrong public key
'您的可用余额不足': InsufficientFunds, // { code: 1, msg: '您的可用余额不足' } - your available balance is insufficient
'您的btc不足': InsufficientFunds, // { code: 1, msg: '您的btc不足' } - your btc is insufficient
'参数非法': InvalidOrder, // {'code': 1, 'msg': '参数非法'} - 'Parameter illegal'
'订单信息不存在': OrderNotFound, // {'code': 1, 'msg': '订单信息不存在'} - 'Order information does not exist'
},
'commonCurrencies': {
'UNI': 'UNI COIN',
'PNT': 'Penta',
},
'options': {
'limits': {
// hardcoding is deprecated, using these predefined values is not recommended, use loadTradingLimits instead
'AFC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}},
'AFC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 6, 'max': 120000 }}},
'AFT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}},
'AICC/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}},
'AIDOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}},
'AISI/ETH': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 500 }}},
'AIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}},
'ANS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 500 }}},
'ANS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}},
'ARC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 60, 'max': 600000 }}},
'AXF/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 1000000 }}},
'BASH/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 3000000 }}},
'BATT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 60, 'max': 1500000 }}},
'BCD/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.3, 'max': 7000 }}},
'BHPC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 70000 }}},
'BHPC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 60000 }}},
'BOPO/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 2000000 }}},
'BOPO/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 100, 'max': 10000000 }}},
'BTC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.001, 'max': 10 }}},
'BTC/CNET': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0005, 'max': 5 }}},
'BTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.0002, 'max': 4 }}},
'BTE/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}},
'BU/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 400000 }}},
'CIC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3000, 'max': 30000000 }}},
'CIT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}},
'CIT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 40000 }}},
'CMT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 2500000 }}},
'CNET/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 12, 'max': 120000 }}},
'CNMC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 4, 'max': 50000 }}},
'CTC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 550000 }}},
'CZR/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 12, 'max': 500000 }}},
'DCON/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 8, 'max': 300000 }}},
'DCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 40000 }}},
'DCT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 2, 'max': 2000 }}},
'DOGE/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 3000, 'max': 14000000 }}},
'DOGE/CKUSD': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 500, 'max': 2000000 }}},
'DRCT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 16, 'max': 190000 }}},
'ELA/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 500 }}},
'ELF/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 100000 }}},
'ELF/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.01, 'max': 100000 }}},
'EOS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}},
'EOS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 30000 }}},
'EOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.18, 'max': 1800 }}},
'ETC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}},
'ETC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2500 }}},
'ETF/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 150000 }}},
'ETH/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.015, 'max': 100 }}},
'ETH/CKUSD': { 'precision': { 'amount': 4, 'price': 4 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}},
'ETH/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.005, 'max': 100 }}},
'FCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}},
'FCT/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.24, 'max': 1000 }}},
'GAME/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 1, 'max': 10000 }}},
'GOOC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}},
'GP/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 600, 'max': 6000000 }}},
'HSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 1000, 'max': 20000000 }}},
'IFISH/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 300, 'max': 8000000 }}},
'IIC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 4000000 }}},
'IMOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 300000 }}},
'JC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}},
'LBTC/BTC': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 0.1, 'max': 3000 }}},
'LEC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 500, 'max': 5000000 }}},
'LKY/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 70000 }}},
'LKY/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
'LMC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 25, 'max': 250000 }}},
'LSK/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.3, 'max': 3000 }}},
'LTC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}},
'LTC/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.01, 'max': 500 }}},
'LTC/USDT': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 450 }}},
'MC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 10000, 'max': 100000000 }}},
'MCC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 30, 'max': 350000 }}},
'MOC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 25, 'max': 600000 }}},
'MRYC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 300, 'max': 3000000 }}},
'MT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 6000000 }}},
'MXI/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 5000, 'max': 60000000 }}},
'NAI/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
'NAS/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.2, 'max': 15000 }}},
'NAS/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.5, 'max': 5000 }}},
'NEWOS/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 65, 'max': 700000 }}},
'NKN/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 3, 'max': 350000 }}},
'NTK/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 2, 'max': 30000 }}},
'ONT/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.2, 'max': 2000 }}},
'ONT/ETH': { 'precision': { 'amount': 3, 'price': 8 }, 'limits': { 'amount': { 'min': 0.01, 'max': 1000 }}},
'PNT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 80, 'max': 800000 }}},
'PST/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 5, 'max': 100000 }}},
'PTT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 450, 'max': 10000000 }}},
'QTUM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.4, 'max': 2800 }}},
'QTUM/CKUSD': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 0.1, 'max': 1000 }}},
'RATING/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}},
'RHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}},
'SDA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 20, 'max': 500000 }}},
'SDD/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 10, 'max': 100000 }}},
'SHC/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 250, 'max': 2500000 }}},
'SHE/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 5000000 }}},
'SMC/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 1000, 'max': 10000000 }}},
'SOP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 50, 'max': 1000000 }}},
'TAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 35, 'max': 800000 }}},
'TIP/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 7, 'max': 200000 }}},
'TKT/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 40, 'max': 400000 }}},
'TLC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 500, 'max': 10000000 }}},
'TNC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 110000 }}},
'TUB/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 200, 'max': 8000000 }}},
'UC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 3000000 }}},
'UDB/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 2000, 'max': 40000000 }}},
'UIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}},
'VAAC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 250000 }}},
'VPN/CNET': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 200, 'max': 2000000 }}},
'VSC/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 30, 'max': 650000 }}},
'WAVES/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 0.15, 'max': 1500 }}},
'WDNA/ETH': { 'precision': { 'amount': 2, 'price': 8 }, 'limits': { 'amount': { 'min': 100, 'max': 250000 }}},
'WIC/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 3, 'max': 30000 }}},
'XAS/CNET': { 'precision': { 'amount': 2, 'price': 2 }, 'limits': { 'amount': { 'min': 2.5, 'max': 25000 }}},
'XLM/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 10, 'max': 300000 }}},
'XLM/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 1, 'max': 300000 }}},
'XLM/USDT': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 5, 'max': 150000 }}},
'XRP/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 24, 'max': 100000 }}},
'XRP/CKUSD': { 'precision': { 'amount': 2, 'price': 3 }, 'limits': { 'amount': { 'min': 5, 'max': 50000 }}},
'YBCT/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 15, 'max': 200000 }}},
'YBCT/CKUSD': { 'precision': { 'amount': 2, 'price': 4 }, 'limits': { 'amount': { 'min': 10, 'max': 200000 }}},
'YBY/CNET': { 'precision': { 'amount': 2, 'price': 6 }, 'limits': { 'amount': { 'min': 25000, 'max': 250000000 }}},
'ZEC/BTC': { 'precision': { 'amount': 4, 'price': 8 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}},
'ZEC/CKUSD': { 'precision': { 'amount': 4, 'price': 2 }, 'limits': { 'amount': { 'min': 0.02, 'max': 100 }}},
},
},
});
}
async fetchTradingLimits (symbols = undefined, params = {}) {
// this method should not be called directly, use loadTradingLimits () instead
// by default it will try load withdrawal fees of all currencies (with separate requests, sequentially)
// however if you define symbols = [ 'ETH/BTC', 'LTC/BTC' ] in args it will only load those
await this.loadMarkets ();
if (symbols === undefined) {
symbols = this.symbols;
}
const result = {};
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
result[symbol] = await this.fetchTradingLimitsById (this.marketId (symbol), params);
}
return result;
}
async fetchTradingLimitsById (id, params = {}) {
const request = {
'symbol': id,
};
const response = await this.publicPostApiOrderTicker (this.extend (request, params));
//
// { code: 0,
// msg: "获取牌价信息成功",
// data: { high: 0.03721392,
// low: 0.03335362,
// buy: "0.03525757",
// sell: "0.03531160",
// last: 0.0352634,
// vol: "184742.4176",
// min_trade: "0.01500000",
// max_trade: "100.00000000",
// number_float: "4",
// price_float: "8" } } }
//
return this.parseTradingLimits (this.safeValue (response, 'data', {}));
}
parseTradingLimits (limits, symbol = undefined, params = {}) {
//
// { high: 0.03721392,
// low: 0.03335362,
// buy: "0.03525757",
// sell: "0.03531160",
// last: 0.0352634,
// vol: "184742.4176",
// min_trade: "0.01500000",
// max_trade: "100.00000000",
// number_float: "4",
// price_float: "8" }
//
return {
'info': limits,
'precision': {
'amount': this.safeInteger (limits, 'number_float'),
'price': this.safeInteger (limits, 'price_float'),
},
'limits': {
'amount': {
'min': this.safeFloat (limits, 'min_trade'),
'max': this.safeFloat (limits, 'max_trade'),
},
},
};
}
async fetchMarkets (params = {}) {
const response = await this.publicGetApiMarketGetPriceList (params);
const result = [];
const keys = Object.keys (response);
for (let i = 0; i < keys.length; i++) {
const currentMarketId = keys[i];
const currentMarkets = response[currentMarketId];
for (let j = 0; j < currentMarkets.length; j++) {
const market = currentMarkets[j];
const baseId = this.safeString (market, 'coin_from');
const quoteId = this.safeString (market, 'coin_to');
let base = baseId.toUpperCase ();
let quote = quoteId.toUpperCase ();
base = this.safeCurrencyCode (base);
quote = this.safeCurrencyCode (quote);
const id = baseId + '2' + quoteId;
const symbol = base + '/' + quote;
const active = true;
const defaults = this.safeValue (this.options['limits'], symbol, {});
result.push (this.extend ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
// overrided by defaults from this.options['limits']
'precision': {
'amount': undefined,
'price': undefined,
},
// overrided by defaults from this.options['limits']
'limits': {
'amount': { 'min': undefined, 'max': undefined },
'price': { 'min': undefined, 'max': undefined },
'cost': { 'min': undefined, 'max': undefined },
},
'info': market,
}, defaults));
}
}
return result;
}
parseTrade (trade, market = undefined) {
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const timestamp = this.safeTimestamp2 (trade, 'date', 'created');
const id = this.safeString (trade, 'tid');
const orderId = this.safeString (trade, 'order_id');
const amount = this.safeFloat2 (trade, 'number', 'amount');
const price = this.safeFloat (trade, 'price');
let cost = undefined;
if (price !== undefined) {
if (amount !== undefined) {
cost = amount * price;
}
}
let side = this.safeString (trade, 'side');
if (side === 'sale') {
side = 'sell';
}
return {
'info': trade,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': undefined,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'order': orderId,
'fee': undefined,
'takerOrMaker': undefined,
};
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'symbol': this.marketId (symbol),
};
if (limit !== undefined) {
request['limit'] = limit;
}
const market = this.market (symbol);
const response = await this.publicPostApiOrderMarketOrder (this.extend (request, params));
return this.parseTrades (response['data'], market, since, limit);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostApiUserUserBalance (params);
const data = this.safeValue (response, 'data');
let keys = Object.keys (data);
const result = { };
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const amount = this.safeFloat (data, key);
const parts = key.split ('_');
const currencyId = parts[0];
const lockOrOver = parts[1];
const code = this.safeCurrencyCode (currencyId);
if (!(code in result)) {
result[code] = this.account ();
}
if (lockOrOver === 'lock') {
result[code]['used'] = parseFloat (amount);
} else {
result[code]['free'] = parseFloat (amount);
}
}
keys = Object.keys (result);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const total = this.sum (result[key]['used'], result[key]['free']);
result[key]['total'] = total;
}
result['info'] = data;
return this.parseBalance (result);
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.markets[symbol];
const request = {
'part': market['quoteId'],
'coin': market['baseId'],
};
const response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params));
const timestamp = this.milliseconds ();
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeFloat (response, 'max'),
'low': this.safeFloat (response, 'min'),
'bid': this.safeFloat (response, 'buy'),
'bidVolume': undefined,
'ask': this.safeFloat (response, 'sale'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': this.safeFloat (response, 'price'),
'last': this.safeFloat (response, 'price'),
'previousClose': undefined,
'change': undefined,
'percentage': this.safeFloat (response, 'change_24h'),
'average': undefined,
'baseVolume': this.safeFloat (response, 'volume_24h'),
'quoteVolume': undefined,
'info': response,
};
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const marketId = this.marketId (symbol);
const request = {
'symbol': marketId,
};
const response = await this.publicPostApiOrderDepth (this.extend (request, params));
const data = this.safeValue (response, 'data');
const timestamp = this.safeTimestamp (data, 'date');
return this.parseOrderBook (data, timestamp);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const response = await this.privatePostApiOrderOrderList (this.extend (request, params));
return this.parseTrades (response['data'], market, since, limit);
}
parseOrderStatus (status) {
const statuses = {
'0': 'open',
'1': 'open', // partially filled
'2': 'closed',
'3': 'canceled',
};
return this.safeString (statuses, status, status);
}
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a `symbol` argument');
}
await this.loadMarkets ();
const request = {
'symbol': this.marketId (symbol),
'trust_id': id,
};
const response = await this.privatePostApiOrderOrderInfo (this.extend (request, params));
const order = this.safeValue (response, 'data');
const timestamp = this.safeTimestamp (order, 'created');
const status = this.parseOrderStatus (this.safeString (order, 'status'));
let side = this.safeString (order, 'flag');
if (side === 'sale') {
side = 'sell';
}
// Can't use parseOrder because the data format is different btw endpoint for fetchOrder and fetchOrders
return {
'info': order,
'id': id,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': undefined,
'side': side,
'price': this.safeFloat (order, 'price'),
'cost': undefined,
'average': this.safeFloat (order, 'avg_price'),
'amount': this.safeFloat (order, 'number'),
'filled': this.safeFloat (order, 'numberdeal'),
'remaining': this.safeFloat (order, 'numberover'),
'status': status,
'fee': undefined,
'clientOrderId': undefined,
'trades': undefined,
};
}
parseOrder (order, market = undefined) {
const id = this.safeString (order, 'id');
const timestamp = this.safeTimestamp (order, 'datetime');
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
}
const type = undefined;
let side = this.safeString (order, 'type');
if (side === 'sale') {
side = 'sell';
}
const price = this.safeFloat (order, 'price');
const average = this.safeFloat (order, 'avg_price');
const amount = this.safeFloat (order, 'amount');
const remaining = this.safeFloat (order, 'amount_outstanding');
const filled = amount - remaining;
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const cost = filled * price;
const fee = undefined;
const result = {
'info': order,
'id': id,
'clientOrderId': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
'cost': cost,
'average': average,
'amount': amount,
'filled': filled,
'remaining': remaining,
'status': status,
'fee': fee,
'trades': undefined,
};
return result;
}
async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'type': type,
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['symbol'] = market['id'];
}
const response = await this.privatePostApiOrderTradeList (this.extend (request, params));
if ('data' in response) {
return this.parseOrders (response['data'], market, since, limit);
}
return [];
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return this.fetchOrdersByType ('open', symbol, since, limit, params);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
const orders = await this.fetchOrders (symbol, since, limit, params);
return this.filterBy (orders, 'status', 'closed');
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
return this.fetchOrdersByType ('all', symbol, since, limit, params);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'symbol': this.marketId (symbol),
'type': side,
'price': this.priceToPrecision (symbol, price),
'number': this.amountToPrecision (symbol, amount),
};
const response = await this.privatePostApiOrderCoinTrust (this.extend (request, params));
const data = this.safeValue (response, 'data', {});
const id = this.safeString (data, 'order_id');
return {
'info': response,
'id': id,
};
}
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument');
}
await this.loadMarkets ();
const request = {};
if (symbol !== undefined) {
request['symbol'] = this.marketId (symbol);
}
if (id !== undefined) {
request['order_id'] = id;
}
return await this.privatePostApiOrderCancel (this.extend (request, params));
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'] + '/' + this.implodeParams (path, params);
const query = this.omit (params, this.extractParams (path));
if (api === 'public') {
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
}
} else {
this.checkRequiredCredentials ();
let payload = this.urlencode ({ 'api_key': this.apiKey });
if (Object.keys (query).length) {
payload += '&' + this.urlencode (this.keysort (query));
}
const auth = payload + '&secret_key=' + this.secret;
const signature = this.hash (this.encode (auth));
body = payload + '&sign=' + signature;
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return; // fallback to default error handler
}
const errorCode = this.safeValue (response, 'code');
if (errorCode !== undefined) {
if (errorCode !== 0) {
//
// { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
// { code: 1, msg: '公钥不合法' } - wrong public key
// { code: 1, msg: '价格输入有误,请检查你的数值精度' } - 'The price input is incorrect, please check your numerical accuracy'
// { code: 1, msg: '单笔最小交易数量不能小于0.00100000,请您重新挂单'} -
// 'The minimum number of single transactions cannot be less than 0.00100000. Please re-post the order'
//
const message = this.safeString (response, 'msg');
const feedback = this.id + ' ' + message;
this.throwExactlyMatchedException (this.exceptions, message, feedback);
if (message.indexOf ('请您重新挂单') >= 0) { // minimum limit
throw new InvalidOrder (feedback);
} else {
throw new ExchangeError (feedback);
}
}
}
}
};
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds, BadSymbol } = require ('./base/errors');
const { ExchangeError, AccountSuspended, ArgumentsRequired, AuthenticationError, DDoSProtection, ExchangeNotAvailable, InvalidOrder, OrderNotFound, PermissionDenied, InsufficientFunds, BadSymbol, RateLimitExceeded, BadRequest } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -71,6 +71,7 @@ module.exports = class bibox extends Exchange {
'get': [
'cquery',
'mdata',
'cdata',
],
},
'private': {
@@ -108,9 +109,16 @@ module.exports = class bibox extends Exchange {
'2021': InsufficientFunds, // Insufficient balance available for withdrawal
'2027': InsufficientFunds, // Insufficient balance available (for trade)
'2033': OrderNotFound, // operation failed! Orders have been completed or revoked
'2065': InvalidOrder, // Precatory price is exorbitant, please reset
'2066': InvalidOrder, // Precatory price is low, please reset
'2067': InvalidOrder, // Does not support market orders
'2068': InvalidOrder, // The number of orders can not be less than
'2078': InvalidOrder, // unvalid order price
'2085': InvalidOrder, // Order quantity is too small
'2091': RateLimitExceeded, // request is too frequency, please try again later
'2092': InvalidOrder, // Minimum amount not met
'3000': BadRequest, // Requested parameter incorrect
'3002': BadRequest, // Parameter cannot be null
'3012': AuthenticationError, // invalid apiKey
'3016': BadSymbol, // Trading pair error
'3024': PermissionDenied, // wrong apikey permissions
@@ -124,6 +132,10 @@ module.exports = class bibox extends Exchange {
'KEY': 'Bihu',
'MTC': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025
'PAI': 'PCHAIN',
'TERN': 'Ternio-ERC20',
},
'options': {
'fetchCurrencies': 'fetch_currencies_public', // or 'fetch_currencies_private' with apiKey and secret
},
});
}
@@ -423,6 +435,78 @@ module.exports = class bibox extends Exchange {
}
async fetchCurrencies (params = {}) {
const method = this.safeString (this.options, 'fetchCurrencies', 'fetch_currencies_public');
return await this[method] (params);
}
async fetchCurrenciesPublic (params = {}) {
const request = {
'cmd': 'currencies',
};
const response = await this.publicGetCdata (this.extend (request, params));
//
// publicGetCdata
//
// {
// "result":[
// {
// "symbol":"BTC",
// "name":"BTC",
// "valid_decimals":8,
// "original_decimals":8,
// "is_erc20":0,
// "enable_withdraw":1,
// "enable_deposit":1,
// "withdraw_min":0.005,
// "describe_summary":"[{\"lang\":\"zh-cn\",\"text\":\"Bitcoin 比特币的概念最初由中本聪在2009年提出,是点对点的基于 SHA-256 算法的一种P2P形式的数字货币,点对点的传输意味着一个去中心化的支付系统。\"},{\"lang\":\"en-ww\",\"text\":\"Bitcoin is a digital asset and a payment system invented by Satoshi Nakamoto who published a related paper in 2008 and released it as open-source software in 2009. The system featured as peer-to-peer; users can transact directly without an intermediary.\"}]"
// }
// ],
// "cmd":"currencies"
// }
//
const currencies = this.safeValue (response, 'result');
const result = {};
for (let i = 0; i < currencies.length; i++) {
const currency = currencies[i];
const id = this.safeString (currency, 'symbol');
const name = this.safeString (currency, 'name'); // contains hieroglyphs causing python ASCII bug
const code = this.safeCurrencyCode (id);
const precision = this.safeInteger (currency, 'valid_decimals');
const deposit = this.safeValue (currency, 'enable_deposit');
const withdraw = this.safeValue (currency, 'enable_withdraw');
const active = (deposit && withdraw);
result[code] = {
'id': id,
'code': code,
'info': currency,
'name': name,
'active': active,
'fee': undefined,
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision),
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': this.safeFloat (currency, 'withdraw_min'),
'max': undefined,
},
},
};
}
return result;
}
async fetchCurrenciesPrivate (params = {}) {
if (!this.apiKey || !this.secret) {
throw new AuthenticationError (this.id + " fetchCurrencies is an authenticated endpoint, therefore it requires 'apiKey' and 'secret' credentials. If you don't need currency details, set exchange.has['fetchCurrencies'] = false before calling its methods.");
}
@@ -435,46 +519,42 @@ module.exports = class bibox extends Exchange {
// {
// "result":[
// {
// "result":[
// {
// "totalBalance":"14.57582269",
// "balance":"14.57582269",
// "freeze":"0.00000000",
// "id":60,
// "symbol":"USDT",
// "icon_url":"/appimg/USDT_icon.png",
// "describe_url":"[{\"lang\":\"zh-cn\",\"link\":\"https://bibox.zendesk.com/hc/zh-cn/articles/115004798234\"},{\"lang\":\"en-ww\",\"link\":\"https://bibox.zendesk.com/hc/en-us/articles/115004798234\"}]",
// "name":"USDT",
// "enable_withdraw":1,
// "enable_deposit":1,
// "enable_transfer":1,
// "confirm_count":2,
// "is_erc20":1,
// "forbid_info":null,
// "describe_summary":"[{\"lang\":\"zh-cn\",\"text\":\"USDT 是 Tether 公司推出的基于稳定价值货币美元(USD)的代币 Tether USD(简称USDT),1USDT=1美元,用户可以随时使用 USDT 与 USD 进行1:1的兑换。\"},{\"lang\":\"en-ww\",\"text\":\"USDT is a cryptocurrency asset issued on the Bitcoin blockchain via the Omni Layer Protocol. Each USDT unit is backed by a U.S Dollar held in the reserves of the Tether Limited and can be redeemed through the Tether Platform.\"}]",
// "total_amount":4776930644,
// "supply_amount":4642367414,
// "price":"--",
// "contract_father":"OMNI",
// "supply_time":"--",
// "comment":null,
// "contract":"31",
// "original_decimals":8,
// "deposit_type":0,
// "hasCobo":0,
// "BTCValue":"0.00126358",
// "CNYValue":"100.93381445",
// "USDValue":"14.57524654",
// "children":[
// {"type":"OMNI","symbol":"USDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":2},
// {"type":"TRC20","symbol":"tUSDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":20},
// {"type":"ERC20","symbol":"eUSDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":25}
// ]
// },
// ],
// "cmd":"transfer/coinList"
// }
// ]
// "totalBalance":"14.57582269",
// "balance":"14.57582269",
// "freeze":"0.00000000",
// "id":60,
// "symbol":"USDT",
// "icon_url":"/appimg/USDT_icon.png",
// "describe_url":"[{\"lang\":\"zh-cn\",\"link\":\"https://bibox.zendesk.com/hc/zh-cn/articles/115004798234\"},{\"lang\":\"en-ww\",\"link\":\"https://bibox.zendesk.com/hc/en-us/articles/115004798234\"}]",
// "name":"USDT",
// "enable_withdraw":1,
// "enable_deposit":1,
// "enable_transfer":1,
// "confirm_count":2,
// "is_erc20":1,
// "forbid_info":null,
// "describe_summary":"[{\"lang\":\"zh-cn\",\"text\":\"USDT 是 Tether 公司推出的基于稳定价值货币美元(USD)的代币 Tether USD(简称USDT),1USDT=1美元,用户可以随时使用 USDT 与 USD 进行1:1的兑换。\"},{\"lang\":\"en-ww\",\"text\":\"USDT is a cryptocurrency asset issued on the Bitcoin blockchain via the Omni Layer Protocol. Each USDT unit is backed by a U.S Dollar held in the reserves of the Tether Limited and can be redeemed through the Tether Platform.\"}]",
// "total_amount":4776930644,
// "supply_amount":4642367414,
// "price":"--",
// "contract_father":"OMNI",
// "supply_time":"--",
// "comment":null,
// "contract":"31",
// "original_decimals":8,
// "deposit_type":0,
// "hasCobo":0,
// "BTCValue":"0.00126358",
// "CNYValue":"100.93381445",
// "USDValue":"14.57524654",
// "children":[
// {"type":"OMNI","symbol":"USDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":2},
// {"type":"TRC20","symbol":"tUSDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":20},
// {"type":"ERC20","symbol":"eUSDT","enable_deposit":1,"enable_withdraw":1,"confirm_count":25}
// ]
// },
// ],
// "cmd":"transfer/coinList"
// }
//
const currencies = this.safeValue (response, 'result');
@@ -799,6 +879,7 @@ module.exports = class bibox extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -851,7 +932,7 @@ module.exports = class bibox extends Exchange {
async fetchClosedOrders (symbol = undefined, since = undefined, limit = 200, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a `symbol` argument');
throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a `symbol` argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -871,7 +952,7 @@ module.exports = class bibox extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a `symbol` argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -998,7 +1079,7 @@ module.exports = class bibox extends Exchange {
}
} else if (api === 'v2private') {
this.checkRequiredCredentials ();
url = this.urls['api'] + '/v2/' + path;
url = this.implodeParams (this.urls['api'], { 'hostname': this.hostname }) + '/v2/' + path;
const json_params = this.json (params);
body = {
'body': json_params,
@@ -78,6 +78,7 @@ module.exports = class bigone extends Exchange {
'private': {
'get': [
'accounts',
'fund/accounts',
'assets/{asset_symbol}/address',
'orders',
'orders/{id}',
@@ -91,6 +92,7 @@ module.exports = class bigone extends Exchange {
'orders/{id}/cancel',
'orders/cancel',
'withdrawals',
'transfer',
],
},
},
@@ -645,7 +647,10 @@ module.exports = class bigone extends Exchange {
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetAccounts (params);
const type = this.safeString (params, 'type', '');
params = this.omit (params, 'type');
const method = 'privateGet' + this.capitalize (type) + 'Accounts';
const response = await this[method] (params);
//
// {
// "code":0,
@@ -721,6 +726,7 @@ module.exports = class bigone extends Exchange {
'symbol': symbol,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -758,7 +764,7 @@ module.exports = class bigone extends Exchange {
if (isStopLimit || isStopMarket) {
const stopPrice = this.safeFloat2 (params, 'stop_price', 'stopPrice');
if (stopPrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires a stop_price parameter');
throw new ArgumentsRequired (this.id + ' createOrder() requires a stop_price parameter');
}
request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
params = this.omit (params, [ 'stop_price', 'stopPrice' ]);
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, InvalidAddress, RateLimitExceeded, PermissionDenied, NotSupported, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, NotSupported, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable } = require ('./base/errors');
const { ROUND, TRUNCATE } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -23,6 +23,7 @@ module.exports = class binance extends Exchange {
'cancelOrder': true,
'CORS': false,
'createOrder': true,
'fetchCurrencies': true,
'fetchBalance': true,
'fetchBidsAsks': true,
'fetchClosedOrders': 'emulated',
@@ -113,6 +114,7 @@ module.exports = class binance extends Exchange {
'margin/priceIndex',
// these endpoints require this.apiKey + this.secret
'asset/assetDividend',
'asset/transfer',
'margin/loan',
'margin/repay',
'margin/account',
@@ -154,6 +156,7 @@ module.exports = class binance extends Exchange {
'sub-account/spotSummary',
'sub-account/status',
'sub-account/transfer/subUserHistory',
'sub-account/universalTransfer',
// lending endpoints
'lending/daily/product/list',
'lending/daily/userLeftQuota',
@@ -179,9 +182,23 @@ module.exports = class binance extends Exchange {
'bswap/liquidityOps',
'bswap/quote',
'bswap/swap',
// leveraged token endpoints
'blvt/tokenInfo',
'blvt/subscribe/record',
'blvt/redeem/record',
'blvt/userLimit',
// broker api
'apiReferral/ifNewUser',
'apiReferral/customization',
'apiReferral/userCustomization',
'apiReferral/rebate/recentRecord',
'apiReferral/rebate/historicalRecord',
'apiReferral/kickback/recentRecord',
'apiReferral/kickback/historicalRecord',
],
'post': [
'asset/dust',
'asset/transfer',
'account/disableFastWithdrawSwitch',
'account/enableFastWithdrawSwitch',
'capital/withdraw/apply',
@@ -199,6 +216,7 @@ module.exports = class binance extends Exchange {
'sub-account/futures/internalTransfer',
'sub-account/transfer/subToSub',
'sub-account/transfer/subToMaster',
'sub-account/universalTransfer',
'userDataStream',
'userDataStream/isolated',
'futures/transfer',
@@ -213,12 +231,21 @@ module.exports = class binance extends Exchange {
'bswap/liquidityAdd',
'bswap/liquidityRemove',
'bswap/swap',
// leveraged token endpoints
'blvt/subscribe',
'blvt/redeem',
// broker api
'apiReferral/customization',
'apiReferral/userCustomization',
'apiReferral/rebate/historicalRecord',
'apiReferral/kickback/historicalRecord',
],
'put': [
'userDataStream',
'userDataStream/isolated',
],
'delete': [
'margin/openOrders',
'margin/order',
'userDataStream',
'userDataStream/isolated',
@@ -323,6 +350,7 @@ module.exports = class binance extends Exchange {
'historicalTrades',
'aggTrades',
'klines',
'continuousKlines',
'fundingRate',
'premiumIndex',
'ticker/24hr',
@@ -357,6 +385,15 @@ module.exports = class binance extends Exchange {
'positionSide/dual',
'userTrades',
'income',
// broker endpoints
'apiReferral/ifNewUser',
'apiReferral/customization',
'apiReferral/userCustomization',
'apiReferral/traderNum',
'apiReferral/overview',
'apiReferral/tradeVol',
'apiReferral/rebateVol',
'apiReferral/traderSummary',
],
'post': [
'batchOrders',
@@ -367,6 +404,9 @@ module.exports = class binance extends Exchange {
'leverage',
'listenKey',
'countdownCancelAll',
// broker endpoints
'apiReferral/customization',
'apiReferral/userCustomization',
],
'put': [
'listenKey',
@@ -446,6 +486,7 @@ module.exports = class binance extends Exchange {
},
// exchange-specific options
'options': {
'fetchCurrencies': false, // this is a private call and it requires API keys
// 'fetchTradesMethod': 'publicGetAggTrades', // publicGetTrades, publicGetHistoricalTrades
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
'defaultType': 'spot', // 'spot', 'future', 'margin', 'delivery'
@@ -460,11 +501,20 @@ module.exports = class binance extends Exchange {
'limit': 'RESULT', // we change it from 'ACK' by default to 'RESULT'
},
'quoteOrderQty': true, // whether market orders support amounts in quote currency
'broker': {
'spot': 'x-R4BD3S82',
'margin': 'x-R4BD3S82',
'future': 'x-xcKtGhcu',
'delivery': 'x-xcKtGhcu',
},
},
// https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
'exceptions': {
'System abnormality': ExchangeError, // {"code":-1000,"msg":"System abnormality"}
'You are not authorized to execute this request.': PermissionDenied, // {"msg":"You are not authorized to execute this request."}
'API key does not exist': AuthenticationError,
'Order would trigger immediately.': OrderImmediatelyFillable,
'Stop price would trigger immediately.': OrderImmediatelyFillable, // {"code":-2010,"msg":"Stop price would trigger immediately."}
'Order would immediately match and take.': OrderImmediatelyFillable, // {"code":-2010,"msg":"Order would immediately match and take."}
'Account has insufficient balance for requested action.': InsufficientFunds,
'Rest API trading is not enabled.': ExchangeNotAvailable,
@@ -508,14 +558,21 @@ module.exports = class binance extends Exchange {
'-2013': OrderNotFound, // fetchOrder (1, 'BTC/USDT') -> 'Order does not exist'
'-2014': AuthenticationError, // { "code":-2014, "msg": "API-key format invalid." }
'-2015': AuthenticationError, // "Invalid API-key, IP, or permissions for action."
'-2019': InsufficientFunds, // {"code":-2019,"msg":"Margin is insufficient."}
'-3005': InsufficientFunds, // {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
'-3008': InsufficientFunds, // {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
'-3010': ExchangeError, // {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
'-3022': AccountSuspended, // You account's trading is banned.
'-4028': BadRequest, // {"code":-4028,"msg":"Leverage 100 is not valid"}
'-5013': InsufficientFunds, // Asset transfer failed: insufficient balance"
},
});
}
currencyToPrecision (currency, fee) {
return this.numberToString (fee);
}
nonce () {
return this.milliseconds () - this.options['timeDifference'];
}
@@ -539,6 +596,145 @@ module.exports = class binance extends Exchange {
return this.options['timeDifference'];
}
async fetchCurrencies (params = {}) {
const fetchCurrenciesEnabled = this.safeValue (this.options, 'fetchCurrencies');
if (!fetchCurrenciesEnabled) {
return undefined;
}
// this endpoint requires authentication
// while fetchCurrencies is a public API method by design
// therefore we check the keys here
// and fallback to generating the currencies from the markets
if (!this.checkRequiredCredentials (false)) {
return undefined;
}
// sandbox/testnet does not support sapi endpoints
const apiBackup = this.safeString (this.urls, 'apiBackup');
if (apiBackup !== undefined) {
return undefined;
}
const response = await this.sapiGetCapitalConfigGetall (params);
const result = {};
for (let i = 0; i < response.length; i++) {
//
// {
// coin: 'LINK',
// depositAllEnable: true,
// withdrawAllEnable: true,
// name: 'ChainLink',
// free: '0.06168',
// locked: '0',
// freeze: '0',
// withdrawing: '0',
// ipoing: '0',
// ipoable: '0',
// storage: '0',
// isLegalMoney: false,
// trading: true,
// networkList: [
// {
// network: 'BNB',
// coin: 'LINK',
// withdrawIntegerMultiple: '0',
// isDefault: false,
// depositEnable: true,
// withdrawEnable: true,
// depositDesc: '',
// withdrawDesc: '',
// specialTips: 'Both a MEMO and an Address are required to successfully deposit your LINK BEP2 tokens to Binance.',
// name: 'BEP2',
// resetAddressStatus: false,
// addressRegex: '^(bnb1)[0-9a-z]{38}$',
// memoRegex: '^[0-9A-Za-z\\-_]{1,120}$',
// withdrawFee: '0.002',
// withdrawMin: '0.01',
// withdrawMax: '9999999',
// minConfirm: 1,
// unLockConfirm: 0
// },
// {
// network: 'BSC',
// coin: 'LINK',
// withdrawIntegerMultiple: '0.00000001',
// isDefault: false,
// depositEnable: true,
// withdrawEnable: true,
// depositDesc: '',
// withdrawDesc: '',
// specialTips: '',
// name: 'BEP20 (BSC)',
// resetAddressStatus: false,
// addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
// memoRegex: '',
// withdrawFee: '0.005',
// withdrawMin: '0.01',
// withdrawMax: '9999999',
// minConfirm: 15,
// unLockConfirm: 0
// },
// {
// network: 'ETH',
// coin: 'LINK',
// withdrawIntegerMultiple: '0.00000001',
// isDefault: true,
// depositEnable: true,
// withdrawEnable: true,
// depositDesc: '',
// withdrawDesc: '',
// name: 'ERC20',
// resetAddressStatus: false,
// addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
// memoRegex: '',
// withdrawFee: '0.34',
// withdrawMin: '0.68',
// withdrawMax: '0',
// minConfirm: 12,
// unLockConfirm: 0
// }
// ]
// }
//
const entry = response[i];
const id = this.safeString (entry, 'coin');
const name = this.safeString (entry, 'name');
const code = this.safeCurrencyCode (id);
const precision = undefined;
let isWithdrawEnabled = true;
let isDepositEnabled = true;
const networkList = this.safeValue (entry, 'networkList', []);
const fees = {};
let fee = undefined;
for (let j = 0; j < networkList.length; j++) {
const networkItem = networkList[j];
const name = this.safeString (networkItem, 'name');
const withdrawFee = this.safeFloat (networkItem, 'withdrawFee');
const depositEnable = this.safeValue (networkItem, 'depositEnable');
const withdrawEnable = this.safeValue (networkItem, 'withdrawEnable');
isDepositEnabled = isDepositEnabled || depositEnable;
isWithdrawEnabled = isWithdrawEnabled || withdrawEnable;
fees[name] = withdrawFee;
const isDefault = this.safeValue (networkItem, 'isDefault');
if (isDefault || fee === undefined) {
fee = withdrawFee;
}
}
const trading = this.safeValue (entry, 'trading');
const active = (isWithdrawEnabled && isDepositEnabled && trading);
result[code] = {
'id': id,
'name': name,
'code': code,
'precision': precision,
'info': entry,
'active': active,
'fee': fee,
'fees': fees,
'limits': this.limits,
};
}
return result;
}
async fetchMarkets (params = {}) {
const defaultType = this.safeString2 (this.options, 'fetchMarkets', 'defaultType', 'spot');
const type = this.safeString (params, 'type', defaultType);
@@ -715,9 +911,8 @@ module.exports = class binance extends Exchange {
const quoteId = this.safeString (market, 'quoteAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const parts = id.split ('_');
const lastPart = this.safeString (parts, 1);
const idSymbol = (delivery) && (lastPart !== 'PERP');
const contractType = this.safeString (market, 'contractType');
const idSymbol = (future || delivery) && (contractType !== 'PERPETUAL');
const symbol = idSymbol ? id : (base + '/' + quote);
const filters = this.safeValue (market, 'filters', []);
const filtersByType = this.indexBy (filters, 'filterType');
@@ -795,7 +990,7 @@ module.exports = class binance extends Exchange {
}
if ('MIN_NOTIONAL' in filtersByType) {
const filter = this.safeValue (filtersByType, 'MIN_NOTIONAL', {});
entry['limits']['cost']['min'] = this.safeFloat (filter, 'minNotional');
entry['limits']['cost']['min'] = this.safeFloat2 (filter, 'minNotional', 'notional');
}
result.push (entry);
}
@@ -1197,15 +1392,24 @@ module.exports = class binance extends Exchange {
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
// binance docs say that the default limit 500, max 1500 for futures, max 1000 for spot markets
// the reality is that the time range wider than 500 candles won't work right
const defaultLimit = 500;
const maxLimit = 1500;
limit = (limit === undefined) ? defaultLimit : Math.min (limit, maxLimit);
const request = {
'symbol': market['id'],
'interval': this.timeframes[timeframe],
'limit': limit,
};
const duration = this.parseTimeframe (timeframe);
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default == max == 500
if (since > 0) {
const endTime = this.sum (since, limit * duration * 1000 - 1);
const now = this.milliseconds ();
request['endTime'] = Math.min (now, endTime);
}
}
let method = 'publicGetKlines';
if (market['future']) {
@@ -1293,6 +1497,23 @@ module.exports = class binance extends Exchange {
// "symbol": "BTCUSDT",
// "time": 1569514978020
// }
// {
// "symbol": "BTCUSDT",
// "id": 477128891,
// "orderId": 13809777875,
// "side": "SELL",
// "price": "38479.55",
// "qty": "0.001",
// "realizedPnl": "-0.00009534",
// "marginAsset": "USDT",
// "quoteQty": "38.47955",
// "commission": "-0.00076959",
// "commissionAsset": "USDT",
// "time": 1612733566708,
// "positionSide": "BOTH",
// "maker": true,
// "buyer": false
// }
//
const timestamp = this.safeInteger2 (trade, 'T', 'time');
const price = this.safeFloat2 (trade, 'p', 'price');
@@ -1322,6 +1543,9 @@ module.exports = class binance extends Exchange {
if ('isMaker' in trade) {
takerOrMaker = trade['isMaker'] ? 'maker' : 'taker';
}
if ('maker' in trade) {
takerOrMaker = trade['maker'] ? 'maker' : 'taker';
}
const marketId = this.safeString (trade, 'symbol');
const symbol = this.safeSymbol (marketId, market);
let cost = undefined;
@@ -1439,7 +1663,7 @@ module.exports = class binance extends Exchange {
'CANCELED': 'canceled',
'PENDING_CANCEL': 'canceling', // currently unused
'REJECTED': 'rejected',
'EXPIRED': 'canceled',
'EXPIRED': 'expired',
};
return this.safeString (statuses, status, status);
}
@@ -1565,6 +1789,8 @@ module.exports = class binance extends Exchange {
}
const clientOrderId = this.safeString (order, 'clientOrderId');
const timeInForce = this.safeString (order, 'timeInForce');
const postOnly = (type === 'limit_maker') || (timeInForce === 'GTX');
const stopPrice = this.safeFloat (order, 'stopPrice');
return {
'info': order,
'id': id,
@@ -1575,8 +1801,10 @@ module.exports = class binance extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
'amount': amount,
'cost': cost,
'average': average,
@@ -1621,7 +1849,15 @@ module.exports = class binance extends Exchange {
'type': uppercaseType,
'side': side.toUpperCase (),
};
if (clientOrderId !== undefined) {
if (clientOrderId === undefined) {
const broker = this.safeValue (this.options, 'broker');
if (broker) {
const brokerId = this.safeString (broker, orderType);
if (brokerId !== undefined) {
request['newClientOrderId'] = brokerId + this.uuid22 ();
}
}
} else {
request['newClientOrderId'] = clientOrderId;
}
if (market['spot']) {
@@ -1700,7 +1936,7 @@ module.exports = class binance extends Exchange {
quantityIsRequired = true;
const callbackRate = this.safeFloat (params, 'callbackRate');
if (callbackRate === undefined) {
throw new InvalidOrder (this.id + ' createOrder method requires a callbackRate extra param for a ' + type + ' order');
throw new InvalidOrder (this.id + ' createOrder() requires a callbackRate extra param for a ' + type + ' order');
}
}
if (quantityIsRequired) {
@@ -1708,7 +1944,7 @@ module.exports = class binance extends Exchange {
}
if (priceIsRequired) {
if (price === undefined) {
throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order');
throw new InvalidOrder (this.id + ' createOrder() requires a price argument for a ' + type + ' order');
}
request['price'] = this.priceToPrecision (symbol, price);
}
@@ -1718,7 +1954,7 @@ module.exports = class binance extends Exchange {
if (stopPriceIsRequired) {
const stopPrice = this.safeFloat (params, 'stopPrice');
if (stopPrice === undefined) {
throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order');
throw new InvalidOrder (this.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order');
} else {
params = this.omit (params, 'stopPrice');
request['stopPrice'] = this.priceToPrecision (symbol, stopPrice);
@@ -1730,7 +1966,7 @@ module.exports = class binance extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1751,7 +1987,7 @@ module.exports = class binance extends Exchange {
if (clientOrderId !== undefined) {
request['origClientOrderId'] = clientOrderId;
} else {
request['orderId'] = parseInt (id);
request['orderId'] = id;
}
const query = this.omit (params, [ 'type', 'clientOrderId', 'origClientOrderId' ]);
const response = await this[method] (this.extend (request, query));
@@ -1760,7 +1996,7 @@ module.exports = class binance extends Exchange {
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1873,7 +2109,7 @@ module.exports = class binance extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1883,11 +2119,11 @@ module.exports = class binance extends Exchange {
const origClientOrderId = this.safeValue2 (params, 'origClientOrderId', 'clientOrderId');
const request = {
'symbol': market['id'],
// 'orderId': parseInt (id),
// 'orderId': id,
// 'origClientOrderId': id,
};
if (origClientOrderId === undefined) {
request['orderId'] = parseInt (id);
request['orderId'] = id;
} else {
request['origClientOrderId'] = origClientOrderId;
}
@@ -1906,7 +2142,7 @@ module.exports = class binance extends Exchange {
async cancelAllOrders (symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1917,7 +2153,9 @@ module.exports = class binance extends Exchange {
const type = this.safeString (params, 'type', defaultType);
const query = this.omit (params, 'type');
let method = 'privateDeleteOpenOrders';
if (type === 'future') {
if (type === 'margin') {
method = 'sapiDeleteMarginOpenOrders';
} else if (type === 'future') {
method = 'fapiPrivateDeleteAllOpenOrders';
} else if (type === 'delivery') {
method = 'dapiPrivateDeleteAllOpenOrders';
@@ -1983,20 +2221,23 @@ module.exports = class binance extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const type = this.safeValue (params, 'type', market['type']);
const defaultType = this.safeString2 (this.options, 'fetchMyTrades', 'defaultType', market['type']);
const type = this.safeString (params, 'type', defaultType);
params = this.omit (params, 'type');
let method = undefined;
if (type === 'spot') {
method = 'privateGetMyTrades';
} else if (type === 'margin') {
method = 'sapiGetMarginMyTrades';
} else if (type === 'future') {
method = 'fapiPrivateGetUserTrades';
} else if (type === 'delivery') {
method = 'dapiPrivateGetUserTrades';
}
params = this.omit (params, 'type');
const request = {
'symbol': market['id'],
};
@@ -2280,7 +2521,10 @@ module.exports = class binance extends Exchange {
tag = undefined;
}
}
const txid = this.safeString (transaction, 'txId');
let txid = this.safeString (transaction, 'txId');
if ((txid !== undefined) && (txid.indexOf ('Internal transfer ') >= 0)) {
txid = txid.slice (18);
}
const currencyId = this.safeString (transaction, 'asset');
const code = this.safeCurrencyCode (currencyId, currency);
let timestamp = undefined;
@@ -2329,15 +2573,27 @@ module.exports = class binance extends Exchange {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'asset': currency['id'],
'coin': currency['id'],
// 'network': 'ETH', // 'BSC', 'XMR', you can get network and isDefault in networkList in the response of sapiGetCapitalConfigDetail
};
const response = await this.wapiGetDepositAddress (this.extend (request, params));
const success = this.safeValue (response, 'success');
if ((success === undefined) || !success) {
throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response – create the deposit address in the user settings first.');
}
// has support for the 'network' parameter
// https://binance-docs.github.io/apidocs/spot/en/#deposit-address-supporting-network-user_data
const response = await this.sapiGetCapitalDepositAddress (this.extend (request, params));
//
// {
// currency: 'XRP',
// address: 'rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh',
// tag: '108618262',
// info: {
// coin: 'XRP',
// address: 'rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh',
// tag: '108618262',
// url: 'https://bithomp.com/explorer/rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh'
// }
// }
//
const address = this.safeString (response, 'address');
const tag = this.safeString (response, 'addressTag');
const tag = this.safeString (response, 'tag');
this.checkAddress (address);
return {
'currency': code,
@@ -2515,7 +2771,7 @@ module.exports = class binance extends Exchange {
'timestamp': this.nonce (),
'recvWindow': recvWindow,
}, params));
} else if (path === 'batchOrders') {
} else if ((path === 'batchOrders') || (path.indexOf ('sub-account') >= 0)) {
query = this.rawencode (this.extend ({
'timestamp': this.nonce (),
'recvWindow': recvWindow,
@@ -42,5 +42,9 @@ module.exports = class binanceus extends binance {
},
});
}
async fetchCurrencies (params = {}) {
return undefined;
}
};
@@ -306,6 +306,7 @@ module.exports = class bit2c extends Exchange {
'symbol': symbol,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -82,6 +82,8 @@ module.exports = class bitbank extends Exchange {
'BCH/JPY': { 'id': 'bcc_jpy', 'symbol': 'BCH/JPY', 'base': 'BCH', 'quote': 'JPY', 'baseId': 'bcc', 'quoteId': 'jpy' },
'MONA/BTC': { 'id': 'mona_btc', 'symbol': 'MONA/BTC', 'base': 'MONA', 'quote': 'BTC', 'baseId': 'mona', 'quoteId': 'btc' },
'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' },
'QTUM/BTC': { 'id': 'qtum_btc', 'symbol': 'QTUM/BTC', 'base': 'QTUM', 'quote': 'BTC', 'baseId': 'qtum', 'quoteId': 'btc' },
'QTUM/JPY': { 'id': 'qtum_jpy', 'symbol': 'QTUM/JPY', 'base': 'QTUM', 'quote': 'JPY', 'baseId': 'qtum', 'quoteId': 'jpy' },
'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' },
'LTC/BTC': { 'id': 'ltc_btc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC', 'baseId': 'ltc', 'quoteId': 'btc' },
'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' },
@@ -90,6 +92,7 @@ module.exports = class bitbank extends Exchange {
'ETH/JPY': { 'id': 'eth_jpy', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy' },
'LTC/JPY': { 'id': 'ltc_jpy', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy' },
'XRP/BTC': { 'id': 'xrp_btc', 'symbol': 'XRP/BTC', 'base': 'XRP', 'quote': 'BTC', 'baseId': 'xrp', 'quoteId': 'btc' },
'XLM/BTC': { 'id': 'xlm_btc', 'symbol': 'XLM/BTC', 'base': 'XLM', 'quote': 'BTC', 'baseId': 'xlm', 'quoteId': 'btc' },
},
'fees': {
'trading': {
@@ -207,8 +210,8 @@ module.exports = class bitbank extends Exchange {
const feeCost = this.safeFloat (trade, 'fee_amount_quote');
if (feeCost !== undefined) {
fee = {
'currency': market['quote'],
'cost': feeCurrency,
'currency': feeCurrency,
'cost': feeCost,
};
}
const orderId = this.safeString (trade, 'order_id');
@@ -367,6 +370,7 @@ module.exports = class bitbank extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -385,7 +389,7 @@ module.exports = class bitbank extends Exchange {
await this.loadMarkets ();
const market = this.market (symbol);
if (price === undefined) {
throw new InvalidOrder (this.id + ' createOrder requires a price argument for both market and limit orders');
throw new InvalidOrder (this.id + ' createOrder() requires a price argument for both market and limit orders');
}
const request = {
'pair': market['id'],
@@ -351,6 +351,7 @@ module.exports = class bitbay extends Exchange {
filled = Math.max (0, amount - remaining);
}
}
const postOnly = this.safeValue (order, 'postOnly');
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
@@ -362,6 +363,7 @@ module.exports = class bitbay extends Exchange {
'symbol': symbol,
'type': this.safeString (order, 'mode'),
'timeInForce': undefined,
'postOnly': postOnly,
'side': this.safeStringLower (order, 'offerType'),
'price': this.safeFloat (order, 'rate'),
'stopPrice': undefined,
@@ -16,7 +16,6 @@ module.exports = class bitfinex extends Exchange {
'countries': [ 'VG' ],
'version': 'v1',
'rateLimit': 1500,
'certified': true,
'pro': true,
// new metainfo interface
'has': {
@@ -54,6 +53,7 @@ module.exports = class bitfinex extends Exchange {
'30m': '30m',
'1h': '1h',
'3h': '3h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1D',
@@ -600,13 +600,32 @@ module.exports = class bitfinex extends Exchange {
async fetchBalance (params = {}) {
await this.loadMarkets ();
const types = {
'exchange': 'exchange',
'deposit': 'funding',
'trading': 'margin',
};
const balanceType = this.safeString (params, 'type', 'exchange');
const query = this.omit (params, 'type');
const response = await this.privatePostBalances (query);
// [ { type: 'deposit',
// currency: 'btc',
// amount: '0.00116721',
// available: '0.00116721' },
// { type: 'exchange',
// currency: 'ust',
// amount: '0.0000002',
// available: '0.0000002' },
// { type: 'trading',
// currency: 'btc',
// amount: '0.0005',
// available: '0.0005' } ],
const result = { 'info': response };
for (let i = 0; i < response.length; i++) {
const balance = response[i];
if (balance['type'] === balanceType) {
const type = this.safeString (balance, 'type');
const parsedType = this.safeString (types, type);
if ((parsedType === balanceType) || (type === balanceType)) {
const currencyId = this.safeString (balance, 'currency');
const code = this.safeCurrencyCode (currencyId);
// bitfinex had BCH previously, now it's BAB, but the old
@@ -769,7 +788,7 @@ module.exports = class bitfinex extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a `symbol` argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -887,6 +906,7 @@ module.exports = class bitfinex extends Exchange {
'symbol': symbol,
'type': orderType,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': this.safeFloat (order, 'price'),
'stopPrice': undefined,
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const bitfinex = require ('./bitfinex.js');
const { ExchangeError, InvalidAddress, ArgumentsRequired, InsufficientFunds, AuthenticationError, OrderNotFound, InvalidOrder, BadRequest, InvalidNonce, BadSymbol, OnMaintenance } = require ('./base/errors');
const { ExchangeError, InvalidAddress, ArgumentsRequired, InsufficientFunds, AuthenticationError, OrderNotFound, InvalidOrder, BadRequest, InvalidNonce, BadSymbol, OnMaintenance, NotSupported } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -53,6 +53,7 @@ module.exports = class bitfinex2 extends bitfinex {
'30m': '30m',
'1h': '1h',
'3h': '3h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1D',
@@ -171,7 +172,9 @@ module.exports = class bitfinex2 extends bitfinex {
'auth/w/position/claim',
'auth/r/positions/hist',
'auth/r/positions/audit',
'auth/r/positions/snap',
'auth/w/deriv/collateral/set',
'auth/w/deriv/collateral/limits',
'auth/r/funding/offers',
'auth/r/funding/offers/{symbol}',
'auth/w/funding/offer/submit',
@@ -583,6 +586,10 @@ module.exports = class bitfinex2 extends bitfinex {
return this.parseBalance (result);
}
async fetchOrder (id, symbol = undefined, params = {}) {
throw new NotSupported (this.id + ' fetchOrder is not implemented yet');
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const precision = this.safeValue (this.options, 'precision', 'R0');
@@ -923,6 +930,7 @@ module.exports = class bitfinex2 extends bitfinex {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -941,16 +949,43 @@ module.exports = class bitfinex2 extends bitfinex {
await this.loadMarkets ();
const market = this.market (symbol);
const orderTypes = this.safeValue (this.options, 'orderTypes', {});
const orderType = this.safeString (orderTypes, type, type);
const orderType = this.safeStringUpper (orderTypes, type, type);
amount = (side === 'sell') ? -amount : amount;
const request = {
'symbol': market['id'],
'type': orderType,
'amount': this.numberToString (amount),
};
if (type !== 'market') {
if ((orderType === 'LIMIT') || (orderType === 'EXCHANGE LIMIT')) {
request['price'] = this.numberToString (price);
} else if ((orderType === 'STOP') || (orderType === 'EXCHANGE STOP')) {
const stopPrice = this.safeFloat (params, 'stopPrice', price);
request['price'] = this.numberToString (stopPrice);
} else if ((orderType === 'STOP LIMIT') || (orderType === 'EXCHANGE STOP LIMIT')) {
const priceAuxLimit = this.safeFloat (params, 'price_aux_limit');
let stopPrice = this.safeFloat (params, 'stopPrice');
if (priceAuxLimit === undefined) {
if (stopPrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires a stopPrice parameter or a price_aux_limit parameter for a ' + orderType + ' order');
} else {
request['price_aux_limit'] = this.numberToString (price);
}
} else {
request['price_aux_limit'] = this.numberToString (priceAuxLimit);
if (stopPrice === undefined) {
stopPrice = price;
}
}
request['price'] = this.numberToString (stopPrice);
} else if ((orderType === 'TRAILING STOP') || (orderType === 'EXCHANGE TRAILING STOP')) {
const priceTrailing = this.safeFloat (params, 'price_trailing');
request['price_trailing'] = this.numberToString (priceTrailing);
const stopPrice = this.safeFloat (params, 'stopPrice', price);
request['price'] = this.numberToString (stopPrice);
} else if ((orderType === 'FOK') || (orderType === 'EXCHANGE FOK') || (orderType === 'IOC') || (orderType === 'EXCHANGE IOC')) {
request['price'] = this.numberToString (price);
}
params = this.omit (params, [ 'stopPrice', 'price_aux_limit', 'price_trailing' ]);
const clientOrderId = this.safeValue2 (params, 'cid', 'clientOrderId');
if (clientOrderId !== undefined) {
request['cid'] = clientOrderId;
@@ -365,6 +365,7 @@ module.exports = class bitflyer extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -424,7 +425,7 @@ module.exports = class bitflyer extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a `symbol` argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a `symbol` argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -440,7 +441,7 @@ module.exports = class bitflyer extends Exchange {
async fetchPositions (symbols = undefined, since = undefined, limit = undefined, params = {}) {
if (symbols === undefined) {
throw new ArgumentsRequired (this.id + ' fetchPositions requires a `symbols` argument, exactly one symbol in an array');
throw new ArgumentsRequired (this.id + ' fetchPositions() requires a `symbols` argument, exactly one symbol in an array');
}
await this.loadMarkets ();
const request = {
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection, PermissionDenied } = require ('./base/errors');
const { ExchangeError, AuthenticationError, OrderNotFound, InsufficientFunds, DDoSProtection, PermissionDenied, BadSymbol } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -223,6 +223,7 @@ module.exports = class bitforex extends Exchange {
},
'commonCurrencies': {
'CREDIT': 'TerraCredit',
'HBC': 'Hybrid Bank Cash',
'IQ': 'IQ.Cash',
'UOS': 'UOS Network',
},
@@ -231,6 +232,7 @@ module.exports = class bitforex extends Exchange {
'1013': AuthenticationError,
'1016': AuthenticationError,
'1017': PermissionDenied, // {"code":"1017","success":false,"time":1602670594367,"message":"IP not allow"}
'1019': BadSymbol, // {"code":"1019","success":false,"time":1607087743778,"message":"Symbol Invalid"}
'3002': InsufficientFunds,
'10204': DDoSProtection,
},
@@ -499,6 +501,7 @@ module.exports = class bitforex extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -1704,7 +1704,7 @@ module.exports = class bitget extends Exchange {
const type = this.safeString (params, 'type', defaultType);
params = this.omit (params, 'type');
if (type === undefined) {
throw new ArgumentsRequired (this.id + " requires an 'accountId' parameter");
throw new ArgumentsRequired (this.id + " getAccountId() requires an 'accountId' parameter");
}
const account = await this.findAccountByType (type);
return account['id'];
@@ -1716,7 +1716,7 @@ module.exports = class bitget extends Exchange {
const defaultType = this.safeString2 (this.options, 'fetchBalance', 'defaultType');
const type = this.safeString (params, 'type', defaultType);
if (type === undefined) {
throw new ArgumentsRequired (this.id + " fetchBalance requires a 'type' parameter, one of 'spot', 'swap'");
throw new ArgumentsRequired (this.id + " fetchBalance() requires a 'type' parameter, one of 'spot', 'swap'");
}
let method = undefined;
const query = this.omit (params, 'type');
@@ -1960,6 +1960,7 @@ module.exports = class bitget extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -2042,7 +2043,7 @@ module.exports = class bitget extends Exchange {
request['client_oid'] = clientOrderId;
const orderType = this.safeString (params, 'type');
if (orderType === undefined) {
throw new ArgumentsRequired (this.id + " createOrder requires a type parameter, '1' = open long, '2' = open short, '3' = close long, '4' = close short for " + market['type'] + ' orders');
throw new ArgumentsRequired (this.id + " createOrder() requires a type parameter, '1' = open long, '2' = open short, '3' = close long, '4' = close short for " + market['type'] + ' orders');
}
request['size'] = this.amountToPrecision (symbol, amount);
request['type'] = orderType;
@@ -2084,7 +2085,7 @@ module.exports = class bitget extends Exchange {
const type = this.safeString (params, 'type', defaultType);
if (type === 'spot') {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument for spot orders');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument for spot orders');
}
}
} else {
@@ -2125,14 +2126,15 @@ module.exports = class bitget extends Exchange {
async cancelOrders (ids, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const type = this.safeString (params, 'type', market['type']);
if (type === undefined) {
throw new ArgumentsRequired (this.id + " cancelOrders requires a type parameter (one of 'spot', 'swap').");
throw new ArgumentsRequired (this.id + " cancelOrders() requires a type parameter (one of 'spot', 'swap').");
}
params = this.omit (params, 'type');
const request = {};
let method = undefined;
if (type === 'spot') {
@@ -2189,13 +2191,13 @@ module.exports = class bitget extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const type = this.safeString (params, 'type', market['type']);
if (type === undefined) {
throw new ArgumentsRequired (this.id + " fetchOrder requires a type parameter (one of 'spot', 'swap').");
throw new ArgumentsRequired (this.id + " fetchOrder() requires a type parameter (one of 'spot', 'swap').");
}
let method = undefined;
const request = {};
@@ -2266,7 +2268,7 @@ module.exports = class bitget extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -2351,7 +2353,7 @@ module.exports = class bitget extends Exchange {
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -2443,7 +2445,7 @@ module.exports = class bitget extends Exchange {
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument');
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
@@ -2480,7 +2482,7 @@ module.exports = class bitget extends Exchange {
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument');
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
@@ -2603,14 +2605,14 @@ module.exports = class bitget extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const type = this.safeString (params, 'type', market['type']);
const query = this.omit (params, 'type');
if (type === 'swap') {
throw new ArgumentsRequired (this.id + ' fetchMyTrades is not supported for ' + type + ' type');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() is not supported for ' + type + ' type');
}
//
// spot
@@ -2667,12 +2669,12 @@ module.exports = class bitget extends Exchange {
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrderTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const type = this.safeString (params, 'type', market['type']);
const query = this.omit (params, 'type');
params = this.omit (params, 'type');
let method = undefined;
const request = {};
if (type === 'spot') {
@@ -2684,7 +2686,7 @@ module.exports = class bitget extends Exchange {
request['symbol'] = market['id'];
method = 'swapGetOrderFills';
}
const response = await this[method] (this.extend (request, query));
const response = await this[method] (this.extend (request, params));
//
// spot
//
@@ -22,6 +22,7 @@ module.exports = class bithumb extends Exchange {
'createOrder': true,
'fetchBalance': true,
'fetchMarkets': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
@@ -45,10 +46,13 @@ module.exports = class bithumb extends Exchange {
'get': [
'ticker/{currency}',
'ticker/all',
'ticker/ALL_BTC',
'ticker/ALL_KRW',
'orderbook/{currency}',
'orderbook/all',
'transaction_history/{currency}',
'transaction_history/all',
'candlestick/{currency}/{interval}',
],
},
'private': {
@@ -92,6 +96,27 @@ module.exports = class bithumb extends Exchange {
'Unknown Error': ExchangeError,
'After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions': ExchangeError, // {"status":"5100","message":"After May 23th, recent_transactions is no longer, hence users will not be able to connect to recent_transactions"}
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'10m': '10m',
'30m': '30m',
'1h': '1h',
'6h': '6h',
'12h': '12h',
'1d': '24h',
},
'options': {
'quoteCurrencies': {
'BTC': {
'precision': {
'price': 8,
},
},
'KRW': {},
},
},
});
}
@@ -100,54 +125,61 @@ module.exports = class bithumb extends Exchange {
}
async fetchMarkets (params = {}) {
const response = await this.publicGetTickerAll (params);
const data = this.safeValue (response, 'data');
const currencyIds = Object.keys (data);
const result = [];
const quote = this.safeCurrencyCode ('KRW');
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
if (currencyId === 'date') {
continue;
}
const market = data[currencyId];
const base = this.safeCurrencyCode (currencyId);
const symbol = currencyId + '/' + quote;
let active = true;
if (Array.isArray (market)) {
const numElements = market.length;
if (numElements === 0) {
active = false;
const quoteCurrencies = this.safeValue (this.options, 'quoteCurrencies', {});
const quotes = Object.keys (quoteCurrencies);
for (let i = 0; i < quotes.length; i++) {
const quote = quotes[i];
const extension = this.safeValue (quoteCurrencies, quote, {});
const method = 'publicGetTickerALL' + quote;
const response = await this[method] (params);
const data = this.safeValue (response, 'data');
const currencyIds = Object.keys (data);
for (let j = 0; j < currencyIds.length; j++) {
const currencyId = currencyIds[j];
if (currencyId === 'date') {
continue;
}
const market = data[currencyId];
const base = this.safeCurrencyCode (currencyId);
const symbol = currencyId + '/' + quote;
let active = true;
if (Array.isArray (market)) {
const numElements = market.length;
if (numElements === 0) {
active = false;
}
}
const entry = this.deepExtend ({
'id': currencyId,
'symbol': symbol,
'base': base,
'quote': quote,
'info': market,
'active': active,
'precision': {
'amount': 4,
'price': 4,
},
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': 500,
'max': 5000000000,
},
},
'baseId': undefined,
'quoteId': undefined,
}, extension);
result.push (entry);
}
result.push ({
'id': currencyId,
'symbol': symbol,
'base': base,
'quote': quote,
'info': market,
'active': active,
'precision': {
'amount': 4,
'price': 4,
},
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': 500,
'max': 5000000000,
},
},
'baseId': undefined,
'quoteId': undefined,
});
}
return result;
}
@@ -349,6 +381,62 @@ module.exports = class bithumb extends Exchange {
return this.parseTicker (data, market);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1576823400000, // 기준 시간
// '8284000', // 시가
// '8286000', // 종가
// '8289000', // 고가
// '8276000', // 저가
// '15.41503692' // 거래량
// ]
//
return [
this.safeInteger (ohlcv, 0),
this.safeFloat (ohlcv, 1),
this.safeFloat (ohlcv, 3),
this.safeFloat (ohlcv, 4),
this.safeFloat (ohlcv, 2),
this.safeFloat (ohlcv, 5),
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'currency': market['base'],
'interval': this.timeframes[timeframe],
};
const response = await this.publicGetCandlestickCurrencyInterval (this.extend (request, params));
//
// {
// 'status': '0000',
// 'data': {
// [
// 1576823400000, // 기준 시간
// '8284000', // 시가
// '8286000', // 종가
// '8289000', // 고가
// '8276000', // 저가
// '15.41503692' // 거래량
// ],
// [
// 1576824000000, // 기준 시간
// '8284000', // 시가
// '8281000', // 종가
// '8289000', // 고가
// '8275000', // 저가
// '6.19584467' // 거래량
// ],
// }
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseOHLCVs (data, market, timeframe, since, limit);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
@@ -470,7 +558,7 @@ module.exports = class bithumb extends Exchange {
const market = this.market (symbol);
const request = {
'order_currency': market['id'],
'Payment_currency': market['quote'],
'payment_currency': market['quote'],
'units': amount,
};
let method = 'privatePostTradePlace';
@@ -483,7 +571,7 @@ module.exports = class bithumb extends Exchange {
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');
throw new InvalidOrder (this.id + ' createOrder() did not return an order id');
}
return {
'info': response,
@@ -496,7 +584,7 @@ module.exports = class bithumb extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -708,6 +796,7 @@ module.exports = class bithumb extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -730,7 +819,7 @@ module.exports = class bithumb extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -770,10 +859,10 @@ module.exports = class bithumb extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
const side_in_params = ('side' in params);
if (!side_in_params) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a `symbol` argument and a `side` parameter (sell or buy)');
}
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a `symbol` argument and a `side` parameter (sell or buy)');
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';
@@ -804,7 +893,7 @@ module.exports = class bithumb extends Exchange {
'address': address,
'currency': currency['id'],
};
if (currency === 'XRP' || currency === 'XMR') {
if (currency === 'XRP' || currency === 'XMR' || currency === 'EOS' || currency === 'STEEM') {
const destination = this.safeString (params, 'destination');
if ((tag === undefined) && (destination === undefined)) {
throw new ArgumentsRequired (this.id + ' ' + code + ' withdraw() requires a tag argument or an extra destination param');
@@ -21,6 +21,9 @@ module.exports = class bitkk extends zb {
'doc': 'https://www.bitkk.com/i/developer',
'fees': 'https://www.bitkk.com/i/rate',
},
'commonCurrencies': {
'BCH': 'BCHOLD',
},
});
}
};
@@ -48,7 +48,7 @@ module.exports = class bitmart extends Exchange {
'logo': 'https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg',
'api': 'https://api-cloud.{hostname}', // bitmart.info for Hong Kong users
'www': 'https://www.bitmart.com/',
'doc': 'https://github.com/bitmartexchange/bitmart-official-api-docs',
'doc': 'https://developer-pro.bitmart.com/',
'referral': 'http://www.bitmart.com/?r=rQCFLh',
'fees': 'https://www.bitmart.com/fee/en',
},
@@ -161,8 +161,8 @@ module.exports = class bitmart extends Exchange {
'trading': {
'tierBased': true,
'percentage': true,
'taker': 0.002,
'maker': 0.001,
'taker': 0.0025,
'maker': 0.0025,
'tiers': {
'taker': [
[0, 0.20 / 100],
@@ -296,6 +296,8 @@ module.exports = class bitmart extends Exchange {
'broad': {},
},
'commonCurrencies': {
'COT': 'Community Coin',
'CPC': 'CPCoin',
'ONE': 'Menlo One',
'PLA': 'Plair',
},
@@ -1241,7 +1243,7 @@ module.exports = class bitmart extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1249,7 +1251,7 @@ module.exports = class bitmart extends Exchange {
const request = {};
if (market['spot']) {
request['symbol'] = market['id'];
request['offset'] = 1;
request['offset'] = 1; // max offset * limit < 500
if (limit === undefined) {
limit = 100; // max 100
}
@@ -1322,7 +1324,7 @@ module.exports = class bitmart extends Exchange {
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrderTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrderTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1600,6 +1602,7 @@ module.exports = class bitmart extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -1705,7 +1708,7 @@ module.exports = class bitmart extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1733,6 +1736,15 @@ module.exports = class bitmart extends Exchange {
// }
// }
//
// spot alternative
//
// {
// "code": 1000,
// "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
// "message": "OK",
// "data": true
// }
//
// contract
//
// {
@@ -1748,16 +1760,19 @@ module.exports = class bitmart extends Exchange {
// }
//
const data = this.safeValue (response, 'data');
if (data === true) {
return this.parseOrder (id, market);
}
const succeeded = this.safeValue (data, 'succeed');
if (succeeded !== undefined) {
id = this.safeString (succeeded, 0);
if (id === undefined) {
throw new InvalidOrder (this.id + ' cancelOrder failed to cancel ' + symbol + ' order id ' + id);
throw new InvalidOrder (this.id + ' cancelOrder() failed to cancel ' + symbol + ' order id ' + id);
}
} else {
const result = this.safeValue (data, 'result');
if (!result) {
throw new InvalidOrder (this.id + ' cancelOrder ' + symbol + ' order id ' + id + ' is filled or canceled');
throw new InvalidOrder (this.id + ' cancelOrder() ' + symbol + ' order id ' + id + ' is filled or canceled');
}
}
const order = this.parseOrder (id, market);
@@ -1766,16 +1781,16 @@ module.exports = class bitmart extends Exchange {
async cancelAllOrders (symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a symbol argument');
}
const side = this.safeString (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + " cancelAllOrders requires a `side` parameter ('buy' or 'sell')");
throw new ArgumentsRequired (this.id + " cancelAllOrders() requires a `side` parameter ('buy' or 'sell')");
}
await this.loadMarkets ();
const market = this.market (symbol);
if (!market['spot']) {
throw new NotSupported (this.id + ' cancelAllOrders does not support ' + market['type'] + ' orders, only spot orders are accepted');
throw new NotSupported (this.id + ' cancelAllOrders() does not support ' + market['type'] + ' orders, only spot orders are accepted');
}
const request = {
'symbol': market['id'],
@@ -1795,12 +1810,12 @@ module.exports = class bitmart extends Exchange {
async cancelOrders (ids, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' canelOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' canelOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
if (!market['spot']) {
throw new NotSupported (this.id + ' cancelOrders does not support ' + market['type'] + ' orders, only contract orders are accepted');
throw new NotSupported (this.id + ' cancelOrders() does not support ' + market['type'] + ' orders, only contract orders are accepted');
}
const orders = [];
for (let i = 0; i < ids.length; i++) {
@@ -1841,7 +1856,7 @@ module.exports = class bitmart extends Exchange {
async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1963,7 +1978,7 @@ module.exports = class bitmart extends Exchange {
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1975,7 +1990,7 @@ module.exports = class bitmart extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const request = {};
@@ -2053,7 +2068,7 @@ module.exports = class bitmart extends Exchange {
const orders = this.safeValue (data, 'orders', []);
const firstOrder = this.safeValue (orders, 0);
if (firstOrder === undefined) {
throw new OrderNotFound (this.id + ' fetchOrder could not find ' + symbol + ' order id ' + id);
throw new OrderNotFound (this.id + ' fetchOrder() could not find ' + symbol + ' order id ' + id);
}
return this.parseOrder (firstOrder, market);
} else {
@@ -133,8 +133,8 @@ module.exports = class bitmax extends Exchange {
'trading': {
'tierBased': true,
'percentage': true,
'taker': 0.001,
'maker': 0.001,
'taker': 0.002,
'maker': 0.002,
},
},
'precisionMode': TICK_SIZE,
@@ -1039,6 +1039,7 @@ module.exports = class bitmax extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -1089,7 +1090,7 @@ module.exports = class bitmax extends Exchange {
if ((type === 'stop_limit') || (type === 'stop_market')) {
const stopPrice = this.safeFloat (params, 'stopPrice');
if (stopPrice === undefined) {
throw new InvalidOrder (this.id + ' createOrder requires a stopPrice parameter for ' + type + ' orders');
throw new InvalidOrder (this.id + ' createOrder() requires a stopPrice parameter for ' + type + ' orders');
} else {
request['stopPrice'] = this.priceToPrecision (symbol, stopPrice);
params = this.omit (params, 'stopPrice');
@@ -1344,7 +1345,7 @@ module.exports = class bitmax extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
await this.loadAccounts ();
@@ -4,7 +4,7 @@
const Exchange = require ('./base/Exchange');
const { TICK_SIZE } = require ('./base/functions/number');
const { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied } = require ('./base/errors');
const { AuthenticationError, BadRequest, DDoSProtection, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidOrder, OrderNotFound, PermissionDenied, ArgumentsRequired } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -167,6 +167,7 @@ module.exports = class bitmex extends Exchange {
'Account has insufficient Available Balance': InsufficientFunds,
'Service unavailable': ExchangeNotAvailable, // {"error":{"message":"Service unavailable","name":"HTTPError"}}
'Server Error': ExchangeError, // {"error":{"message":"Server Error","name":"HTTPError"}}
'Unable to cancel order due to existing state': InvalidOrder,
},
},
'precisionMode': TICK_SIZE,
@@ -1219,6 +1220,43 @@ module.exports = class bitmex extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// "orderID":"56222c7a-9956-413a-82cf-99f4812c214b",
// "clOrdID":"",
// "clOrdLinkID":"",
// "account":1455728,
// "symbol":"XBTUSD",
// "side":"Sell",
// "simpleOrderQty":null,
// "orderQty":1,
// "price":40000,
// "displayQty":null,
// "stopPx":null,
// "pegOffsetValue":null,
// "pegPriceType":"",
// "currency":"USD",
// "settlCurrency":"XBt",
// "ordType":"Limit",
// "timeInForce":"GoodTillCancel",
// "execInst":"",
// "contingencyType":"",
// "exDestination":"XBME",
// "ordStatus":"New",
// "triggered":"",
// "workingIndicator":true,
// "ordRejReason":"",
// "simpleLeavesQty":null,
// "leavesQty":1,
// "simpleCumQty":null,
// "cumQty":0,
// "avgPx":null,
// "multiLegReportingType":"SingleSecurity",
// "text":"Submitted via API.",
// "transactTime":"2021-01-02T21:38:49.246Z",
// "timestamp":"2021-01-02T21:38:49.246Z"
// }
//
const status = this.parseOrderStatus (this.safeString (order, 'ordStatus'));
const marketId = this.safeString (order, 'symbol');
const symbol = this.safeSymbol (marketId, market);
@@ -1248,6 +1286,8 @@ module.exports = class bitmex extends Exchange {
const clientOrderId = this.safeString (order, 'clOrdID');
const timeInForce = this.parseTimeInForce (this.safeString (order, 'timeInForce'));
const stopPrice = this.safeFloat (order, 'stopPx');
const execInst = this.safeString (order, 'execInst');
const postOnly = (execInst === 'ParticipateDoNotInitiate');
return {
'info': order,
'id': id,
@@ -1258,6 +1298,7 @@ module.exports = class bitmex extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -1326,18 +1367,21 @@ module.exports = class bitmex extends Exchange {
const request = {
'symbol': market['id'],
'side': this.capitalize (side),
'orderQty': amount,
'orderQty': parseFloat (this.amountToPrecision (symbol, amount)),
'ordType': orderType,
};
if (price !== undefined) {
if (orderType === 'Stop') {
const stopPrice = this.safeFloat2 (params, 'stopPx', 'stopPrice');
request['stopPx'] = stopPrice;
params = this.omit (params, [ 'stopPx', 'stopPrice' ]);
if ((orderType === 'Stop') || (orderType === 'StopLimit') || (orderType === 'MarketIfTouched') || (orderType === 'LimitIfTouched')) {
const stopPrice = this.safeFloat2 (params, 'stopPx', 'stopPrice');
if (stopPrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires a stopPx or stopPrice parameter for the ' + orderType + ' order type');
} else {
request['price'] = price;
request['stopPx'] = parseFloat (this.priceToPrecision (symbol, stopPrice));
params = this.omit (params, [ 'stopPx', 'stopPrice' ]);
}
}
if ((orderType === 'Limit') || (orderType === 'StopLimit') || (orderType === 'LimitIfTouched')) {
request['price'] = parseFloat (this.priceToPrecision (symbol, price));
}
const clientOrderId = this.safeString2 (params, 'clOrdID', 'clientOrderId');
if (clientOrderId !== undefined) {
request['clOrdID'] = clientOrderId;
@@ -790,7 +790,10 @@ module.exports = class bitpanda extends Exchange {
//
const feeInfo = this.safeValue (trade, 'fee', {});
trade = this.safeValue (trade, 'trade', trade);
const timestamp = this.parse8601 (this.safeString (trade, 'time'));
let timestamp = this.safeInteger (trade, 'trade_timestamp');
if (timestamp === undefined) {
timestamp = this.parse8601 (this.safeString (trade, 'time'));
}
const side = this.safeStringLower2 (trade, 'side', 'taker_side');
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'amount');
@@ -965,7 +968,7 @@ module.exports = class bitpanda extends Exchange {
if (since !== undefined) {
const to = this.safeString (params, 'to');
if (to === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits requires a "to" iso8601 string param with the since argument is specified');
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a "to" iso8601 string param with the since argument is specified');
}
request['from'] = this.iso8601 (since);
}
@@ -1020,7 +1023,7 @@ module.exports = class bitpanda extends Exchange {
if (since !== undefined) {
const to = this.safeString (params, 'to');
if (to === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a "to" iso8601 string param with the since argument is specified');
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a "to" iso8601 string param with the since argument is specified');
}
request['from'] = this.iso8601 (since);
}
@@ -1325,6 +1328,7 @@ module.exports = class bitpanda extends Exchange {
}
const timeInForce = this.parseTimeInForce (this.safeString (order, 'time_in_force'));
const stopPrice = this.safeFloat (order, 'trigger_price');
const postOnly = this.safeValue (order, 'is_post_only');
const result = {
'id': id,
'clientOrderId': clientOrderId,
@@ -1335,6 +1339,7 @@ module.exports = class bitpanda extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -1409,7 +1414,7 @@ module.exports = class bitpanda extends Exchange {
if (uppercaseType === 'STOP') {
const triggerPrice = this.safeFloat (params, 'trigger_price');
if (triggerPrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires a trigger_price param for ' + type + ' orders');
throw new ArgumentsRequired (this.id + ' createOrder() requires a trigger_price param for ' + type + ' orders');
}
request['trigger_price'] = this.priceToPrecision (symbol, triggerPrice);
params = this.omit (params, 'trigger_price');
@@ -1560,7 +1565,7 @@ module.exports = class bitpanda extends Exchange {
if (since !== undefined) {
const to = this.safeString (params, 'to');
if (to === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a "to" iso8601 string param with the since argument is specified, max range is 100 days');
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a "to" iso8601 string param with the since argument is specified, max range is 100 days');
}
request['from'] = this.iso8601 (since);
}
@@ -1724,7 +1729,7 @@ module.exports = class bitpanda extends Exchange {
if (since !== undefined) {
const to = this.safeString (params, 'to');
if (to === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a "to" iso8601 string param with the since argument is specified, max range is 100 days');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a "to" iso8601 string param with the since argument is specified, max range is 100 days');
}
request['from'] = this.iso8601 (since);
}
@@ -4,7 +4,7 @@
const Exchange = require ('./base/Exchange');
const { ExchangeError, InvalidNonce, AuthenticationError, OrderNotFound } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const { TICK_SIZE, ROUND } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -181,23 +181,25 @@ module.exports = class bitso extends Exchange {
const taker = this.safeFloat (flatRate, 'taker');
const feeTiers = this.safeValue (fees, 'structure', []);
const fee = {
'maker': maker,
'taker': taker,
'taker': parseFloat (this.decimalToPrecision (taker / 100, ROUND, 0.00000001, TICK_SIZE)),
'maker': parseFloat (this.decimalToPrecision (maker / 100, ROUND, 0.00000001, TICK_SIZE)),
'percentage': true,
'tierBased': true,
};
const takerFees = [];
const makerFees = [];
for (let i = 0; i < feeTiers.length; i++) {
const tier = feeTiers[i];
for (let j = 0; j < feeTiers.length; j++) {
const tier = feeTiers[j];
const volume = this.safeFloat (tier, 'volume');
const takerFee = this.safeFloat (tier, 'taker');
const makerFee = this.safeFloat (tier, 'maker');
takerFees.push ([ volume, takerFee ]);
makerFees.push ([ volume, makerFee ]);
if (i === 0) {
fee['taker'] = taker;
fee['maker'] = maker;
const takerFeeToPrecision = parseFloat (this.decimalToPrecision (takerFee / 100, ROUND, 0.00000001, TICK_SIZE));
const makerFeeToPrecision = parseFloat (this.decimalToPrecision (makerFee / 100, ROUND, 0.00000001, TICK_SIZE));
takerFees.push ([ volume, takerFeeToPrecision ]);
makerFees.push ([ volume, makerFeeToPrecision ]);
if (j === 0) {
fee['taker'] = parseFloat (this.decimalToPrecision (taker / 100, ROUND, 0.00000001, TICK_SIZE));
fee['maker'] = parseFloat (this.decimalToPrecision (maker / 100, ROUND, 0.00000001, TICK_SIZE));
}
}
const tiers = {
@@ -434,6 +436,7 @@ module.exports = class bitso extends Exchange {
'symbol': symbol,
'type': orderType,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { AuthenticationError, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder, ArgumentsRequired, OnMaintenance } = require ('./base/errors');
const { AuthenticationError, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder, ArgumentsRequired, OnMaintenance, ExchangeNotAvailable } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -223,6 +223,7 @@ module.exports = class bitstamp extends Exchange {
'Order not found': OrderNotFound,
'Price is more than 20% below market price.': InvalidOrder,
'Bitstamp.net is under scheduled maintenance.': OnMaintenance, // { "error": "Bitstamp.net is under scheduled maintenance. We'll be back soon." }
'Order could not be placed.': ExchangeNotAvailable, // Order could not be placed (perhaps due to internal error or trade halt). Please retry placing order.
},
'broad': {
'Minimum order size is': InvalidOrder, // Minimum order size is 5.0 EUR.
@@ -705,7 +706,7 @@ module.exports = class bitstamp extends Exchange {
const duration = this.parseTimeframe (timeframe);
if (limit === undefined) {
if (since === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument');
throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument or a limit argument');
} else {
limit = 1000;
const start = parseInt (since / 1000);
@@ -771,8 +772,8 @@ module.exports = class bitstamp extends Exchange {
return {
'info': balance,
'symbol': symbol,
'maker': balance.fee,
'taker': balance.fee,
'maker': balance['fee'],
'taker': balance['fee'],
};
}
@@ -1262,6 +1263,7 @@ module.exports = class bitstamp extends Exchange {
'symbol': symbol,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { BadSymbol, ExchangeError, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection, PermissionDenied, AddressPending, OnMaintenance, BadRequest } = require ('./base/errors');
const { BadSymbol, ExchangeError, ExchangeNotAvailable, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, DDoSProtection, PermissionDenied, AddressPending, OnMaintenance, BadRequest, InvalidAddress } = require ('./base/errors');
const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number');
// ---------------------------------------------------------------------------
@@ -23,6 +23,7 @@ module.exports = class bittrex extends Exchange {
'CORS': false,
'cancelAllOrders': true,
'cancelOrder': true,
'createDepositAddress': true,
'createMarketOrder': true,
'createOrder': true,
'fetchBalance': true,
@@ -137,10 +138,10 @@ module.exports = class bittrex extends Exchange {
},
'fees': {
'trading': {
'tierBased': false,
'tierBased': true,
'percentage': true,
'maker': 0.0025,
'taker': 0.0025,
'maker': 0.0035,
'taker': 0.0035,
},
'funding': {
'tierBased': false,
@@ -758,7 +759,22 @@ module.exports = class bittrex extends Exchange {
const isCeilingMarket = (uppercaseType === 'CEILING_MARKET');
const isCeilingOrder = isCeilingLimit || isCeilingMarket;
if (isCeilingOrder) {
request['ceiling'] = this.priceToPrecision (symbol, price);
let cost = undefined;
if (isCeilingLimit) {
request['limit'] = this.priceToPrecision (symbol, price);
cost = this.safeFloat2 (params, 'ceiling', 'cost', amount);
} else if (isCeilingMarket) {
cost = this.safeFloat2 (params, 'ceiling', 'cost');
if (cost === undefined) {
if (price === undefined) {
cost = amount;
} else {
cost = amount * price;
}
}
}
params = this.omit (params, [ 'ceiling', 'cost' ]);
request['ceiling'] = this.costToPrecision (symbol, cost);
// bittrex only accepts IMMEDIATE_OR_CANCEL or FILL_OR_KILL for ceiling orders
request['timeInForce'] = 'IMMEDIATE_OR_CANCEL';
} else {
@@ -975,7 +991,7 @@ module.exports = class bittrex extends Exchange {
'GOOD_TIL_CANCELLED': 'GTC',
'IMMEDIATE_OR_CANCEL': 'IOC',
'FILL_OR_KILL': 'FOK',
'POST_ONLY_GOOD_TIL_CANCELLED': 'POST_ONLY_GOOD_TIL_CANCELLED',
'POST_ONLY_GOOD_TIL_CANCELLED': 'PO',
};
return this.safeString (timeInForces, timeInForce, timeInForce);
}
@@ -1045,6 +1061,7 @@ module.exports = class bittrex extends Exchange {
status = 'canceled';
}
const timeInForce = this.parseTimeInForce (this.safeString (order, 'timeInForce'));
const postOnly = (timeInForce === 'PO');
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
@@ -1054,6 +1071,7 @@ module.exports = class bittrex extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': direction,
'price': limit,
'stopPrice': undefined,
@@ -1191,6 +1209,40 @@ module.exports = class bittrex extends Exchange {
return this.parseOrders (response, market, since, limit);
}
async createDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currencySymbol': currency['id'],
};
const response = await this.privatePostAddresses (this.extend (request, params));
//
// {
// "status":"PROVISIONED",
// "currencySymbol":"XRP",
// "cryptoAddress":"rPVMhWBsfF9iMXYj3aAzJVkPDTFNSyWdKy",
// "cryptoAddressTag":"392034158"
// }
//
let address = this.safeString (response, 'cryptoAddress');
const message = this.safeString (response, 'status');
if (!address || message === 'REQUESTED') {
throw new AddressPending (this.id + ' the address for ' + code + ' is being generated (pending, not ready yet, retry again later)');
}
let tag = this.safeString (response, 'cryptoAddressTag');
if ((tag === undefined) && (currency['type'] in this.options['tag'])) {
tag = address;
address = currency['address'];
}
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': response,
};
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
@@ -1198,18 +1250,21 @@ module.exports = class bittrex extends Exchange {
'currencySymbol': currency['id'],
};
const response = await this.privateGetAddressesCurrencySymbol (this.extend (request, params));
// {
// "status": "PROVISIONED",
// "currencySymbol": "BTC",
// "cryptoAddress": "1PhmYjnJPZH5NUwV8AUjqkeDkCBpbE2xqX"
// }
//
// {
// "status":"PROVISIONED",
// "currencySymbol":"XRP",
// "cryptoAddress":"rPVMhWBsfF9iMXYj3aAzJVkPDTFNSyWdKy",
// "cryptoAddressTag":"392034158"
// }
//
let address = this.safeString (response, 'cryptoAddress');
const message = this.safeString (response, 'status');
if (!address || message === 'REQUESTED') {
throw new AddressPending (this.id + ' the address for ' + code + ' is being generated (pending, not ready yet, retry again later)');
}
let tag = undefined;
if (currency['type'] in this.options['tag']) {
let tag = this.safeString (response, 'cryptoAddressTag');
if ((tag === undefined) && (currency['type'] in this.options['tag'])) {
tag = address;
address = currency['address'];
}
@@ -1309,6 +1364,9 @@ module.exports = class bittrex extends Exchange {
this.throwExactlyMatchedException (this.exceptions['exact'], code, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], code, feedback);
}
if ((code === 'NOT_FOUND') && (url.indexOf ('addresses') >= 0)) {
throw new InvalidAddress (feedback);
}
// throw new ExchangeError (this.id + ' malformed response ' + this.json (response));
return;
}
@@ -4,7 +4,7 @@
const Exchange = require ('./base/Exchange');
const { ExchangeError, BadSymbol, AuthenticationError, InsufficientFunds, InvalidOrder, ArgumentsRequired, OrderNotFound, InvalidAddress, BadRequest, RateLimitExceeded, PermissionDenied, ExchangeNotAvailable, AccountSuspended, OnMaintenance } = require ('./base/errors');
const { TRUNCATE } = require ('./base/functions/number');
const { SIGNIFICANT_DIGITS, DECIMAL_PLACES, TRUNCATE, ROUND } = require ('./base/functions/number');
// ----------------------------------------------------------------------------
@@ -221,12 +221,33 @@ module.exports = class bitvavo extends Exchange {
'expires': 1000, // 1 second
},
},
'precisionMode': SIGNIFICANT_DIGITS,
'commonCurrencies': {
'MIOTA': 'IOTA', // https://github.com/ccxt/ccxt/issues/7487
},
});
}
currencyToPrecision (currency, fee) {
return this.decimalToPrecision (fee, 0, this.currencies[currency]['precision']);
}
amountToPrecision (symbol, amount) {
// https://docs.bitfinex.com/docs/introduction#amount-precision
// The amount field allows up to 8 decimals.
// Anything exceeding this will be rounded to the 8th decimal.
return this.decimalToPrecision (amount, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES);
}
priceToPrecision (symbol, price) {
price = this.decimalToPrecision (price, ROUND, this.markets[symbol]['precision']['price'], this.precisionMode);
// https://docs.bitfinex.com/docs/introduction#price-precision
// The precision level of all trading prices is based on significant figures.
// All pairs on Bitfinex use up to 5 significant digits and up to 8 decimals (e.g. 1.2345, 123.45, 1234.5, 0.00012345).
// Prices submit with a precision larger than 5 will be cut by the API.
return this.decimalToPrecision (price, TRUNCATE, 8, DECIMAL_PLACES);
}
async fetchTime (params = {}) {
const response = await this.publicGetTime (params);
//
@@ -768,16 +789,18 @@ module.exports = class bitvavo extends Exchange {
const request = {
'market': market['id'],
'side': side,
'orderType': type,
'orderType': type, // 'market', 'limit', 'stopLoss', 'stopLossLimit', 'takeProfit', 'takeProfitLimit'
// 'amount': this.amountToPrecision (symbol, amount),
// 'price': this.priceToPrecision (symbol, price),
// 'amountQuote': this.costToPrecision (symbol, cost),
// 'timeInForce': 'GTC', // "GTC" "IOC" "FOK"
// 'selfTradePrevention': "decrementAndCancel", // "decrementAndCancel" "cancelOldest" "cancelNewest" "cancelBoth"
// 'timeInForce': 'GTC', // 'GTC', 'IOC', 'FOK'
// 'selfTradePrevention': 'decrementAndCancel', // 'decrementAndCancel', 'cancelOldest', 'cancelNewest', 'cancelBoth'
// 'postOnly': false,
// 'disableMarketProtection': false, // don't cancel if the next fill price is 10% worse than the best fill price
// 'responseRequired': true, // false is faster
};
const isStopLimit = (type === 'stopLossLimit') || (type === 'takeProfitLimit');
const isStopMarket = (type === 'stopLoss') || (type === 'takeProfit');
if (type === 'market') {
let cost = undefined;
if (price !== undefined) {
@@ -795,6 +818,26 @@ module.exports = class bitvavo extends Exchange {
} else if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
request['amount'] = this.amountToPrecision (symbol, amount);
} else if (isStopMarket || isStopLimit) {
let stopPrice = this.safeFloat2 (params, 'stopPrice', 'triggerAmount');
if (stopPrice === undefined) {
if (isStopLimit) {
throw new ArgumentsRequired (this.id + ' createOrder requires a stopPrice parameter for a ' + type + ' order');
} else if (isStopMarket) {
if (price === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires a price argument or a stopPrice parameter for a ' + type + ' order');
} else {
stopPrice = price;
}
}
}
if (isStopLimit) {
request['price'] = this.priceToPrecision (symbol, price);
}
params = this.omit (params, [ 'stopPrice', 'triggerAmount' ]);
request['triggerAmount'] = this.priceToPrecision (symbol, stopPrice);
request['triggerType'] = 'price';
request['amount'] = this.amountToPrecision (symbol, amount);
}
const response = await this.privatePostOrder (this.extend (request, params));
//
@@ -856,13 +899,13 @@ module.exports = class bitvavo extends Exchange {
const response = await this.privatePutOrder (this.extend (request, params));
return this.parseOrder (response, market);
} else {
throw new ArgumentsRequired (this.id + ' editOrder requires an amount argument, or a price argument, or non-empty params');
throw new ArgumentsRequired (this.id + ' editOrder() requires an amount argument, or a price argument, or non-empty params');
}
}
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -900,7 +943,7 @@ module.exports = class bitvavo extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
await this.loadMarkets ();
@@ -949,7 +992,7 @@ module.exports = class bitvavo extends Exchange {
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1010,24 +1053,13 @@ module.exports = class bitvavo extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
// 'market': market['id'],
// 'limit': 500,
// 'start': since,
// 'end': this.milliseconds (),
// 'orderIdFrom': 'af76d6ce-9f7c-4006-b715-bb5d430652d0',
// 'orderIdTo': 'af76d6ce-9f7c-4006-b715-bb5d430652d0',
// 'market': market['id'], // rate limit 25 without a market, 1 with market specified
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
if (since !== undefined) {
request['start'] = since;
}
if (limit !== undefined) {
request['limit'] = limit; // default 500, max 1000
}
const response = await this.privateGetOrdersOpen (this.extend (request, params));
//
// [
@@ -1082,6 +1114,7 @@ module.exports = class bitvavo extends Exchange {
'partiallyFilled': 'open',
'expired': 'canceled',
'rejected': 'canceled',
'awaitingTrigger': 'open', // https://github.com/ccxt/ccxt/issues/8489
};
return this.safeString (statuses, status, status);
}
@@ -1183,6 +1216,9 @@ module.exports = class bitvavo extends Exchange {
}
}
const timeInForce = this.safeString (order, 'timeInForce');
const postOnly = this.safeValue (order, 'postOnly');
// https://github.com/ccxt/ccxt/issues/8489
const stopPrice = this.safeFloat (order, 'triggerPrice');
return {
'info': order,
'id': id,
@@ -1193,9 +1229,10 @@ module.exports = class bitvavo extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': undefined,
'stopPrice': stopPrice,
'amount': amount,
'cost': cost,
'average': average,
@@ -1209,7 +1246,7 @@ module.exports = class bitvavo extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -675,7 +675,7 @@ module.exports = class bitz extends Exchange {
}
} else {
if (since !== undefined) {
throw new ArgumentsRequired (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));
@@ -779,6 +779,7 @@ module.exports = class bitz extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -948,7 +949,7 @@ module.exports = class bitz extends Exchange {
async fetchOrdersWithMethod (method, symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -662,6 +662,7 @@ module.exports = class bleutrade extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -460,6 +460,7 @@ module.exports = class braziliex extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -348,6 +348,7 @@ module.exports = class btcalpha extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -306,6 +306,7 @@ module.exports = class btcbox extends Exchange {
'side': side,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'status': status,
'symbol': symbol,
'price': price,
@@ -865,6 +865,7 @@ module.exports = class btcmarkets extends Exchange {
const clientOrderId = this.safeString (order, 'clientOrderId');
const timeInForce = this.safeString (order, 'timeInForce');
const stopPrice = this.safeFloat (order, 'triggerPrice');
const postOnly = this.safeValue (order, 'postOnly');
return {
'info': order,
'id': id,
@@ -875,6 +876,7 @@ module.exports = class btcmarkets extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -335,6 +335,7 @@ module.exports = class btctradeua extends Exchange {
'symbol': symbol,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'side': this.safeString (order, 'type'),
'price': this.safeFloat (order, 'price'),
'stopPrice': undefined,
@@ -351,7 +352,7 @@ module.exports = class btctradeua extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -275,7 +275,7 @@ module.exports = class btcturk extends Exchange {
};
if (type === 'market') {
if (!('Total' in params)) {
throw new ExchangeError (this.id + ' createOrder requires the "Total" extra parameter for market orders (amount and price are both ignored)');
throw new ExchangeError (this.id + ' createOrder() requires the "Total" extra parameter for market orders (amount and price are both ignored)');
}
} else {
request['Price'] = price;
@@ -537,29 +537,65 @@ module.exports = class buda extends Exchange {
}
parseOrder (order, market = undefined) {
//
// {
// 'id': 63679183,
// 'uuid': 'f9697bee-627e-4175-983f-0d5a41963fec',
// 'market_id': 'ETH-CLP',
// 'account_id': 51590,
// 'type': 'Ask',
// 'state': 'received',
// 'created_at': '2021-01-04T08:29:52.730Z',
// 'fee_currency': 'CLP',
// 'price_type': 'limit',
// 'source': None,
// 'limit': ['741000.0', 'CLP'],
// 'amount': ['0.001', 'ETH'],
// 'original_amount': ['0.001', 'ETH'],
// 'traded_amount': ['0.0', 'ETH'],
// 'total_exchanged': ['0.0', 'CLP'],
// 'paid_fee': ['0.0', 'CLP']
// }
//
const id = this.safeString (order, 'id');
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
const marketId = this.safeString (order, 'market_id');
const symbol = this.safeSymbol (marketId, market);
const symbol = this.safeSymbol (marketId, market, '-');
const type = this.safeString (order, 'price_type');
const side = this.safeStringLower (order, 'type');
const status = this.parseOrderStatus (this.safeString (order, 'state'));
const amount = parseFloat (order['original_amount'][0]);
const remaining = parseFloat (order['amount'][0]);
const filled = parseFloat (order['traded_amount'][0]);
const cost = parseFloat (order['total_exchanged'][0]);
let price = this.safeFloat (order, 'limit');
if (price !== undefined) {
price = parseFloat (price[0]);
const originalAmount = this.safeValue (order, 'original_amount', []);
const amount = this.safeFloat (originalAmount, 0);
const remainingAmount = this.safeValue (order, 'amount', []);
const remaining = this.safeFloat (remainingAmount, 0);
const tradedAmount = this.safeValue (order, 'traded_amount', []);
const filled = this.safeFloat (tradedAmount, 0);
const totalExchanged = this.safeValue (order, 'totalExchanged', []);
const cost = this.safeFloat (totalExchanged, 0);
const limitPrice = this.safeValue (order, 'limit', []);
let price = this.safeFloat (limitPrice, 0);
if (price === undefined) {
if (limitPrice !== undefined) {
price = limitPrice;
}
}
if (cost > 0 && filled > 0) {
price = this.priceToPrecision (symbol, cost / filled);
let average = undefined;
if ((cost !== undefined) && (filled !== undefined) && (filled > 0)) {
average = this.priceToPrecision (symbol, cost / filled);
}
const paidFee = this.safeValue (order, 'paid_fee', []);
const feeCost = this.safeFloat (paidFee, 0);
let fee = undefined;
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (paidFee, 1);
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCost,
'code': feeCurrencyCode,
};
}
const fee = {
'cost': parseFloat (order['paid_fee'][0]),
'currency': order['paid_fee'][1],
};
return {
'info': order,
'id': id,
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
@@ -569,17 +605,17 @@ module.exports = class buda extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
'average': average,
'cost': cost,
'amount': amount,
'filled': filled,
'remaining': remaining,
'trades': undefined,
'fee': fee,
'info': order,
'average': undefined,
};
}
@@ -761,6 +761,7 @@ module.exports = class bw extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -777,7 +778,7 @@ module.exports = class bw extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -812,7 +813,7 @@ module.exports = class bw extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -17,6 +17,7 @@ module.exports = class bybit extends Exchange {
'version': 'v2',
'userAgent': undefined,
'rateLimit': 100,
'hostname': 'bybit.com', // bybit.com, bytick.com
'has': {
'cancelOrder': true,
'CORS': true,
@@ -59,9 +60,9 @@ module.exports = class bybit extends Exchange {
'1y': 'Y',
},
'urls': {
'test': 'https://api-testnet.bybit.com',
'test': 'https://api-testnet.{hostname}',
'logo': 'https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg',
'api': 'https://api.bybit.com',
'api': 'https://api.{hostname}',
'www': 'https://www.bybit.com',
'doc': [
'https://bybit-exchange.github.io/docs/inverse/',
@@ -72,117 +73,112 @@ module.exports = class bybit extends Exchange {
'referral': 'https://www.bybit.com/app/register?ref=X7Prm',
},
'api': {
'v2': {
'public': {
'get': [
'orderBook/L2',
'kline/list',
'tickers',
'trading-records',
'symbols',
'liq-records',
'mark-price-kline',
'index-price-kline',
'premium-index-kline',
'open-interest',
'big-deal',
'account-ratio',
'time',
'announcement',
],
},
'private': {
'get': [
'order/list',
'order',
'stop-order/list',
'stop-order',
'position/list',
'execution/list',
'trade/closed-pnl/list',
'funding/prev-funding-rate',
'funding/prev-funding',
'funding/predicted-funding',
'account/api-key',
'account/lcp',
'wallet/balance',
'wallet/fund/records',
'wallet/withdraw/list',
'exchange-order/list',
],
'post': [
'order/create',
'order/cancel',
'order/cancelAll',
'order/replace',
'stop-order/create',
'stop-order/cancel',
'stop-order/cancelAll',
'stop-order/replace',
'position/change-position-margin',
'position/trading-stop',
'position/leverage/save',
],
},
},
'public': {
'get': [
'orderBook/L2',
'kline/list',
'tickers',
'trading-records',
'symbols',
'liq-records',
'mark-price-kline',
'open-interest',
'big-deal',
'account-ratio',
'time',
'announcement',
],
'linear': {
'get': [
'kline',
'recent-trading-records',
'funding/prev-funding-rate',
'mark-price-kline',
'index-price-kline',
'premium-index-kline',
'risk-limit',
],
},
},
'private': {
'get': [
'order/list',
'order',
'stop-order/list',
'stop-order',
'position/list',
'wallet/balance',
'execution/list',
'trade/closed-pnl/list',
'account/lcp',
'exchange-order/list',
],
'post': [
'order/create',
'order/cancel',
'order/cancelAll',
'order/replace',
'stop-order/create',
'stop-order/cancel',
'stop-order/cancelAll',
'stop-order/replace',
],
'linear': {
'get': [
'order/list',
'order/search',
'stop-order/list',
'stop-order/search',
'position/list',
'trade/execution/list',
'trade/closed-pnl/list',
'funding/predicted-funding',
'funding/prev-funding',
],
'post': [
'order/create',
'order/cancel',
'order/cancel-all',
'order/replace',
'stop-order/create',
'stop-order/cancel',
'stop-order/cancel-all',
'stop-order/replace',
'position/set-auto-add-margin',
'position/switch-isolated',
'tpsl/switch-mode',
'position/add-margin',
'position/set-leverage',
'position/trading-stop',
],
},
},
'openapi': {
'get': [
'order/list', // deprecated
'stop-order/list', // deprecated
'wallet/risk-limit/list',
'funding/prev-funding-rate',
'funding/prev-funding',
'funding/predicted-funding',
'api-key',
'wallet/fund/records',
'wallet/withdraw/list',
],
'post': [
'order/replace', // deprecated
'stop-order/create', // deprecated
'stop-order/cancel', // deprecated
'stop-order/replace', // deprecated
'position/trading-stop',
'wallet/risk-limit',
],
},
'publicLinear': {
'get': [
'kline',
'recent-trading-records',
'funding/prev-funding-rate',
'mark-price-kline',
'risk-limit',
],
},
'privateLinear': {
'get': [
'order/list',
'order/search',
'stop-order/list',
'stop-order/search',
'position/list',
'trade/execution/list',
'trade/closed-pnl/list',
'funding/prev-funding',
'funding/predicted-funding',
],
'post': [
'order/create',
'order/cancel',
'order/cancel-all',
'order/replace',
'stop-order/create',
'stop-order/cancel',
'stop-order/cancel-all',
'stop-order/replace',
'position/switch-isolated',
'position/set-auto-add-margin',
'tpsl/switch-mode',
'position/set-leverage',
'position/trading-stop',
'position/add-margin',
],
},
'position': {
'post': [
'change-position-margin',
],
},
'user': {
'get': [
'leverage', // deprecated
],
'post': [
'leverage/save',
],
'wallet': {
'get': [
'risk-limit/list',
],
'post': [
'risk-limit',
],
},
},
},
'httpExceptions': {
@@ -285,6 +281,7 @@ module.exports = class bybit extends Exchange {
'options': {
'marketTypes': {
'BTC/USDT': 'linear',
'BCH/USDT': 'linear',
'ETH/USDT': 'linear',
'LTC/USDT': 'linear',
'XTZ/USDT': 'linear',
@@ -292,10 +289,10 @@ module.exports = class bybit extends Exchange {
},
'code': 'BTC',
'cancelAllOrders': {
'method': 'privatePostOrderCancelAll', // privatePostStopOrderCancelAll
// 'method': 'v2PrivatePostOrderCancelAll', // v2PrivatePostStopOrderCancelAll
},
'recvWindow': 5 * 1000, // 5 sec default
'timeDifference': 0, // the difference between system clock and Binance clock
'timeDifference': 0, // the difference between system clock and exchange server clock
'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation
},
'fees': {
@@ -327,7 +324,7 @@ module.exports = class bybit extends Exchange {
}
async fetchTime (params = {}) {
const response = await this.publicGetTime (params);
const response = await this.v2PublicGetTime (params);
//
// {
// ret_code: 0,
@@ -345,30 +342,7 @@ module.exports = class bybit extends Exchange {
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference ();
}
const response = await this.publicGetSymbols (params);
//
// {
// ret_code: 0,
// ret_msg: 'OK',
// ext_code: '',
// ext_info: '',
// result: [
// {
// name: 'BTCUSD',
// base_currency: 'BTC',
// quote_currency: 'USD',
// price_scale: 2,
// taker_fee: '0.00075',
// maker_fee: '-0.00025',
// leverage_filter: { min_leverage: 1, max_leverage: 100, leverage_step: '0.01' },
// price_filter: { min_price: '0.5', max_price: '999999.5', tick_size: '0.5' },
// lot_size_filter: { max_trading_qty: 1000000, min_trading_qty: 1, qty_step: 1 }
// },
// ],
// time_now: '1583930495.454196'
// }
//
// sandbox/testnet
const response = await this.v2PublicGetSymbols (params);
//
// {
// "ret_code":0,
@@ -377,8 +351,8 @@ module.exports = class bybit extends Exchange {
// "ext_info":"",
// "result":[
// {
// "symbol":"BTCUSD",
// "symbol_alias":"BTCUSD",
// "name":"BTCUSD",
// "alias":"BTCUSD",
// "status":"Trading",
// "base_currency":"BTC",
// "quote_currency":"USD",
@@ -388,9 +362,22 @@ module.exports = class bybit extends Exchange {
// "leverage_filter":{"min_leverage":1,"max_leverage":100,"leverage_step":"0.01"},
// "price_filter":{"min_price":"0.5","max_price":"999999.5","tick_size":"0.5"},
// "lot_size_filter":{"max_trading_qty":1000000,"min_trading_qty":1,"qty_step":1}
// }
// },
// {
// "name":"BTCUSDT",
// "alias":"BTCUSDT",
// "status":"Trading",
// "base_currency":"BTC",
// "quote_currency":"USDT",
// "price_scale":2,
// "taker_fee":"0.00075",
// "maker_fee":"-0.00025",
// "leverage_filter":{"min_leverage":1,"max_leverage":100,"leverage_step":"0.01"},
// "price_filter":{"min_price":"0.5","max_price":"999999.5","tick_size":"0.5"},
// "lot_size_filter":{"max_trading_qty":100,"min_trading_qty":0.001,"qty_step":0.001}
// },
// ],
// "time_now":"1605916574.118500"
// "time_now":"1610539664.818033"
// }
//
const markets = this.safeValue (response, 'result', []);
@@ -457,65 +444,6 @@ module.exports = class bybit extends Exchange {
return result;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const request = {};
const coin = this.safeString (params, 'coin');
const code = this.safeString (params, 'code');
if (coin !== undefined) {
request['coin'] = coin;
} else if (code !== undefined) {
const currency = this.currency (code);
request['coin'] = currency['id'];
}
const response = await this.privateGetWalletBalance (this.extend (request, params));
//
// {
// ret_code: 0,
// ret_msg: 'OK',
// ext_code: '',
// ext_info: '',
// result: {
// BTC: {
// equity: 0,
// available_balance: 0,
// used_margin: 0,
// order_margin: 0,
// position_margin: 0,
// occ_closing_fee: 0,
// occ_funding_fee: 0,
// wallet_balance: 0,
// realised_pnl: 0,
// unrealised_pnl: 0,
// cum_realised_pnl: 0,
// given_cash: 0,
// service_cash: 0
// }
// },
// time_now: '1583937810.370020',
// rate_limit_status: 119,
// rate_limit_reset_ms: 1583937810367,
// rate_limit: 120
// }
//
const result = {
'info': response,
};
const balances = this.safeValue (response, 'result', {});
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const balance = balances[currencyId];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'available_balance');
account['used'] = this.safeFloat (balance, 'used_margin');
account['total'] = this.safeFloat (balance, 'equity');
result[code] = account;
}
return this.parseBalance (result);
}
parseTicker (ticker, market = undefined) {
//
// fetchTicker
@@ -594,7 +522,7 @@ module.exports = class bybit extends Exchange {
const request = {
'symbol': market['id'],
};
const response = await this.publicGetTickers (this.extend (request, params));
const response = await this.v2PublicGetTickers (this.extend (request, params));
//
// {
// ret_code: 0,
@@ -642,7 +570,7 @@ module.exports = class bybit extends Exchange {
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const response = await this.publicGetTickers (params);
const response = await this.v2PublicGetTickers (params);
//
// {
// ret_code: 0,
@@ -740,7 +668,7 @@ module.exports = class bybit extends Exchange {
const now = this.seconds ();
if (since === undefined) {
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument');
throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument or a limit argument');
} else {
request['from'] = now - limit * duration;
}
@@ -752,7 +680,7 @@ module.exports = class bybit extends Exchange {
}
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'publicLinearGetKline' : 'publicGetKlineList';
const method = (marketType === 'linear') ? 'publicLinearGetKline' : 'v2PublicGetKlineList';
const response = await this[method] (this.extend (request, params));
//
// inverse perpetual BTC/USD
@@ -907,7 +835,7 @@ module.exports = class bybit extends Exchange {
}
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'publicGetTradingRecords';
const method = (marketType === 'linear') ? 'publicLinearGetRecentTradingRecords' : 'v2PublicGetTradingRecords';
const response = await this[method] (this.extend (request, params));
//
// {
@@ -961,7 +889,7 @@ module.exports = class bybit extends Exchange {
const request = {
'symbol': market['id'],
};
const response = await this.publicGetOrderBookL2 (this.extend (request, params));
const response = await this.v2PublicGetOrderBookL2 (this.extend (request, params));
//
// {
// ret_code: 0,
@@ -984,6 +912,65 @@ module.exports = class bybit extends Exchange {
return this.parseOrderBook (result, timestamp, 'Buy', 'Sell', 'price', 'size');
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const request = {};
const coin = this.safeString (params, 'coin');
const code = this.safeString (params, 'code');
if (coin !== undefined) {
request['coin'] = coin;
} else if (code !== undefined) {
const currency = this.currency (code);
request['coin'] = currency['id'];
}
const response = await this.v2PrivateGetWalletBalance (this.extend (request, params));
//
// {
// ret_code: 0,
// ret_msg: 'OK',
// ext_code: '',
// ext_info: '',
// result: {
// BTC: {
// equity: 0,
// available_balance: 0,
// used_margin: 0,
// order_margin: 0,
// position_margin: 0,
// occ_closing_fee: 0,
// occ_funding_fee: 0,
// wallet_balance: 0,
// realised_pnl: 0,
// unrealised_pnl: 0,
// cum_realised_pnl: 0,
// given_cash: 0,
// service_cash: 0
// }
// },
// time_now: '1583937810.370020',
// rate_limit_status: 119,
// rate_limit_reset_ms: 1583937810367,
// rate_limit: 120
// }
//
const result = {
'info': response,
};
const balances = this.safeValue (response, 'result', {});
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const balance = balances[currencyId];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeFloat (balance, 'available_balance');
account['used'] = this.safeFloat (balance, 'used_margin');
account['total'] = this.safeFloat (balance, 'equity');
result[code] = account;
}
return this.parseBalance (result);
}
parseOrderStatus (status) {
const statuses = {
// basic orders
@@ -1166,6 +1153,7 @@ module.exports = class bybit extends Exchange {
}
const timeInForce = this.parseTimeInForce (this.safeString (order, 'time_in_force'));
const stopPrice = this.safeFloat (order, 'stop_px');
const postOnly = (timeInForce === 'PO');
return {
'info': order,
'id': id,
@@ -1176,6 +1164,7 @@ module.exports = class bybit extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -1192,7 +1181,7 @@ module.exports = class bybit extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1206,7 +1195,7 @@ module.exports = class bybit extends Exchange {
};
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearGetOrderSearch' : 'privateGetOrder';
let method = (marketType === 'linear') ? 'privateLinearGetOrderSearch' : 'v2PrivateGetOrder';
const stopOrderId = this.safeString (params, 'stop_order_id');
if (stopOrderId === undefined) {
const orderLinkId = this.safeString (params, 'order_link_id');
@@ -1214,7 +1203,7 @@ module.exports = class bybit extends Exchange {
request['order_id'] = id;
}
} else {
method = (marketType === 'linear') ? 'privateLinearGetStopOrderSearch' : 'privateGetStopOrder';
method = (marketType === 'linear') ? 'privateLinearGetStopOrderSearch' : 'v2PrivateGetStopOrder';
}
const response = await this[method] (this.extend (request, params));
//
@@ -1327,7 +1316,7 @@ module.exports = class bybit extends Exchange {
if (price !== undefined) {
request['price'] = parseFloat (this.priceToPrecision (symbol, price));
} else {
throw new ArgumentsRequired (this.id + ' createOrder requires a price argument for a ' + type + ' order');
throw new ArgumentsRequired (this.id + ' createOrder() requires a price argument for a ' + type + ' order');
}
}
const clientOrderId = this.safeString2 (params, 'order_link_id', 'clientOrderId');
@@ -1339,7 +1328,7 @@ module.exports = class bybit extends Exchange {
const basePrice = this.safeValue (params, 'base_price');
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearPostOrderCreate' : 'privatePostOrderCreate';
let method = (marketType === 'linear') ? 'privateLinearPostOrderCreate' : 'v2PrivatePostOrderCreate';
if (marketType === 'linear') {
method = 'privateLinearPostOrderCreate';
request['reduce_only'] = false;
@@ -1347,15 +1336,15 @@ module.exports = class bybit extends Exchange {
}
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');
throw new ArgumentsRequired (this.id + ' createOrder() requires both the stop_px and base_price params for a conditional ' + type + ' order');
} else {
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCreate' : 'openapiPostStopOrderCreate';
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCreate' : 'v2PrivatePostStopOrderCreate';
request['stop_px'] = parseFloat (this.priceToPrecision (symbol, stopPx));
request['base_price'] = parseFloat (this.priceToPrecision (symbol, basePrice));
params = this.omit (params, [ 'stop_px', 'stopPrice', 'base_price' ]);
}
} else if (basePrice !== undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires both the stop_px and base_price params for a conditional ' + type + ' order');
throw new ArgumentsRequired (this.id + ' createOrder() requires both the stop_px and base_price params for a conditional ' + type + ' order');
}
const response = await this[method] (this.extend (request, params));
//
@@ -1441,7 +1430,7 @@ module.exports = class bybit extends Exchange {
async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires an symbol argument');
throw new ArgumentsRequired (this.id + ' editOrder() requires an symbol argument');
}
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
@@ -1457,17 +1446,23 @@ module.exports = class bybit extends Exchange {
// 'stop_order_id': id, // only for conditional orders
// 'p_r_trigger_price': 123.45, // new trigger price also known as stop_px
};
let method = (marketType === 'linear') ? 'privateLinearPostOrderReplace' : 'openapiPostOrderReplace';
let method = (marketType === 'linear') ? 'privateLinearPostOrderReplace' : 'v2PrivatePostOrderReplace';
const stopOrderId = this.safeString (params, 'stop_order_id');
if (stopOrderId !== undefined) {
method = (marketType === 'linear') ? 'privateLinearPostStopOrderReplace' : 'openapiPostStopOrderReplace';
method = (marketType === 'linear') ? 'privateLinearPostStopOrderReplace' : 'v2PrivatePostStopOrderReplace';
request['stop_order_id'] = stopOrderId;
params = this.omit (params, [ 'stop_order_id' ]);
} else {
request['order_id'] = id;
}
if (amount !== undefined) {
request['p_r_qty'] = parseInt (this.amountToPrecision (symbol, amount));
let qty = this.amountToPrecision (symbol, amount);
if (market['inverse']) {
qty = parseInt (qty);
} else {
qty = parseFloat (qty);
}
request['p_r_qty'] = qty;
}
if (price !== undefined) {
request['p_r_price'] = parseFloat (this.priceToPrecision (symbol, price));
@@ -1510,7 +1505,7 @@ module.exports = class bybit extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1524,7 +1519,7 @@ module.exports = class bybit extends Exchange {
};
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeValue (marketTypes, symbol);
let method = (marketType === 'linear') ? 'privateLinearPostOrderCancel' : 'privatePostOrderCancel';
let method = (marketType === 'linear') ? 'privateLinearPostOrderCancel' : 'v2PrivatePostOrderCancel';
const stopOrderId = this.safeString (params, 'stop_order_id');
if (stopOrderId === undefined) {
const orderLinkId = this.safeString (params, 'order_link_id');
@@ -1532,7 +1527,7 @@ module.exports = class bybit extends Exchange {
request['order_id'] = id;
}
} else {
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCancel' : 'openapiPostStopOrderCancel';
method = (marketType === 'linear') ? 'privateLinearPostStopOrderCancel' : 'v2PrivatePostStopOrderCancel';
}
const response = await this[method] (this.extend (request, params));
const result = this.safeValue (response, 'result', {});
@@ -1541,17 +1536,17 @@ module.exports = class bybit extends Exchange {
async cancelAllOrders (symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelAllOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelAllOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
const options = this.safeValue (this.options, 'cancelAllOrders');
const options = this.safeValue (this.options, 'cancelAllOrders', {});
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const defaultMethod = (marketType === 'linear') ? 'privateLinearPostOrderCancelAll' : 'privatePostOrderCancelAll';
const defaultMethod = (marketType === 'linear') ? 'privateLinearPostOrderCancelAll' : 'v2PrivatePostOrderCancelAll';
const method = this.safeString (options, 'method', defaultMethod);
const response = await this[method] (this.extend (request, params));
const result = this.safeValue (response, 'result', []);
@@ -1583,7 +1578,7 @@ module.exports = class bybit extends Exchange {
const options = this.safeValue (this.options, 'fetchOrders', {});
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
let defaultMethod = (marketType === 'linear') ? 'privateLinearGetOrderList' : 'openapiGetOrderList';
let defaultMethod = (marketType === 'linear') ? 'privateLinearGetOrderList' : 'v2PrivateGetOrderList';
let query = params;
if (('stop_order_id' in params) || ('stop_order_status' in params)) {
let stopOrderStatus = this.safeValue (params, 'stopOrderStatus');
@@ -1594,7 +1589,7 @@ module.exports = class bybit extends Exchange {
request['stop_order_status'] = stopOrderStatus;
query = this.omit (params, 'stop_order_status');
}
defaultMethod = (marketType === 'linear') ? 'privateLinearGetStopOrderList' : 'openapiGetStopOrderList';
defaultMethod = (marketType === 'linear') ? 'privateLinearGetStopOrderList' : 'v2PrivateGetStopOrderList';
}
const method = this.safeString (options, 'method', defaultMethod);
const response = await this[method] (this.extend (request, query));
@@ -1760,7 +1755,7 @@ module.exports = class bybit extends Exchange {
if (symbol === undefined) {
const orderId = this.safeString (params, 'order_id');
if (orderId === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument or an order_id param');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument or an order_id param');
} else {
request['order_id'] = orderId;
params = this.omit (params, 'order_id');
@@ -1777,7 +1772,7 @@ module.exports = class bybit extends Exchange {
}
const marketTypes = this.safeValue (this.options, 'marketTypes', {});
const marketType = this.safeString (marketTypes, symbol);
const method = (marketType === 'linear') ? 'privateLinearGetTradeExecutionList' : 'privateGetExecutionList';
const method = (marketType === 'linear') ? 'privateLinearGetTradeExecutionList' : 'v2PrivateGetExecutionList';
const response = await this[method] (this.extend (request, params));
//
// inverse
@@ -1867,41 +1862,60 @@ module.exports = class bybit extends Exchange {
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currency': currency['id'],
// 'coin': currency['id'],
// 'currency': currency['id'], // alias
// 'start_date': this.iso8601 (since),
// 'end_date': this.iso8601 (till),
'wallet_fund_type': 'Deposit', // Deposit, Withdraw, RealisedPNL, Commission, Refund, Prize, ExchangeOrderWithdraw, ExchangeOrderDeposit
// 'page': 1,
// 'limit': 20, // max 50
};
if (limit !== undefined) {
request['count'] = limit;
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['coin'] = currency['id'];
}
const response = await this.privateGetGetDeposits (this.extend (request, params));
if (since !== undefined) {
request['start_date'] = this.ymd (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.v2PrivateGetWalletFundRecords (this.extend (request, params));
//
// {
// "jsonrpc": "2.0",
// "id": 5611,
// "ret_code": 0,
// "ret_msg": "ok",
// "ext_code": "",
// "result": {
// "count": 1,
// "data": [
// {
// "address": "2N35qDKDY22zmJq9eSyiAerMD4enJ1xx6ax",
// "amount": 5,
// "currency": "BTC",
// "received_timestamp": 1549295017670,
// "state": "completed",
// "transaction_id": "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda",
// "updated_timestamp": 1549295130159
// "id": 234467,
// "user_id": 1,
// "coin": "BTC",
// "wallet_id": 27913,
// "type": "Realized P&L",
// "amount": "-0.00000006",
// "tx_id": "",
// "address": "BTCUSD",
// "wallet_balance": "0.03000330",
// "exec_time": "2019-12-09T00:00:25.000Z",
// "cross_seq": 0
// }
// ]
// }
// },
// "ext_info": null,
// "time_now": "1577481867.115552",
// "rate_limit_status": 119,
// "rate_limit_reset_ms": 1577481867122,
// "rate_limit": 120
// }
//
const result = this.safeValue (response, 'result', {});
const data = this.safeValue (result, 'data', []);
return this.parseTransactions (data, currency, since, limit, params);
return this.parseTransactions (data, currency, since, limit);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
@@ -1920,12 +1934,12 @@ module.exports = class bybit extends Exchange {
request['coin'] = currency['id'];
}
if (since !== undefined) {
request['start_date'] = this.iso8601 (since);
request['start_date'] = this.ymd (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.openapiGetWalletWithdrawList (this.extend (request, params));
const response = await this.v2PrivateGetWalletWithdrawList (this.extend (request, params));
//
// {
// "ret_code": 0,
@@ -1991,9 +2005,25 @@ module.exports = class bybit extends Exchange {
// "updated_at": "2019-06-11T02:20:24.000Z"
// }
//
// fetchDeposits ledger entries
//
// {
// "id": 234467,
// "user_id": 1,
// "coin": "BTC",
// "wallet_id": 27913,
// "type": "Realized P&L",
// "amount": "-0.00000006",
// "tx_id": "",
// "address": "BTCUSD",
// "wallet_balance": "0.03000330",
// "exec_time": "2019-12-09T00:00:25.000Z",
// "cross_seq": 0
// }
//
const currencyId = this.safeString (transaction, 'coin');
const code = this.safeCurrencyCode (currencyId, currency);
const timestamp = this.parse8601 (this.safeString (transaction, 'submited_at'));
const timestamp = this.parse8601 (this.safeString2 (transaction, 'submited_at', 'exec_time'));
const updated = this.parse8601 (this.safeString (transaction, 'updated_at'));
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const address = this.safeString (transaction, 'address');
@@ -2043,12 +2073,12 @@ module.exports = class bybit extends Exchange {
request['coin'] = currency['id'];
}
if (since !== undefined) {
request['start_date'] = this.iso8601 (since);
request['start_date'] = this.ymd (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.openapiGetWalletFundRecords (this.extend (request, params));
const response = await this.v2PrivateGetWalletFundRecords (this.extend (request, params));
//
// {
// "ret_code": 0,
@@ -2147,45 +2177,28 @@ module.exports = class bybit extends Exchange {
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'];
let request = path;
let url = this.implodeParams (this.urls['api'], { 'hostname': this.hostname });
const type = this.safeString (api, 0);
const section = this.safeString (api, 1);
let request = '/' + type + '/' + section + '/' + path;
// public v2
if (api === 'public') {
request = '/' + this.version + '/' + api + '/' + request;
if (section === 'public') {
if (Object.keys (params).length) {
request += '?' + this.rawencode (params);
}
} else if (api === 'publicLinear') {
request = '/public/linear/' + request;
} else if (type === 'public') {
if (Object.keys (params).length) {
request += '?' + this.rawencode (params);
}
} else {
this.checkRequiredCredentials ();
if (api === 'openapi') {
request = '/open-api/' + request;
} 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;
}
const timestamp = this.nonce ();
const query = this.extend (params, {
'api_key': this.apiKey,
'recv_window': this.options['recvWindow'],
'timestamp': timestamp,
});
let auth = this.rawencode (this.keysort (query));
// https://github.com/ccxt/ccxt/issues/7377
// https://github.com/ccxt/ccxt/issues/7515
// bybit encodes whole floats as integers without .0 for conditional stop-orders only
if (path.indexOf ('stop-order') >= 0) {
auth = auth.replace ('.0&', '&');
}
const auth = this.rawencode (this.keysort (query));
const signature = this.hmac (this.encode (auth), this.encode (this.secret));
if (method === 'POST') {
body = this.json (this.extend (query, {
@@ -285,7 +285,7 @@ module.exports = class bytetrade extends Exchange {
async fetchBalance (params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired (this.id + ' fetchDeposits requires this.apiKey or userid argument');
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
const request = {
@@ -580,6 +580,7 @@ module.exports = class bytetrade extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -597,7 +598,7 @@ module.exports = class bytetrade extends Exchange {
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
this.checkRequiredDependencies ();
if (this.apiKey === undefined) {
throw new ArgumentsRequired ('createOrder requires this.apiKey or userid in params');
throw new ArgumentsRequired ('createOrder() requires this.apiKey or userid in params');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -781,7 +782,7 @@ module.exports = class bytetrade extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchOrder requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchOrder() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
const request = {
@@ -799,7 +800,7 @@ module.exports = class bytetrade extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchOpenOrders requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchOpenOrders() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
const request = {
@@ -819,7 +820,7 @@ module.exports = class bytetrade extends Exchange {
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchClosedOrders requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchClosedOrders() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
let market = undefined;
@@ -839,7 +840,7 @@ module.exports = class bytetrade extends Exchange {
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchOrders requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchOrders() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
let market = undefined;
@@ -859,10 +860,10 @@ module.exports = class bytetrade extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (this.apiKey === undefined) {
throw new ArgumentsRequired ('cancelOrder requires hasAlreadyAuthenticatedSuccessfully');
throw new ArgumentsRequired ('cancelOrder() requires hasAlreadyAuthenticatedSuccessfully');
}
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -960,7 +961,7 @@ module.exports = class bytetrade extends Exchange {
async transfer (code, amount, address, message = '', params = {}) {
this.checkRequiredDependencies ();
if (this.apiKey === undefined) {
throw new ArgumentsRequired ('transfer requires this.apiKey');
throw new ArgumentsRequired ('transfer() requires this.apiKey');
}
await this.loadMarkets ();
const currency = this.currency (code);
@@ -1066,7 +1067,7 @@ module.exports = class bytetrade extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchMyTrades requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchMyTrades() requires this.apiKey or userid argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1086,7 +1087,7 @@ module.exports = class bytetrade extends Exchange {
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchDeposits requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchDeposits() requires this.apiKey or userid argument');
}
let currency = undefined;
const request = {
@@ -1109,7 +1110,7 @@ module.exports = class bytetrade extends Exchange {
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchWithdrawals requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchWithdrawals() requires this.apiKey or userid argument');
}
let currency = undefined;
const request = {
@@ -1193,7 +1194,7 @@ module.exports = class bytetrade extends Exchange {
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
if (!('userid' in params) && (this.apiKey === undefined)) {
throw new ArgumentsRequired ('fetchDepositAddress requires this.apiKey or userid argument');
throw new ArgumentsRequired ('fetchDepositAddress() requires this.apiKey or userid argument');
}
const currency = this.currency (code);
const request = {
@@ -1219,7 +1220,7 @@ module.exports = class bytetrade extends Exchange {
this.checkAddress (address);
await this.loadMarkets ();
if (this.apiKey === undefined) {
throw new ArgumentsRequired ('withdraw requires this.apiKey');
throw new ArgumentsRequired (this.id + ' withdraw() requires this.apiKey');
}
const addressResponse = await this.fetchDepositAddress (code);
const chainTypeString = this.safeString (addressResponse, 'chainType');
@@ -6,25 +6,27 @@ const huobipro = require ('./huobipro.js');
// ---------------------------------------------------------------------------
module.exports = class huobiru extends huobipro {
module.exports = class cdax extends huobipro {
describe () {
return this.deepExtend (super.describe (), {
'id': 'huobiru',
'name': 'Huobi Russia',
'id': 'cdax',
'name': 'CDAX',
'countries': [ 'RU' ],
'hostname': 'www.huobi.com.ru',
'pro': true,
'hostname': 'cdax.io',
'pro': false,
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg',
'logo': 'https://user-images.githubusercontent.com/1294454/102157692-fd406280-3e90-11eb-8d46-4511b617cd17.jpg',
'api': {
'market': 'https://{hostname}/api',
'public': 'https://{hostname}/api',
'private': 'https://{hostname}/api',
'v2Public': 'https://{hostname}/api',
'v2Private': 'https://{hostname}/api',
},
'www': 'https://www.huobi.com.ru/ru-ru',
'referral': 'https://www.huobi.com.ru/invite?invite_code=esc74',
'doc': 'https://github.com/cloudapidoc/API_Docs_en',
'fees': 'https://www.huobi.com.ru/ru-ru/about/fee/',
'www': 'https://cdax.io',
'referral': 'https://cdax.io/invite?invite_code=esc74',
'doc': 'https://github.com/cloudapidoc/API_Docs',
'fees': 'https://cdax.io/about/fee',
},
});
}
@@ -676,10 +676,9 @@ module.exports = class cex extends Exchange {
let cost = undefined;
if (market !== undefined) {
symbol = market['symbol'];
cost = this.safeFloat (order, 'ta:' + market['quote']);
if (cost === undefined) {
cost = this.safeFloat (order, 'tta:' + market['quote']);
}
const taCost = this.safeFloat (order, 'ta:' + market['quote']);
const ttaCost = this.safeFloat (order, 'tta:' + market['quote']);
cost = this.sum (taCost, ttaCost);
const baseFee = 'fa:' + market['base'];
const baseTakerFee = 'tfa:' + market['base'];
const quoteFee = 'fa:' + market['quote'];
@@ -883,6 +882,7 @@ module.exports = class cex extends Exchange {
'symbol': symbol,
'type': (price === undefined) ? 'market' : 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -918,7 +918,7 @@ module.exports = class cex extends Exchange {
await this.loadMarkets ();
const method = 'privatePostArchivedOrdersPair';
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchClosedOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchClosedOrders() requires a symbol argument');
}
const market = this.market (symbol);
const request = { 'pair': market['id'] };
@@ -1252,10 +1252,10 @@ module.exports = class cex extends Exchange {
async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
if (amount === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires a amount argument');
throw new ArgumentsRequired (this.id + ' editOrder() requires a amount argument');
}
if (price === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires a price argument');
throw new ArgumentsRequired (this.id + ' editOrder() requires a price argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -3,7 +3,7 @@
// ----------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, AuthenticationError, RateLimitExceeded } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, AuthenticationError, RateLimitExceeded, InvalidNonce } = require ('./base/errors');
// ----------------------------------------------------------------------------
@@ -123,23 +123,28 @@ module.exports = class coinbase extends Exchange {
},
},
'exceptions': {
'two_factor_required': AuthenticationError, // 402 When sending money over 2fa limit
'param_required': ExchangeError, // 400 Missing parameter
'validation_error': ExchangeError, // 400 Unable to validate POST/PUT
'invalid_request': ExchangeError, // 400 Invalid request
'personal_details_required': AuthenticationError, // 400 User’s personal detail required to complete this request
'identity_verification_required': AuthenticationError, // 400 Identity verification is required to complete this request
'jumio_verification_required': AuthenticationError, // 400 Document verification is required to complete this request
'jumio_face_match_verification_required': AuthenticationError, // 400 Document verification including face match is required to complete this request
'unverified_email': AuthenticationError, // 400 User has not verified their email
'authentication_error': AuthenticationError, // 401 Invalid auth (generic)
'invalid_token': AuthenticationError, // 401 Invalid Oauth token
'revoked_token': AuthenticationError, // 401 Revoked Oauth token
'expired_token': AuthenticationError, // 401 Expired Oauth token
'invalid_scope': AuthenticationError, // 403 User hasn’t authenticated necessary scope
'not_found': ExchangeError, // 404 Resource not found
'rate_limit_exceeded': RateLimitExceeded, // 429 Rate limit exceeded
'internal_server_error': ExchangeError, // 500 Internal server error
'exact': {
'two_factor_required': AuthenticationError, // 402 When sending money over 2fa limit
'param_required': ExchangeError, // 400 Missing parameter
'validation_error': ExchangeError, // 400 Unable to validate POST/PUT
'invalid_request': ExchangeError, // 400 Invalid request
'personal_details_required': AuthenticationError, // 400 User’s personal detail required to complete this request
'identity_verification_required': AuthenticationError, // 400 Identity verification is required to complete this request
'jumio_verification_required': AuthenticationError, // 400 Document verification is required to complete this request
'jumio_face_match_verification_required': AuthenticationError, // 400 Document verification including face match is required to complete this request
'unverified_email': AuthenticationError, // 400 User has not verified their email
'authentication_error': AuthenticationError, // 401 Invalid auth (generic)
'invalid_token': AuthenticationError, // 401 Invalid Oauth token
'revoked_token': AuthenticationError, // 401 Revoked Oauth token
'expired_token': AuthenticationError, // 401 Expired Oauth token
'invalid_scope': AuthenticationError, // 403 User hasn’t authenticated necessary scope
'not_found': ExchangeError, // 404 Resource not found
'rate_limit_exceeded': RateLimitExceeded, // 429 Rate limit exceeded
'internal_server_error': ExchangeError, // 500 Internal server error
},
'broad': {
'request timestamp expired': InvalidNonce, // {"errors":[{"id":"authentication_error","message":"request timestamp expired"}]}
},
},
'commonCurrencies': {
'CGLD': 'CELO',
@@ -172,7 +177,11 @@ module.exports = class coinbase extends Exchange {
}
async fetchAccounts (params = {}) {
const response = await this.privateGetAccounts (params);
await this.loadMarkets ();
const request = {
'limit': 100,
};
const response = await this.privateGetAccounts (this.extend (request, params));
//
// {
// "id": "XLM",
@@ -703,7 +712,10 @@ module.exports = class coinbase extends Exchange {
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetAccounts (params);
const request = {
'limit': 100,
};
const response = await this.privateGetAccounts (this.extend (request, params));
const balances = this.safeValue (response, 'data');
const accounts = this.safeValue (params, 'type', this.options['accounts']);
const result = { 'info': response };
@@ -1175,7 +1187,9 @@ module.exports = class coinbase extends Exchange {
//
let errorCode = this.safeString (response, 'error');
if (errorCode !== undefined) {
this.throwExactlyMatchedException (this.exceptions, errorCode, feedback);
const errorMessage = this.safeString (response, 'error_description');
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback);
throw new ExchangeError (feedback);
}
const errors = this.safeValue (response, 'errors');
@@ -1184,8 +1198,10 @@ module.exports = class coinbase extends Exchange {
const numErrors = errors.length;
if (numErrors > 0) {
errorCode = this.safeString (errors[0], 'id');
const errorMessage = this.safeString (errors[0], 'message');
if (errorCode !== undefined) {
this.throwExactlyMatchedException (this.exceptions, errorCode, feedback);
this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], errorMessage, feedback);
throw new ExchangeError (feedback);
}
}
@@ -345,6 +345,7 @@ module.exports = class coinbasepro extends Exchange {
}
async fetchAccounts (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetAccounts (params);
//
// [
@@ -523,12 +524,20 @@ module.exports = class coinbasepro extends Exchange {
// trade_id: 82047307,
// maker_order_id: '0f358725-2134-435e-be11-753912a326e0',
// taker_order_id: '252b7002-87a3-425c-ac73-f5b9e23f3caf',
// order_id: 'd50ec984-77a8-460a-b958-66f114b0de9b',
// side: 'sell',
// size: '0.00513192',
// price: '9314.78',
// product_id: 'BTC-USD',
// profile_id: '6244401d-c078-40d9-b305-7ad3551bc3b0',
// sequence: 12038915443,
// time: '2020-01-31T20:03:41.158814Z'
// created_at: '2014-11-07T22:19:28.578544Z',
// liquidity: 'T',
// fee: '0.00025',
// settled: true,
// usd_volume: '0.0924556000000000',
// user_id: '595eb864313c2b02ddf2937d'
// }
//
const timestamp = this.parse8601 (this.safeString2 (trade, 'time', 'created_at'));
@@ -537,10 +546,15 @@ module.exports = class coinbasepro extends Exchange {
let feeRate = undefined;
let feeCurrency = undefined;
let takerOrMaker = undefined;
let cost = undefined;
if (market !== undefined) {
const feeCurrencyId = this.safeStringLower (market, 'quoteId');
const costField = feeCurrencyId + '_value';
cost = this.safeFloat (trade, costField);
feeCurrency = market['quote'];
if ('liquidity' in trade) {
takerOrMaker = (trade['liquidity'] === 'T') ? 'taker' : 'maker';
const liquidity = this.safeString (trade, 'liquidity');
if (liquidity !== undefined) {
takerOrMaker = (liquidity === 'T') ? 'taker' : 'maker';
feeRate = market[takerOrMaker];
}
}
@@ -560,6 +574,9 @@ module.exports = class coinbasepro extends Exchange {
}
const price = this.safeFloat (trade, 'price');
const amount = this.safeFloat (trade, 'size');
if (cost === undefined) {
cost = amount * price;
}
return {
'id': id,
'order': orderId,
@@ -573,14 +590,14 @@ module.exports = class coinbasepro extends Exchange {
'price': price,
'amount': amount,
'fee': fee,
'cost': price * amount,
'cost': cost,
};
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
// as of 2018-08-23
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -600,6 +617,9 @@ module.exports = class coinbasepro extends Exchange {
const request = {
'id': market['id'], // fixes issue #2
};
if (limit !== undefined) {
request['limit'] = limit; // default 100
}
const response = await this.publicGetProductsIdTrades (this.extend (request, params));
return this.parseTrades (response, market, since, limit);
}
@@ -676,6 +696,27 @@ module.exports = class coinbasepro extends Exchange {
}
parseOrder (order, market = undefined) {
//
// createOrder
//
// {
// "id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2",
// "price": "0.10000000",
// "size": "0.01000000",
// "product_id": "BTC-USD",
// "side": "buy",
// "stp": "dc",
// "type": "limit",
// "time_in_force": "GTC",
// "post_only": false,
// "created_at": "2016-12-08T20:02:28.53864Z",
// "fill_fees": "0.0000000000000000",
// "filled_size": "0.00000000",
// "executed_value": "0.0000000000000000",
// "status": "pending",
// "settled": false
// }
//
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
const marketId = this.safeString (order, 'product_id');
market = this.safeMarket (marketId, market, '-');
@@ -706,9 +747,13 @@ module.exports = class coinbasepro extends Exchange {
const id = this.safeString (order, 'id');
const type = this.safeString (order, 'type');
const side = this.safeString (order, 'side');
const timeInForce = this.safeString (order, 'time_in_force');
const postOnly = this.safeValue (order, 'post_only');
const stopPrice = this.safeFloat (order, 'stop_price');
const clientOrderId = this.safeString (order, 'client_oid');
return {
'id': id,
'clientOrderId': undefined,
'clientOrderId': clientOrderId,
'info': order,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
@@ -716,10 +761,11 @@ module.exports = class coinbasepro extends Exchange {
'status': status,
'symbol': market['symbol'],
'type': type,
'timeInForce': undefined,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': undefined,
'stopPrice': stopPrice,
'cost': cost,
'amount': amount,
'filled': filled,
@@ -732,10 +778,18 @@ module.exports = class coinbasepro extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'id': id,
};
const response = await this.privateGetOrdersId (this.extend (request, params));
const request = {};
const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_oid');
let method = undefined;
if (clientOrderId === undefined) {
method = 'privateGetOrdersId';
request['id'] = id;
} else {
method = 'privateGetOrdersClientClientOid';
request['client_oid'] = clientOrderId;
params = this.omit (params, [ 'clientOrderId', 'client_oid' ]);
}
const response = await this[method] (this.extend (request, params));
return this.parseOrder (response);
}
@@ -753,17 +807,10 @@ module.exports = class coinbasepro extends Exchange {
}
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'status': 'all',
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['product_id'] = market['id'];
}
const response = await this.privateGetOrders (this.extend (request, params));
return this.parseOrders (response, market, since, limit);
return await this.fetchOpenOrders (symbol, since, limit, this.extend (request, params));
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
@@ -774,47 +821,131 @@ module.exports = class coinbasepro extends Exchange {
market = this.market (symbol);
request['product_id'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 100
}
const response = await this.privateGetOrders (this.extend (request, params));
return this.parseOrders (response, market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'status': 'done',
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['product_id'] = market['id'];
}
const response = await this.privateGetOrders (this.extend (request, params));
return this.parseOrders (response, market, since, limit);
return await this.fetchOpenOrders (symbol, since, limit, this.extend (request, params));
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
// let oid = this.nonce ().toString ();
const market = this.market (symbol);
const request = {
'product_id': this.marketId (symbol),
'side': side,
'size': this.amountToPrecision (symbol, amount),
// common params --------------------------------------------------
// 'client_oid': clientOrderId,
'type': type,
'side': side,
'product_id': market['id'],
// 'size': this.amountToPrecision (symbol, amount),
// 'stp': 'dc', // self-trade prevention, dc = decrease and cancel, co = cancel oldest, cn = cancel newest, cb = cancel both
// 'stop': 'loss', // "loss" = stop loss below price, "entry" = take profit above price
// 'stop_price': this.priceToPrecision (symbol, price),
// limit order params ---------------------------------------------
// 'price': this.priceToPrecision (symbol, price),
// 'size': this.amountToPrecision (symbol, amount),
// 'time_in_force': 'GTC', // GTC, GTT, IOC, or FOK
// 'cancel_after' [optional]* min, hour, day, requires time_in_force to be GTT
// 'post_only': false, // invalid when time_in_force is IOC or FOK
// market order params --------------------------------------------
// 'size': this.amountToPrecision (symbol, amount),
// 'funds': this.costToPrecision (symbol, amount),
};
const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_oid');
if (clientOrderId !== undefined) {
request['client_oid'] = clientOrderId;
params = this.omit (params, [ 'clientOrderId', 'client_oid' ]);
}
const stopPrice = this.safeFloat2 (params, 'stopPrice', 'stop_price');
if (stopPrice !== undefined) {
request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
params = this.omit (params, [ 'stopPrice', 'stop_price' ]);
}
const timeInForce = this.safeString2 (params, 'timeInForce', 'time_in_force');
if (timeInForce !== undefined) {
request['time_in_force'] = timeInForce;
params = this.omit (params, [ 'timeInForce', 'time_in_force' ]);
}
if (type === 'limit') {
request['price'] = this.priceToPrecision (symbol, price);
request['size'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market') {
let cost = this.safeFloat2 (params, 'cost', 'funds');
if (cost === undefined) {
if (price !== undefined) {
cost = amount * price;
}
} else {
params = this.omit (params, [ 'cost', 'funds' ]);
}
if (cost !== undefined) {
request['funds'] = this.costToPrecision (symbol, cost);
} else {
request['size'] = this.amountToPrecision (symbol, amount);
}
}
const response = await this.privatePostOrders (this.extend (request, params));
return this.parseOrder (response);
//
// {
// "id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2",
// "price": "0.10000000",
// "size": "0.01000000",
// "product_id": "BTC-USD",
// "side": "buy",
// "stp": "dc",
// "type": "limit",
// "time_in_force": "GTC",
// "post_only": false,
// "created_at": "2016-12-08T20:02:28.53864Z",
// "fill_fees": "0.0000000000000000",
// "filled_size": "0.00000000",
// "executed_value": "0.0000000000000000",
// "status": "pending",
// "settled": false
// }
//
return this.parseOrder (response, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
return await this.privateDeleteOrdersId ({ 'id': id });
const request = {
// 'product_id': market['id'], // the request will be more performant if you include it
};
const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_oid');
let method = undefined;
if (clientOrderId === undefined) {
method = 'privateDeleteOrdersId';
request['id'] = id;
} else {
method = 'privateDeleteOrdersClientClientOid';
request['client_oid'] = clientOrderId;
params = this.omit (params, [ 'clientOrderId', 'client_oid' ]);
}
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['product_id'] = market['symbol']; // the request will be more performant if you include it
}
return await this[method] (this.extend (request, params));
}
async cancelAllOrders (symbol = undefined, params = {}) {
return await this.privateDeleteOrders (params);
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['product_id'] = market['symbol']; // the request will be more performant if you include it
}
return await this.privateDeleteOrders (this.extend (request, params));
}
calculateFee (symbol, type, side, amount, price, takerOrMaker = 'taker', params = {}) {
@@ -880,6 +1011,9 @@ module.exports = class coinbasepro extends Exchange {
} else {
method += 'Crypto';
request['crypto_address'] = address;
if (tag !== undefined) {
request['destination_tag'] = tag;
}
}
const response = await this[method] (this.extend (request, params));
if (!response) {
@@ -965,7 +1099,7 @@ module.exports = class coinbasepro extends Exchange {
const currencyId = this.safeString (transaction, 'currency');
const code = this.safeCurrencyCode (currencyId, currency);
const status = this.parseTransactionStatus (transaction);
const amount = this.safeFloat (transaction, 'amount');
let amount = this.safeFloat (transaction, 'amount');
let type = this.safeString (transaction, 'type');
let address = this.safeString (details, 'crypto_address');
const tag = this.safeString (details, 'destination_tag');
@@ -976,9 +1110,12 @@ module.exports = class coinbasepro extends Exchange {
address = this.safeString (details, 'sent_to_address', address);
const feeCost = this.safeFloat (details, 'fee');
if (feeCost !== undefined) {
if (amount !== undefined) {
amount -= feeCost;
}
fee = {
'cost': feeCost,
'code': code,
'currency': code,
};
}
}
@@ -81,10 +81,11 @@ module.exports = class coincheck extends Exchange {
'markets': {
'BTC/JPY': { 'id': 'btc_jpy', 'symbol': 'BTC/JPY', 'base': 'BTC', 'quote': 'JPY', 'baseId': 'btc', 'quoteId': 'jpy' }, // the only real pair
// 'ETH/JPY': { 'id': 'eth_jpy', 'symbol': 'ETH/JPY', 'base': 'ETH', 'quote': 'JPY', 'baseId': 'eth', 'quoteId': 'jpy' },
// 'ETC/JPY': { 'id': 'etc_jpy', 'symbol': 'ETC/JPY', 'base': 'ETC', 'quote': 'JPY', 'baseId': 'etc', 'quoteId': 'jpy' },
'ETC/JPY': { 'id': 'etc_jpy', 'symbol': 'ETC/JPY', 'base': 'ETC', 'quote': 'JPY', 'baseId': 'etc', 'quoteId': 'jpy' },
// 'DAO/JPY': { 'id': 'dao_jpy', 'symbol': 'DAO/JPY', 'base': 'DAO', 'quote': 'JPY', 'baseId': 'dao', 'quoteId': 'jpy' },
// 'LSK/JPY': { 'id': 'lsk_jpy', 'symbol': 'LSK/JPY', 'base': 'LSK', 'quote': 'JPY', 'baseId': 'lsk', 'quoteId': 'jpy' },
// 'FCT/JPY': { 'id': 'fct_jpy', 'symbol': 'FCT/JPY', 'base': 'FCT', 'quote': 'JPY', 'baseId': 'fct', 'quoteId': 'jpy' },
'FCT/JPY': { 'id': 'fct_jpy', 'symbol': 'FCT/JPY', 'base': 'FCT', 'quote': 'JPY', 'baseId': 'fct', 'quoteId': 'jpy' },
'MONA/JPY': { 'id': 'mona_jpy', 'symbol': 'MONA/JPY', 'base': 'MONA', 'quote': 'JPY', 'baseId': 'mona', 'quoteId': 'jpy' },
// 'XMR/JPY': { 'id': 'xmr_jpy', 'symbol': 'XMR/JPY', 'base': 'XMR', 'quote': 'JPY', 'baseId': 'xmr', 'quoteId': 'jpy' },
// 'REP/JPY': { 'id': 'rep_jpy', 'symbol': 'REP/JPY', 'base': 'REP', 'quote': 'JPY', 'baseId': 'rep', 'quoteId': 'jpy' },
// 'XRP/JPY': { 'id': 'xrp_jpy', 'symbol': 'XRP/JPY', 'base': 'XRP', 'quote': 'JPY', 'baseId': 'xrp', 'quoteId': 'jpy' },
@@ -93,7 +94,7 @@ module.exports = class coincheck extends Exchange {
// 'LTC/JPY': { 'id': 'ltc_jpy', 'symbol': 'LTC/JPY', 'base': 'LTC', 'quote': 'JPY', 'baseId': 'ltc', 'quoteId': 'jpy' },
// 'DASH/JPY': { 'id': 'dash_jpy', 'symbol': 'DASH/JPY', 'base': 'DASH', 'quote': 'JPY', 'baseId': 'dash', 'quoteId': 'jpy' },
// 'ETH/BTC': { 'id': 'eth_btc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC', 'baseId': 'eth', 'quoteId': 'btc' },
// 'ETC/BTC': { 'id': 'etc_btc', 'symbol': 'ETC/BTC', 'base': 'ETC', 'quote': 'BTC', 'baseId': 'etc', 'quoteId': 'btc' },
'ETC/BTC': { 'id': 'etc_btc', 'symbol': 'ETC/BTC', 'base': 'ETC', 'quote': 'BTC', 'baseId': 'etc', 'quoteId': 'btc' },
// 'LSK/BTC': { 'id': 'lsk_btc', 'symbol': 'LSK/BTC', 'base': 'LSK', 'quote': 'BTC', 'baseId': 'lsk', 'quoteId': 'btc' },
// 'FCT/BTC': { 'id': 'fct_btc', 'symbol': 'FCT/BTC', 'base': 'FCT', 'quote': 'BTC', 'baseId': 'fct', 'quoteId': 'btc' },
// 'XMR/BTC': { 'id': 'xmr_btc', 'symbol': 'XMR/BTC', 'base': 'XMR', 'quote': 'BTC', 'baseId': 'xmr', 'quoteId': 'btc' },
@@ -197,6 +198,7 @@ module.exports = class coincheck extends Exchange {
'side': side,
'type': undefined,
'timeInForce': undefined,
'postOnly': undefined,
'status': status,
'symbol': symbol,
'price': price,
@@ -210,11 +212,12 @@ module.exports = class coincheck extends Exchange {
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
if (symbol !== 'BTC/JPY') {
throw new BadSymbol (this.id + ' fetchOrderBook () supports BTC/JPY only');
}
await this.loadMarkets ();
const response = await this.publicGetOrderBooks (params);
const market = this.market (symbol);
const request = {
'pair': market['id'],
};
const response = await this.publicGetOrderBooks (this.extend (request, params));
return this.parseOrderBook (response);
}
@@ -223,7 +226,11 @@ module.exports = class coincheck extends Exchange {
throw new BadSymbol (this.id + ' fetchTicker () supports BTC/JPY only');
}
await this.loadMarkets ();
const ticker = await this.publicGetTicker (params);
const market = this.market (symbol);
const request = {
'pair': market['id'],
};
const ticker = await this.publicGetTicker (this.extend (request, params));
const timestamp = this.safeTimestamp (ticker, 'timestamp');
const last = this.safeFloat (ticker, 'last');
return {
@@ -337,9 +344,6 @@ module.exports = class coincheck extends Exchange {
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
if (symbol !== 'BTC/JPY') {
throw new BadSymbol (this.id + ' fetchTrades () supports BTC/JPY only');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
@@ -154,6 +154,7 @@ module.exports = class coinegg extends Exchange {
},
'commonCurrencies': {
'JBC': 'JubaoCoin',
'SBTC': 'Super Bitcoin',
},
});
}
@@ -367,6 +368,7 @@ module.exports = class coinegg extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -92,6 +92,8 @@ module.exports = class coinex extends Exchange {
'order/status',
'order/status/batch',
'order/user/deals',
'sub_account/balance',
'sub_account/transfer/history',
],
'post': [
'balance/coin/withdraw',
@@ -499,6 +501,7 @@ module.exports = class coinex extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -562,7 +565,7 @@ module.exports = class coinex extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -759,7 +762,7 @@ module.exports = class coinex extends Exchange {
const timestamp = this.safeTimestamp (transaction, 'create_time');
const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit';
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const amount = this.safeFloat (transaction, 'amount');
let amount = this.safeFloat (transaction, 'amount');
let feeCost = this.safeFloat (transaction, 'tx_fee');
if (type === 'deposit') {
feeCost = 0;
@@ -768,6 +771,10 @@ module.exports = class coinex extends Exchange {
'cost': feeCost,
'currency': code,
};
// https://github.com/ccxt/ccxt/issues/8321
if (amount !== undefined) {
amount = amount - feeCost;
}
return {
'info': transaction,
'id': id,
@@ -787,7 +794,7 @@ module.exports = class coinex extends Exchange {
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals requires a currency code argument');
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
@@ -845,7 +852,7 @@ module.exports = class coinex extends Exchange {
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits requires a currency code argument');
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
@@ -910,7 +917,7 @@ module.exports = class coinex extends Exchange {
'tonce': nonce.toString (),
}, query);
query = this.keysort (query);
const urlencoded = this.urlencode (query);
const urlencoded = this.rawencode (query);
const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret));
headers = {
'Authorization': signature.toUpperCase (),
@@ -217,7 +217,7 @@ module.exports = class coinfalcon extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -320,6 +320,7 @@ module.exports = class coinfalcon extends Exchange {
type = type[0];
}
const side = this.safeString (order, 'order_type');
const postOnly = this.safeValue (order, 'post_only');
return {
'id': this.safeString (order, 'id'),
'clientOrderId': undefined,
@@ -329,6 +330,7 @@ module.exports = class coinfalcon extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -27,7 +27,7 @@ module.exports = class coinfloor extends Exchange {
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/87153925-ef265e80-c2c0-11ea-91b5-020c804b90e0.jpg',
'api': 'https://webapi.coinfloor.co.uk/bist',
'api': 'https://webapi.coinfloor.co.uk/v2/bist',
'www': 'https://www.coinfloor.co.uk',
'doc': [
'https://github.com/coinfloor/api',
@@ -90,7 +90,7 @@ module.exports = class coinfloor extends Exchange {
market = this.markets_by_id[marketId];
}
if (market === undefined) {
throw new ArgumentsRequired (this.id + ' fetchBalance requires a symbol param');
throw new ArgumentsRequired (this.id + ' fetchBalance() requires a symbol param');
}
const request = {
'id': market['id'],
@@ -223,7 +223,7 @@ module.exports = class coinfloor extends Exchange {
if (code !== undefined) {
market = this.market (code);
if (market === undefined) {
throw new ArgumentsRequired (this.id + ' fetchTransactions requires a code argument (a market symbol)');
throw new ArgumentsRequired (this.id + ' fetchTransactions() requires a code argument (a market symbol)');
}
}
const request = {
@@ -431,7 +431,7 @@ module.exports = class coinfloor extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -480,6 +480,7 @@ module.exports = class coinfloor extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -495,7 +496,7 @@ module.exports = class coinfloor extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol param');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol param');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -150,6 +150,8 @@ module.exports = class coinmarketcap extends Exchange {
// https://github.com/ccxt/ccxt/issues/6081
// https://github.com/ccxt/ccxt/issues/3365
// https://github.com/ccxt/ccxt/issues/2873
'SBTCT': 'SiamBitcoin', // conflict with sBTC
'Super Bitcoin': 'Super Bitcoin', // conflict with sBTC
'TerraCredit': 'TerraCredit', // conflict with CREDIT (PROXI)
'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol)
'UNI COIN': 'UNI COIN', // conflict with UNI (Uniswap)
@@ -529,7 +529,7 @@ module.exports = class coinmate extends Exchange {
async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -641,6 +641,7 @@ module.exports = class coinmate extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -373,7 +373,7 @@ module.exports = class coinone extends Exchange {
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -518,6 +518,7 @@ module.exports = class coinone extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -567,7 +568,7 @@ module.exports = class coinone extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -602,14 +603,14 @@ module.exports = class coinone extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
// eslint-disable-next-line quotes
throw new ArgumentsRequired (this.id + " cancelOrder requires a symbol argument. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder.");
throw new ArgumentsRequired (this.id + " cancelOrder() requires a symbol argument. To cancel the order, pass a symbol argument and {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument of cancelOrder.");
}
const price = this.safeFloat (params, 'price');
const qty = this.safeFloat (params, 'qty');
const isAsk = this.safeInteger (params, 'is_ask');
if ((price === undefined) || (qty === undefined) || (isAsk === undefined)) {
// eslint-disable-next-line quotes
throw new ArgumentsRequired (this.id + " cancelOrder requires {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument.");
throw new ArgumentsRequired (this.id + " cancelOrder() requires {'price': 12345, 'qty': 1.2345, 'is_ask': 0} in the params argument.");
}
await this.loadMarkets ();
const request = {
@@ -3,7 +3,7 @@
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError, NotSupported } = require ('./base/errors');
const { ExchangeError, ArgumentsRequired, AuthenticationError } = require ('./base/errors');
// ---------------------------------------------------------------------------
@@ -70,27 +70,72 @@ module.exports = class coinspot extends Exchange {
},
'markets': {
'BTC/AUD': { 'id': 'btc', 'symbol': 'BTC/AUD', 'base': 'BTC', 'quote': 'AUD', 'baseId': 'btc', 'quoteId': 'aud' },
'ETH/AUD': { 'id': 'eth', 'symbol': 'ETH/AUD', 'base': 'ETH', 'quote': 'AUD', 'baseId': 'eth', 'quoteId': 'aud' },
'XRP/AUD': { 'id': 'xrp', 'symbol': 'XRP/AUD', 'base': 'XRP', 'quote': 'AUD', 'baseId': 'xrp', 'quoteId': 'aud' },
'LTC/AUD': { 'id': 'ltc', 'symbol': 'LTC/AUD', 'base': 'LTC', 'quote': 'AUD', 'baseId': 'ltc', 'quoteId': 'aud' },
'DOGE/AUD': { 'id': 'doge', 'symbol': 'DOGE/AUD', 'base': 'DOGE', 'quote': 'AUD', 'baseId': 'doge', 'quoteId': 'aud' },
'RFOX/AUD': { 'id': 'rfox', 'symbol': 'RFOX/AUD', 'base': 'RFOX', 'quote': 'AUD', 'baseId': 'rfox', 'quoteId': 'aud' },
'POWR/AUD': { 'id': 'powr', 'symbol': 'POWR/AUD', 'base': 'POWR', 'quote': 'AUD', 'baseId': 'powr', 'quoteId': 'aud' },
'NEO/AUD': { 'id': 'neo', 'symbol': 'NEO/AUD', 'base': 'NEO', 'quote': 'AUD', 'baseId': 'neo', 'quoteId': 'aud' },
'TRX/AUD': { 'id': 'trx', 'symbol': 'TRX/AUD', 'base': 'TRX', 'quote': 'AUD', 'baseId': 'trx', 'quoteId': 'aud' },
'EOS/AUD': { 'id': 'eos', 'symbol': 'EOS/AUD', 'base': 'EOS', 'quote': 'AUD', 'baseId': 'eos', 'quoteId': 'aud' },
'XLM/AUD': { 'id': 'xlm', 'symbol': 'XLM/AUD', 'base': 'XLM', 'quote': 'AUD', 'baseId': 'xlm', 'quoteId': 'aud' },
'RHOC/AUD': { 'id': 'rhoc', 'symbol': 'RHOC/AUD', 'base': 'RHOC', 'quote': 'AUD', 'baseId': 'rhoc', 'quoteId': 'aud' },
'GAS/AUD': { 'id': 'gas', 'symbol': 'GAS/AUD', 'base': 'GAS', 'quote': 'AUD', 'baseId': 'gas', 'quoteId': 'aud' },
},
'commonCurrencies': {
'DRK': 'DASH',
},
'options': {
'fetchBalance': 'private_post_my_balances',
},
});
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostMyBalances (params);
const method = this.safeString (this.options, 'fetchBalance', 'private_post_my_balances');
const response = await this[method] (params);
//
// read-write api keys
//
// ...
//
// read-only api keys
//
// {
// "status":"ok",
// "balances":[
// {
// "LTC":{"balance":0.1,"audbalance":16.59,"rate":165.95}
// }
// ]
// }
//
const result = { 'info': response };
const balances = this.safeValue (response, 'balance', {});
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['total'] = this.safeFloat (balances, currencyId);
result[code] = account;
const balances = this.safeValue2 (response, 'balance', 'balances');
if (Array.isArray (balances)) {
for (let i = 0; i < balances.length; i++) {
const currencies = balances[i];
const currencyIds = Object.keys (currencies);
for (let j = 0; j < currencyIds.length; j++) {
const currencyId = currencyIds[j];
const balance = currencies[currencyId];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['total'] = this.safeFloat (balance, 'balance');
result[code] = account;
}
}
} else {
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['total'] = this.safeFloat (balances, currencyId);
result[code] = account;
}
}
return this.parseBalance (result);
}
@@ -210,9 +255,16 @@ module.exports = class coinspot extends Exchange {
}
async cancelOrder (id, symbol = undefined, params = {}) {
throw new NotSupported (this.id + ' cancelOrder () is not fully implemented yet');
// let method = 'privatePostMyBuy';
// return await this[method] ({ 'id': id });
const side = this.safeString (params, 'side');
if (side !== 'buy' && side !== 'sell') {
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a side parameter, "buy" or "sell"');
}
params = this.omit (params, 'side');
const method = 'privatePostMy' + this.capitalize (side) + 'Cancel';
const request = {
'id': id,
};
return await this[method] (this.extend (request, params));
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
@@ -133,6 +133,7 @@ module.exports = class crex24 extends Exchange {
'GHOST': 'GHOSTPRISM',
'IQ': 'IQ.Cash',
'PUT': 'PutinCoin',
'SBTC': 'SBTCT', // SiamBitcoin
'UNI': 'Universe',
'YOYO': 'YOYOW',
},
@@ -158,6 +159,7 @@ module.exports = class crex24 extends Exchange {
"Parameter 'instrument' contains invalid value.": BadSymbol,
},
'broad': {
'try again later': ExchangeNotAvailable, // {"errorDescription":"Failed to process the request. Please, try again later."}
'API Key': AuthenticationError, // "API Key '9edc48de-d5b0-4248-8e7e-f59ffcd1c7f1' doesn't exist."
'Insufficient funds': InsufficientFunds, // "Insufficient funds: new order requires 10 ETH which is more than the available balance."
'has been delisted.': BadSymbol, // {"errorDescription":"Instrument '$PAC-BTC' has been delisted."}
@@ -757,14 +759,14 @@ module.exports = class crex24 extends Exchange {
}
if (priceIsRequired) {
if (price === undefined) {
throw new InvalidOrder (this.id + ' createOrder method requires a price argument for a ' + type + ' order');
throw new InvalidOrder (this.id + ' createOrder() requires a price argument for a ' + type + ' order');
}
request['price'] = this.priceToPrecision (symbol, price);
}
if (stopPriceIsRequired) {
const stopPrice = this.safeFloat (params, 'stopPrice');
if (stopPrice === undefined) {
throw new InvalidOrder (this.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order');
throw new InvalidOrder (this.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order');
} else {
request['stopPrice'] = this.priceToPrecision (symbol, stopPrice);
}
@@ -916,7 +916,7 @@ module.exports = class currencycom extends Exchange {
if (market['margin']) {
accountId = this.safeInteger (params, 'accountId');
if (accountId === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires an accountId parameter for ' + market['type'] + ' market ' + symbol);
throw new ArgumentsRequired (this.id + ' createOrder() requires an accountId parameter for ' + market['type'] + ' market ' + symbol);
}
}
const uppercaseType = type.toUpperCase ();
@@ -977,7 +977,7 @@ module.exports = class currencycom extends Exchange {
const symbols = this.symbols;
const numSymbols = symbols.length;
const fetchOpenOrdersRateLimit = parseInt (numSymbols / 2);
throw new ExchangeError (this.id + ' fetchOpenOrders WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.');
throw new ExchangeError (this.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + fetchOpenOrdersRateLimit.toString () + ' seconds. Do not call this method frequently to avoid ban. Set ' + this.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = false to suppress this warning message.');
}
const response = await this.privateGetOpenOrders (this.extend (request, params));
return this.parseOrders (response, market, since, limit);
@@ -985,7 +985,7 @@ module.exports = class currencycom extends Exchange {
async cancelOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder requires a symbol argument');
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -1020,7 +1020,7 @@ module.exports = class currencycom extends Exchange {
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
File diff suppressed because it is too large. Load diff
@@ -778,7 +778,7 @@ module.exports = class deribit extends Exchange {
const now = this.milliseconds ();
if (since === undefined) {
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument');
throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument or a limit argument');
} else {
request['start_timestamp'] = now - (limit - 1) * duration * 1000;
request['end_timestamp'] = now;
@@ -1011,7 +1011,7 @@ module.exports = class deribit extends Exchange {
'cancelled': 'canceled',
'filled': 'closed',
'rejected': 'rejected',
// 'untriggered': 'open',
'untriggered': 'open',
};
return this.safeString (statuses, status, status);
}
@@ -1096,7 +1096,8 @@ module.exports = class deribit extends Exchange {
trades = this.parseTrades (trades, market);
}
const timeInForce = this.parseTimeInForce (this.safeString (order, 'time_in_force'));
const stopPrice = undefined;
const stopPrice = this.safeValue (order, 'stop_price');
const postOnly = this.safeValue (order, 'post_only');
return {
'info': order,
'id': id,
@@ -1107,6 +1108,7 @@ module.exports = class deribit extends Exchange {
'symbol': market['symbol'],
'type': type,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
@@ -1191,13 +1193,13 @@ module.exports = class deribit extends Exchange {
if (price !== undefined) {
request['price'] = this.priceToPrecision (symbol, price);
} else {
throw new ArgumentsRequired (this.id + ' createOrder requires a price argument for a ' + type + ' order');
throw new ArgumentsRequired (this.id + ' createOrder() requires a price argument for a ' + type + ' order');
}
}
if (stopPriceIsRequired) {
const stopPrice = this.safeFloat2 (params, 'stop_price', 'stopPrice');
if (stopPrice === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires a stop_price or stopPrice param for a ' + type + ' order');
throw new ArgumentsRequired (this.id + ' createOrder() requires a stop_price or stopPrice param for a ' + type + ' order');
} else {
request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
}
@@ -1266,10 +1268,10 @@ module.exports = class deribit extends Exchange {
async editOrder (id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
if (amount === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires an amount argument');
throw new ArgumentsRequired (this.id + ' editOrder() requires an amount argument');
}
if (price === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder requires a price argument');
throw new ArgumentsRequired (this.id + ' editOrder() requires a price argument');
}
await this.loadMarkets ();
const request = {
@@ -1631,7 +1633,7 @@ module.exports = class deribit extends Exchange {
const request = {
'instrument_name': market['id'],
};
const response = await this.privateGetPosition (this.extend (request, params));
const response = await this.privateGetGetPosition (this.extend (request, params));
//
// {
// "jsonrpc": "2.0",
@@ -1670,7 +1672,7 @@ module.exports = class deribit extends Exchange {
const request = {
'currency': currency['id'],
};
const response = await this.privateGetPositions (this.extend (request, params));
const response = await this.privateGetGetPositions (this.extend (request, params));
//
// {
// "jsonrpc": "2.0",
@@ -502,7 +502,7 @@ module.exports = class digifinex extends Exchange {
async fetchTickers (symbols = undefined, params = {}) {
const apiKey = this.safeValue (params, 'apiKey', this.apiKey);
if (!apiKey) {
throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter');
throw new ArgumentsRequired (this.id + ' fetchTickers() is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter');
}
await this.loadMarkets ();
const request = {
@@ -548,7 +548,7 @@ module.exports = class digifinex extends Exchange {
async fetchTicker (symbol, params = {}) {
const apiKey = this.safeValue (params, 'apiKey', this.apiKey);
if (!apiKey) {
throw new ArgumentsRequired (this.id + ' fetchTicker is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter');
throw new ArgumentsRequired (this.id + ' fetchTicker() is a private v2 endpoint that requires an `exchange.apiKey` credential or an `apiKey` extra parameter');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -994,6 +994,7 @@ module.exports = class digifinex extends Exchange {
'symbol': symbol,
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -1284,7 +1285,7 @@ module.exports = class digifinex extends Exchange {
const address = this.safeString (depositAddress, 'address');
const tag = this.safeString (depositAddress, 'addressTag');
const currencyId = this.safeStringUpper (depositAddress, 'currency');
const code = this.safeCurrencCode (currencyId);
const code = this.safeCurrencyCode (currencyId);
return {
'info': depositAddress,
'code': code,
@@ -653,7 +653,7 @@ module.exports = class dsx extends Exchange {
await this.loadMarkets ();
const market = this.market (symbol);
if (type === 'market' && price === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder requires a price argument even for market orders, that is the worst price that you agree to fill your order for');
throw new ArgumentsRequired (this.id + ' createOrder() requires a price argument even for market orders, that is the worst price that you agree to fill your order for');
}
const request = {
'pair': market['id'],
@@ -840,6 +840,7 @@ module.exports = class dsx extends Exchange {
'lastTradeTimestamp': lastTradeTimestamp,
'type': orderType,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
File diff suppressed because it is too large. Load diff
@@ -605,7 +605,7 @@ module.exports = class eterbase extends Exchange {
request['start'] = now - duration * limit * 1000;
request['end'] = now;
} else {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument, or a limit argument, or both');
throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires a since argument, or a limit argument, or both');
}
await this.loadMarkets ();
const market = this.market (symbol);
@@ -807,6 +807,7 @@ module.exports = class eterbase extends Exchange {
}
const timeInForce = this.safeString (order, 'timeInForce');
const stopPrice = this.safeFloat (order, 'stopPrice');
const postOnly = this.safeValue (order, 'postOnly');
return {
'info': order,
'id': id,
@@ -816,8 +817,9 @@ module.exports = class eterbase extends Exchange {
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'side': side,
'timeInForce': timeInForce,
'postOnly': postOnly,
'side': side,
'price': price,
'stopPrice': stopPrice,
'amount': amount,
@@ -702,7 +702,7 @@ module.exports = class exmo extends Exchange {
const now = this.milliseconds ();
if (since === undefined) {
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV requires a since argument or a limit argument');
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');
@@ -1318,6 +1318,7 @@ module.exports = class exmo extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -1436,12 +1437,21 @@ module.exports = class exmo extends Exchange {
const type = this.safeString (transaction, 'type');
const currencyId = this.safeString (transaction, 'curr');
const code = this.safeCurrencyCode (currencyId, currency);
let address = this.safeString (transaction, 'account');
if (address !== undefined) {
const parts = address.split (':');
const numParts = parts.length;
if (numParts === 2) {
address = parts[1].replace (' ', '');
let address = undefined;
const tag = undefined;
let comment = undefined;
const account = this.safeString (transaction, 'account');
if (type === 'deposit') {
comment = account;
} else if (type === 'withdrawal') {
address = account;
if (address !== undefined) {
const parts = address.split (':');
const numParts = parts.length;
if (numParts === 2) {
address = this.safeString (parts, 1);
address = address.replace (' ', '');
}
}
}
let fee = undefined;
@@ -1469,16 +1479,21 @@ module.exports = class exmo extends Exchange {
return {
'info': transaction,
'id': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'currency': code,
'amount': amount,
'address': address,
'tag': undefined, // refix it properly
'addressTo': address,
'addressFrom': undefined,
'tag': tag,
'tagTo': tag,
'tagFrom': undefined,
'status': status,
'type': type,
'updated': undefined,
'comment': comment,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'fee': fee,
};
}
@@ -326,6 +326,7 @@ module.exports = class exx extends Exchange {
'symbol': symbol,
'type': 'limit',
'timeInForce': undefined,
'postOnly': undefined,
'side': order['type'],
'price': price,
'stopPrice': undefined,
@@ -387,7 +388,7 @@ module.exports = class exx extends Exchange {
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders requires a symbol argument');
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
const market = this.market (symbol);
const request = {
@@ -592,6 +592,7 @@ module.exports = class fcoin extends Exchange {
'symbol': symbol,
'type': orderType,
'timeInForce': undefined,
'postOnly': undefined,
'side': side,
'price': price,
'stopPrice': undefined,
@@ -246,7 +246,7 @@ module.exports = class flowbtc extends Exchange {
};
return await this.privatePostCancelOrder (this.extend (request, params));
}
throw new ExchangeError (this.id + ' requires `ins` symbol parameter for cancelling an order');
throw new ExchangeError (this.id + ' cancelOrder() requires an `ins` symbol parameter for cancelling an order');
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
Loaded 100 of 284 files, more files were not shown because too many files have changed in this diff. Show more