├── .github ├── ISSUE_TEMPLATE └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── .husky └── pre-commit ├── .jsdoc.json ├── .travis.yml ├── CHANGELOG ├── DOCUMENTATION.md ├── LICENSE.md ├── README.md ├── docs ├── RESTv1.html ├── RESTv2.html ├── fonts │ ├── Montserrat │ │ ├── Montserrat-Bold.eot │ │ ├── Montserrat-Bold.ttf │ │ ├── Montserrat-Bold.woff │ │ ├── Montserrat-Bold.woff2 │ │ ├── Montserrat-Regular.eot │ │ ├── Montserrat-Regular.ttf │ │ ├── Montserrat-Regular.woff │ │ └── Montserrat-Regular.woff2 │ └── Source-Sans-Pro │ │ ├── sourcesanspro-light-webfont.eot │ │ ├── sourcesanspro-light-webfont.svg │ │ ├── sourcesanspro-light-webfont.ttf │ │ ├── sourcesanspro-light-webfont.woff │ │ ├── sourcesanspro-light-webfont.woff2 │ │ ├── sourcesanspro-regular-webfont.eot │ │ ├── sourcesanspro-regular-webfont.svg │ │ ├── sourcesanspro-regular-webfont.ttf │ │ ├── sourcesanspro-regular-webfont.woff │ │ └── sourcesanspro-regular-webfont.woff2 ├── global.html ├── index.html ├── rest1.js.html ├── rest2.js.html ├── scripts │ ├── collapse.js │ ├── commonNav.js │ ├── linenumber.js │ ├── nav.js │ ├── polyfill.js │ ├── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js │ └── search.js └── styles │ ├── jsdoc.css │ └── prettify.css ├── examples ├── cancel_order_multi.js ├── candles.js ├── order_multi_op.js ├── pay_invoices.js └── trades.js ├── index.js ├── lib ├── rest1.js └── rest2.js ├── package.json └── test └── lib ├── rest-1-integration.js ├── rest-2-integration.js ├── rest-2-issue-80-argument-length.js ├── rest-2-smoke-test.js ├── rest-2-unit.js └── rest1.js /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | #### Issue type 2 | - [ ] bug 3 | - [ ] missing functionality 4 | - [ ] performance 5 | - [ ] feature request 6 | 7 | #### Brief description 8 | 9 | #### Steps to reproduce 10 | - 11 | 12 | ##### Additional Notes: 13 | - 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | ### Description: 2 | ... 3 | 4 | ### Breaking changes: 5 | - [ ] 6 | 7 | ### New features: 8 | - [ ] 9 | 10 | ### Fixes: 11 | - [ ] 12 | 13 | ### PR status: 14 | - [ ] Version bumped 15 | - [ ] Change-log updated 16 | - [ ] Tests added or updated 17 | - [ ] Documentation updated 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | /test/test_api_keys.json 4 | /build 5 | npm-debug.log 6 | source/images 7 | .DS_Store 8 | todo 9 | package-lock.json 10 | .env 11 | /dist 12 | tags 13 | .nyc_output 14 | coverage 15 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # only run on added/modified files 4 | git diff --name-only --cached --diff-filter=AM -- "*.js" | \ 5 | while read -r file 6 | do 7 | if [ -f "$file" ]; then # don't run on deleted files 8 | npx standard "$file" 9 | fi 10 | done 11 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": false, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["lib", "DOCUMENTATION.md"], 8 | "includePattern": ".js$", 9 | "excludePattern": "(node_modules/|docs)" 10 | }, 11 | "plugins": [ 12 | "plugins/markdown" 13 | ], 14 | "templates": { 15 | "cleverLinks": false, 16 | "monospaceLinks": true, 17 | "smallSourceLink": true, 18 | "hideAuthor": true 19 | }, 20 | "opts": { 21 | "destination": "./docs/", 22 | "encoding": "utf8", 23 | "private": false, 24 | "recurse": true, 25 | "template": "./node_modules/docdash" 26 | }, 27 | "package": "", 28 | "docdash": { 29 | "static": true, 30 | "sort": true, 31 | "search": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: 5 | - "stable" 6 | 7 | install: 8 | - npm install 9 | 10 | script: 11 | - npm run lint 12 | - npm run unit 13 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # 6.0.0 2 | - RESTv2: remove pulse endpoints 3 | 4 | # 5.6.0 5 | - RESTv2: updated jsdocs of v2/withdraw with latest params 6 | 7 | # 5.5.0 8 | - RESTv2: recurring algo order list endpoint 9 | 10 | # 5.4.0 11 | - RESTv2: recurring algo order endpoints 12 | 13 | # 5.3.0 14 | - RESTv2: add getWeightedAverages method 15 | 16 | # 5.2.0 17 | - RESTv2: add payCurrencyConversionList method 18 | - RESTv2: add payAddCurrencyConversion method 19 | - RESTv2: add payRemoveCurrencyConversion method 20 | - RESTv2: add payMerchantDailyLimit method 21 | - RESTv2: add payMerchantSettingsWrite method 22 | - RESTv2: add payMerchantSettingsWriteBatch method 23 | - RESTv2: add payMerchantSettingsRead method 24 | - RESTv2: add payMerchantSettingsList method 25 | 26 | # 5.1.1 27 | - dep: upgraded bfx-api-node-models to 1.7.1 for compatibility 28 | 29 | # 5.1.0 30 | - RESTv2: add movementInfo method 31 | 32 | # 5.0.2 33 | - RESTv2: fix empty id array in movements hist 34 | 35 | # 5.0.1 36 | - RESTv2: added address filter in movements hist 37 | - RESTv2: added endpoint for querying lnx invoices and payments 38 | 39 | # 5.0.0 40 | - RESTv2: refactored methods in format (params, cb) 41 | - RESTv2: replaced request-promise and bluebird with native promises and node-fetch 42 | - RESTv2: added support for order transform on submitOrder and closePosition 43 | - RESTv1: replaced request with node-fetch 44 | 45 | # 4.6.1 46 | - RESTv2: add submitRecurringAlgoOrder method 47 | - RESTv2: add getRecurringAlgoOrder method 48 | - RESTv2: add updateRecurringAlgoOrder method 49 | - RESTv2: add cancelRecurringAlgoOrder method 50 | - RESTv2: add getRecurringAlgoOrders method 51 | 52 | # 4.6.0 53 | - RESTv2: add ability to set timeout for private requests and change value in extra option 54 | 55 | # 4.5.0 56 | - RESTv2: add payDepositsUnlinked method 57 | - RESTv2: fix issue with wrong type in jsdocs for amount in transfer method 58 | 59 | # 4.4.1 60 | - fixed dependency versions 61 | 62 | # 4.4.0 63 | - RESTv2: add invalidateAuthToken method 64 | 65 | # 4.3.1 66 | - RESTv2: Fix nullish (optional) parameters in requests 67 | 68 | # 4.3.0 69 | - RESTv2: add getCoreSettings method 70 | 71 | # 4.2.0 72 | - RESTv2: add payInvoiceComplete method 73 | 74 | # 4.1.4 75 | - RESTv2: adjusted symbolDetails to return future pairs info, returns future pairs by default 76 | 77 | # 4.1.3 78 | - RESTv2: fixed filtering out false writePermission in generateToken 79 | 80 | # 4.1.2 81 | - RESTv2: fixed missing caps param in generateToken endpoint 82 | 83 | # 4.1.1 84 | - RESTv2: adjusted jsdoc for required email field in payInvoiceCreate 85 | 86 | # 4.1.0 87 | - RESTv2: add payInvoiceCreate method 88 | - RESTv2: add payInvoiceList method 89 | 90 | # 4.0.0 91 | - RESTv2: removed RESTv1 usage inside RESTv2 92 | - RESTv2: removed _rest1 93 | - RESTv2: removed _makePublicLegacyRequest 94 | - RESTv2: removed _makeAuthLegacyRequest 95 | - RESTv2: removed accountInfo, accountSummary already exists in v2 integration 96 | - RESTv2: removed balances, wallets already exists in v2 integration 97 | - RESTv2: adjusted accountSummary to use official documented v2 endpoint, response format changed 98 | - RESTv2: upgraded symbolDetails to v2, response format changed 99 | - RESTv2: upgraded accountFees to v2, response format changed 100 | - RESTv2: upgraded keyPermissions to v2, response format changed 101 | - RESTv2: upgraded closePosition to v2, response format changed 102 | 103 | # 3.1.0 104 | - RESTv2: add addPulseComment function 105 | - RESTv2: add fetchPulseComments function 106 | 107 | # 3.0.19 108 | - RESTv2: add _cust_ip to generateToken function 109 | - Remove Babel 110 | 111 | # 3.0.18 112 | - RESTv2: add walletFx to currencies function 113 | 114 | # 3.0.17 115 | - RESTv2: marketAveragePrice test fixes 116 | 117 | # 3.0.16 118 | - RESTv2: add marketAveragePrice function 119 | - RESTv2: add generateInvoice function 120 | - RESTv2: add keepFunding function 121 | - RESTv2: add cancelOrderMulti function 122 | - RESTv2: add orderMultiOp function 123 | - RESTv2: add submitOrderMulti function 124 | - RESTv2: add updateOrderMulti function 125 | - RESTv2: add cancelOrders function 126 | 127 | # 3.0.15 128 | - RESTv2: add publicPulseHistory function 129 | - RESTv2: add private pulseHistory function 130 | 131 | # 3.0.14 132 | - RESTv2: add private deletePulse function 133 | 134 | # 3.0.13 135 | - RESTv2: add inactiveSymbols endpoint 136 | 137 | # 3.0.12 138 | - RESTv2: add public pulse profile endpoint 139 | 140 | # 3.0.11 141 | - RESTv2: add positionsSnaps function 142 | 143 | # 3.0.10 144 | - RESTv2: adds changeLogs function 145 | 146 | # 3.0.9 147 | - RESTv2: fix [cb] find/replace error to cb where appropriate 148 | 149 | # 3.0.8 150 | - RESTv2: fixes cancelOrderWithCid function 151 | - RESTv2: removes cancelOrderWithDate function 152 | 153 | # 3.0.7 154 | - meta: added JSDoc-generated HTML docs 155 | - meta: added husky pre-commit test hook 156 | - meta: standardized eslint config 157 | 158 | # 3.0.6 159 | - RESTv2: adds cancelOrderWithCid function 160 | - RESTv2: adds cancelOrderWithDate function 161 | 162 | # 3.0.5 163 | - RESTv2: adds orderHistoryWithIds function 164 | - RESTv2: adds activeOrdersWithIds function 165 | 166 | # 3.0.4 167 | - RESTv2: added usesAgent() method 168 | - RESTv2: added getURL() method 169 | - RESTv2: default connection url now exposed on RESTv2.url 170 | 171 | # 3.0.3 172 | - fix: refactor tests so they can run alongside all other HF/API library tests 173 | 174 | # 3.0.2 175 | - RESTv2: add filters to ledgers 176 | 177 | # 3.0.1 178 | - RESTv2: add logins 179 | 180 | # 3.0.0 181 | - Hotfix: _takeResNotifyInfo return all notification info 182 | - restv2.withdraw returns notfication instead of just info 183 | 184 | # 2.0.7 185 | - Fix: submitFundingOffer 186 | 187 | # 2.0.6 188 | - Fix: Use correct endpoint for transfer 189 | 190 | # 2.0.5 191 | - RESTv2: add affCode support 192 | 193 | # 2.0.4 194 | - v2/rest/withdraw invalid url hotfix 195 | 196 | # 2.0.3 197 | - manifest: switch to npm reg deps for bfx modules 198 | - readme: minor edit 199 | 200 | # 2.0.2 201 | - hotfix circular deps error 202 | 203 | # 2.0.1 204 | - docs: create/update 205 | 206 | # 2.0.0 207 | - RESTv2: add submitOrder 208 | - RESTv2: add updateOrder 209 | - RESTv2: add cancelOrder 210 | - RESTv2: add claimPosition 211 | - RESTv2: add submitFundingOffer 212 | - RESTv2: add cancelFundingOffer 213 | - RESTv2: add closeFunding 214 | - RESTv2: add submitAutoFunding 215 | - RESTv2: add transfer 216 | - RESTv2: add getDepositAddress 217 | - RESTv2: add withdraw 218 | 219 | # 1.1.4 220 | - manifest: bump deps 221 | - meta: add github issue/pr templates 222 | - meta: standardize travis config 223 | - meta: add example 224 | 225 | # 1.1.3 226 | - RESTv2: add liquidations method 227 | 228 | # 1.1.2 229 | - RESTv2: add derivsPositionCollateralSet method 230 | - RESTv2: add statusMessages method 231 | - manifest: bump deps 232 | 233 | # 1.1.1 234 | - meta: ignore dist folder 235 | - RESTv1: add get/post debug logging 236 | - RESTv2: add get/post debug logging 237 | 238 | # 1.1.0 239 | - bump minor due to changes in v1.0.10 240 | 241 | # 1.0.10 242 | - RESTv2: add futures method 243 | - RESTv2 fix: add symbol to currencies list entries 244 | 245 | # 1.0.9 246 | - RESTv2 refactor: add ability to fetch all funding trades to fundingTrades() 247 | 248 | # 1.0.8 249 | - RESTv2 refactor: use v2 conf endpoint for symbols() 250 | - manifest: moved babel deps to dev-deps 251 | - meta: add .babelrc 252 | 253 | # 1.0.7 254 | - RESTv2 fix: PositionHist model renamed to Position 255 | 256 | # 1.0.6 257 | - RESTv2: add conf method 258 | - RESTv2: add generateToken method 259 | 260 | # 1.0.5 261 | - RESTv2 fix: default req limit for tickersHistory, positionsHistory, positionsAudit 262 | - RESTv2 fix: typo in tickersHistory API URL generation 263 | - RESTv2 refactor: add currency option to walletsHistory 264 | 265 | # 1.0.4 266 | - RESTv2: add tickersHistory method 267 | - RESTv2: add positionsHistory method 268 | - RESTv2: add positionsAudit method 269 | - RESTv2: add walletsHistory method 270 | 271 | # 1.0.3 272 | - RESTv2: refactor internal callback handling 273 | 274 | # 1.0.2 275 | - RESTv2: add orderBook method 276 | - manifest: add babel build for browser compatibility 277 | - manifest: use versioned bfx packages 278 | 279 | # 1.0.1 280 | - RESTv2: add pool & explorer data to currencies() 281 | - manifest: rm bfx-api-node-core 282 | - meta: add jsdoc 283 | - meta: fill in README 284 | 285 | # 1.0.0 286 | - Initial version 287 | -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | # Bitfinex RESTv1 & RESTv2 APIs for Node.JS 2 | 3 | A Node.JS reference implementation of the Bitfinex REST APIs 4 | 5 | To use, construct a new instance of either the `RESTv1` or `RESTv2` classes. All API methods return promises and accept a callback as the last parameter; the callback will be called with `(error, response)`. 6 | 7 | To minimize the data sent over the network the transmitted data is structured in arrays. In order to reconstruct key / value pairs, set `opts.transform` to `true` when creating an interface. 8 | 9 | ### Features 10 | 11 | * Official implementation 12 | * REST v2 API 13 | * REST v1 API 14 | 15 | ## Installation 16 | 17 | ```bash 18 | npm i --save bfx-api-node-rest 19 | ``` 20 | 21 | ### Quickstart 22 | 23 | ```js 24 | const { RESTv2 } = require('bfx-api-node-rest') 25 | const rest = new RESTv2({ transform: true }) 26 | 27 | // do something with the RESTv2 instance 28 | ``` 29 | 30 | ### Docs 31 | 32 | Documentation at [https://docs.bitfinex.com/v2/reference](https://docs.bitfinex.com/v2/reference) 33 | 34 | ## Example 35 | 36 | ```js 37 | const { RESTv2 } = require('bfx-api-node-rest') 38 | 39 | const rest = new RESTv2({ 40 | apiKey: '...', 41 | apiSecret: '...', 42 | authToken: '...', // optional, has priority over API key/secret 43 | url: '...', // optional 44 | transform: true, // to have full models returned by all methods 45 | agent: null, // optional proxy agent 46 | }) 47 | 48 | rest.candles({ 49 | timeframe: '1m', 50 | symbol: 'tBTCUSD', 51 | query: { 52 | start: Date.now() - (24 * 60 * 60 * 1000), 53 | end: Date.now(), 54 | limit: 1000, 55 | } 56 | }).then((candles) => { 57 | // ... 58 | }).catch((err) => { 59 | console.log(err) 60 | }) 61 | ``` 62 | 63 | ## FAQ 64 | 65 | ### nonce too small 66 | 67 | I make multiple parallel request and I receive an error that the nonce is too small. What does it mean? 68 | 69 | Nonces are used to guard against replay attacks. When multiple HTTP requests arrive at the API with the wrong nonce, e.g. because of an async timing issue, the API will reject the request. 70 | 71 | If you need to go parallel, you have to use multiple API keys right now. 72 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 bitfinexcom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitfinex RESTv1 & RESTv2 APIs for Node.JS 2 | 3 | [![Build Status](https://travis-ci.org/bitfinexcom/bfx-api-node-rest.svg?branch=master)](https://travis-ci.org/bitfinexcom/bfx-api-node-rest) 4 | A Node.JS reference implementation of the Bitfinex REST APIs 5 | 6 | To use, construct a new instance of either the `RESTv1` or `RESTv2` classes. 7 | All API methods return promises and accept a callback as the last parameter; the 8 | callback will be called with `(error, response)`. 9 | 10 | To minimize the data sent over the network the transmitted data is structured in 11 | arrays. In order to reconstruct key / value pairs, set `opts.transform` to `true` 12 | when creating an interface. 13 | 14 | ## Features 15 | 16 | * Official implementation 17 | * REST v2 API 18 | * REST v1 API 19 | 20 | ## Installation 21 | 22 | ```bash 23 | npm i --save bfx-api-node-rest 24 | ``` 25 | 26 | ### Quickstart 27 | 28 | ```js 29 | const { RESTv2 } = require('bfx-api-node-rest') 30 | const rest = new RESTv2({ transform: true }) 31 | 32 | // do something with the RESTv2 instance 33 | ``` 34 | 35 | ### Docs 36 | 37 | Documentation at [https://docs.bitfinex.com/v2/reference](https://docs.bitfinex.com/v2/reference) 38 | 39 | [See `docs/`](/docs) for JSDoc generated documentation of available methods. 40 | 41 | ## Example 42 | 43 | ```js 44 | const { RESTv2 } = require('bfx-api-node-rest') 45 | 46 | const rest = new RESTv2({ 47 | apiKey: '...', 48 | apiSecret: '...', 49 | authToken: '...', // optional, has priority over API key/secret 50 | url: '...', // optional 51 | transform: true, // to have full models returned by all methods 52 | agent: null, // optional proxy agent 53 | }) 54 | 55 | rest.candles({ 56 | timeframe: '1m', 57 | symbol: 'tBTCUSD', 58 | query: { 59 | start: Date.now() - (24 * 60 * 60 * 1000), 60 | end: Date.now(), 61 | limit: 1000, 62 | } 63 | }).then((candles) => { 64 | // ... 65 | }).catch((err) => { 66 | console.log(err) 67 | }) 68 | ``` 69 | 70 | ## FAQ 71 | 72 | ### nonce too small 73 | 74 | I make multiple parallel request and I receive an error that the nonce is too 75 | small. What does it mean? 76 | 77 | Nonces are used to guard against replay attacks. When multiple HTTP requests 78 | arrive at the API with the wrong nonce, e.g. because of an async timing issue, 79 | the API will reject the request. 80 | 81 | If you need to go parallel, you have to use multiple API keys right now. 82 | 83 | ### Contributing 84 | 85 | 1. Fork it 86 | 2. Create your feature branch (`git checkout -b my-new-feature`) 87 | 3. Commit your changes (`git commit -am 'Add some feature'`) 88 | 4. Push to the branch (`git push origin my-new-feature`) 89 | 5. Create a new Pull Request 90 | -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Bold.eot -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Bold.woff -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Bold.woff2 -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Regular.eot -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Regular.woff -------------------------------------------------------------------------------- /docs/fonts/Montserrat/Montserrat-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Montserrat/Montserrat-Regular.woff2 -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfinexcom/bfx-api-node-rest/d32e2703dd0edbd46e9a615da42d59a9fd8b19a5/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 -------------------------------------------------------------------------------- /docs/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Global - Documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 37 | 38 |
39 | 40 |

Global

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 | 52 |

53 | 54 | 55 | 56 |

57 | 58 | 59 |
60 | 61 |
62 | 63 |
64 | 65 | 66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |

Type Definitions

131 | 132 | 133 | 134 |

ClientOrderIdPayload

135 | 136 | 137 | 138 | 139 | 140 |
141 | 142 | 143 | 144 |
Source:
145 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
180 | 181 | 182 | 183 |
Properties:
184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 |
NameTypeDescription
0 213 | 214 | 215 | number 216 | 217 | 218 | 219 | 220 |

client order ID

1 237 | 238 | 239 | string 240 | 241 | 242 | 243 | 244 |

client order ID date i.e. '2020-05-28'

256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 |
Type:
265 |
    266 |
  • 267 | 268 | Array 269 | 270 | 271 | 272 |
  • 273 |
274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 |

MultiOrderOp

283 | 284 | 285 | 286 | 287 | 288 |
289 | 290 | 291 | 292 |
Source:
293 |
296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
328 | 329 | 330 | 331 |
Properties:
332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 |
NameTypeDescription
0 361 | 362 | 363 | string 364 | 365 | 366 | 367 | 368 |

operation, i.e. 'oc', 'on', 'oc_multi', 'ou'

1 385 | 386 | 387 | MultiOrderOpPayload 388 | | 389 | 390 | Order 391 | 392 | 393 | 394 | 395 |

payload, i.e. { id: [1, 2] }

407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 |
Type:
416 |
    417 |
  • 418 | 419 | Array 420 | 421 | 422 | 423 |
  • 424 |
425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 |

MultiOrderOpPayload

434 | 435 | 436 | 437 | 438 | 439 |
440 | 441 | 442 | 443 |
Source:
444 |
447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 |
479 | 480 | 481 | 482 |
Properties:
483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 525 | 526 | 527 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 |
NameTypeAttributesDescription
id 514 | 515 | 516 | number 517 | | 518 | 519 | Array.<number> 520 | 521 | 522 | 523 | 524 | 528 | 529 | <optional>
530 | 531 | 532 | 533 |

array of order IDs or single order ID

544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 |
Type:
553 |
    554 |
  • 555 | 556 | object 557 | 558 | 559 | 560 |
  • 561 |
562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 |
573 | 574 |
575 | 576 | 577 | 578 | 579 | 580 | 581 |
582 | 583 |
584 | 585 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home - Documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |

50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 |

Bitfinex RESTv1 & RESTv2 APIs for Node.JS

66 |

A Node.JS reference implementation of the Bitfinex REST APIs

67 |

To use, construct a new instance of either the RESTv1 or RESTv2 classes. All API methods return promises and accept a callback as the last parameter; the callback will be called with (error, response).

68 |

To minimize the data sent over the network the transmitted data is structured in arrays. In order to reconstruct key / value pairs, set opts.transform to true when creating an interface.

69 |

Features

70 |
    71 |
  • Official implementation
  • 72 |
  • REST v2 API
  • 73 |
  • REST v1 API
  • 74 |
75 |

Installation

76 |
  npm i --save bfx-api-node-rest
 77 | 
78 |

Quickstart

79 |
const { RESTv2 } = require('bfx-api-node-rest')
 80 | const rest = new RESTv2({ transform: true })
 81 | 
 82 | // do something with the RESTv2 instance
 83 | 
84 |

Docs

85 |

Documentation at https://docs.bitfinex.com/v2/reference

86 |

Example

87 |
const { RESTv2 } = require('bfx-api-node-rest')
 88 | 
 89 | const rest = new RESTv2({
 90 |   apiKey: '...',
 91 |   apiSecret: '...',
 92 |   authToken: '...', // optional, has priority over API key/secret
 93 |   url: '...',       // optional
 94 |   transform: true,  // to have full models returned by all methods
 95 |   agent: null,      // optional proxy agent
 96 | })
 97 | 
 98 | rest.candles({
 99 |   timeframe: '1m',
100 |   symbol: 'tBTCUSD',
101 |   query: {
102 |     start: Date.now() - (24 * 60 * 60 * 1000),
103 |     end: Date.now(),
104 |     limit: 1000,
105 |   }
106 | }).then((candles) => {
107 |   // ...
108 | }).catch((err) => {
109 |   console.log(err)
110 | })
111 | 
112 |

FAQ

113 |

nonce too small

114 |

I make multiple parallel request and I receive an error that the nonce is too small. What does it mean?

115 |

Nonces are used to guard against replay attacks. When multiple HTTP requests arrive at the API with the wrong nonce, e.g. because of an async timing issue, the API will reject the request.

116 |

If you need to go parallel, you have to use multiple API keys right now.

117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 |
127 | 128 |
129 | 130 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/rest1.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | rest1.js - Documentation 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 37 | 38 |
39 | 40 |

rest1.js

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 |
50 |
'use strict'
 51 | /* eslint-disable */
 52 | /* legacy API interface, not cleaned up with new eslint rules */
 53 | 
 54 | const fetch = require('node-fetch')
 55 | const debug = require('debug')('bfx:rest1')
 56 | const { genAuthSig, nonce } = require('bfx-api-node-util')
 57 | const API_URL = 'https://api.bitfinex.com'
 58 | 
 59 | /**
 60 |  * Communicates with v1 of the Bitfinex HTTP API
 61 |  */
 62 | class RESTv1 {
 63 |   /**
 64 |    * Instantiate a new REST v1 transport.
 65 |    *
 66 |    * @param {Object} opts
 67 |    * @param {string?} opts.apiKey
 68 |    * @param {string?} opts.apiSecret
 69 |    * @param {string?} opts.url - endpoint URL
 70 |    * @param {Object?} opts.agent - optional node agent for connection (proxy)
 71 |    * @param {Method?} opts.nonceGenerator - optional, should return a nonce
 72 |    */
 73 |   constructor (opts = {}) {
 74 |     this._url = opts.url || API_URL
 75 |     this._apiKey = opts.apiKey || ''
 76 |     this._apiSecret = opts.apiSecret || ''
 77 |     this._agent = opts.agent
 78 |     this._generateNonce = (typeof opts.nonceGenerator === 'function')
 79 |       ? opts.nonceGenerator
 80 |       : nonce
 81 |   }
 82 | 
 83 |   /**
 84 |    * @param {string} body - raw JSON
 85 |    * @param {Method} cb
 86 |    * @private
 87 |    */
 88 |   _parse_req_body (result, cb) {
 89 |     if (typeof result.message === 'string') {
 90 |       if (result.message.indexOf('Nonce is too small') !== -1) {
 91 |         result.message += ' See https://github.com/bitfinexcom/bitfinex-api-node/blob/master/README.md#nonce-too-small for help'
 92 |       }
 93 | 
 94 |       return cb(new Error(result.message))
 95 |     }
 96 | 
 97 |     return cb(null, result)
 98 |   }
 99 | 
100 |   /**
101 |    * @param {string} path
102 |    * @param {Object} params
103 |    * @param {Method} cb
104 |    * @private
105 |    */
106 |   async make_request (path, params, cb) {
107 |     if (!this._apiKey || !this._apiSecret) {
108 |       return cb(new Error('missing api key or secret'))
109 |     }
110 |     if (!path) {
111 |       return cb(new Error('path is missing'))
112 |     }
113 | 
114 |     const payload = Object.assign({
115 |       request: `/v1/${path}`,
116 |       nonce: JSON.stringify(this._generateNonce())
117 |     }, params)
118 | 
119 |     const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString('base64')
120 |     const { sig } = genAuthSig(this._apiSecret, payloadBase64)
121 |     const url = `${this._url}/v1/${path}`
122 | 
123 |     debug('POST %s', url)
124 | 
125 |     const reqOpts = {
126 |       method: 'POST',
127 |       timeout: 15000,
128 |       agent: this._agent,
129 |       headers: {
130 |         'X-BFX-APIKEY': this._apiKey,
131 |         'X-BFX-PAYLOAD': payloadBase64,
132 |         'X-BFX-SIGNATURE': sig
133 |       }
134 |     }
135 | 
136 |     try {
137 |       const resp = await fetch(url, reqOpts)
138 |       if (!resp.ok && +resp.status !== 400) {
139 |         throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`)
140 |       }
141 |       const json = await resp.json()
142 |       return this._parse_req_body(json, cb)
143 |     } catch (err) {
144 |       return cb(err)
145 |     }
146 |   }
147 | 
148 |   /**
149 |    * @param {string} path
150 |    * @param {Method} cb
151 |    * @private
152 |    */
153 |   async make_public_request (path, cb) {
154 |     if (!path) {
155 |       return cb(new Error('path is missing'))
156 |     }
157 | 
158 |     const url = `${this._url}/v1/${path}`
159 | 
160 |     debug('GET %s', url)
161 | 
162 |     const reqOpts = {
163 |       method: 'GET',
164 |       agent: this._agent,
165 |       timeout: 15000
166 |     }
167 | 
168 |     try {
169 |       const resp = await fetch(url, reqOpts)
170 |       if (!resp.ok && +resp.status !== 400) {
171 |         throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`)
172 |       }
173 |       const json = await resp.json()
174 |       return this._parse_req_body(json, cb)
175 |     } catch (err) {
176 |       return cb(err)
177 |     }
178 |   }
179 | 
180 |   /**
181 |    * @param {string} symbol
182 |    * @param {Method} cb
183 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-ticker
184 |    */
185 |   ticker (symbol = 'BTCUSD', cb) {
186 |     if (!cb) {
187 |       cb = (err, data) => {
188 |         if (err) {
189 |           console.error(err)
190 |         }
191 | 
192 |         console.log(data)
193 |       }
194 |     }
195 | 
196 |     return this.make_public_request(`pubticker/${symbol}`, cb)
197 |   }
198 | 
199 |   /**
200 |    * @param {string} symbol
201 |    * @param {Method} cb
202 |    */
203 |   today (symbol, cb) {
204 |     return this.make_public_request(`today/${symbol}`, cb)
205 |   }
206 | 
207 |   /**
208 |    * @param {string} symbol
209 |    * @param {Method} cb
210 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-stats
211 |    */
212 |   stats (symbol, cb) {
213 |     return this.make_public_request(`stats/${symbol}`, cb)
214 |   }
215 | 
216 |   /**
217 |    * @param {string} currency
218 |    * @param {Object} options
219 |    * @param {Method} cb
220 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-fundingbook
221 |    */
222 |   fundingbook (currency, options, cb) {
223 |     let uri = `lendbook/${currency}`
224 | 
225 |     if (typeof options === 'function') {
226 |       cb = options
227 |     } else {
228 |       const keys = Object.keys(options)
229 | 
230 |       for (let i = 0; i < keys.length; i++) {
231 |         uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}`
232 |       }
233 |     }
234 | 
235 |     return this.make_public_request(uri, cb)
236 |   }
237 | 
238 |   /**
239 |    * @param {string} symbol
240 |    * @param {Object} options
241 |    * @param {Method} cb
242 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-orderbook
243 |    */
244 |   orderbook (symbol, options, cb) {
245 |     let uri = `book/${symbol}`
246 | 
247 |     if (typeof options === 'function') {
248 |       cb = options
249 |     } else {
250 |       const keys = Object.keys(options)
251 | 
252 |       for (let i = 0; i < keys.length; i++) {
253 |         uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}`
254 |       }
255 |     }
256 | 
257 |     return this.make_public_request(uri, cb)
258 |   }
259 | 
260 |   /**
261 |    * @param {string} symbol
262 |    * @param {Method} cb
263 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-trades
264 |    */
265 |   trades (symbol, cb) {
266 |     return this.make_public_request('trades/' + symbol, cb)
267 |   }
268 | 
269 |   /**
270 |    * @param {string} symbol
271 |    * @param {Method} cb
272 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-lends
273 |    */
274 |   lends (currency, cb) {
275 |     return this.make_public_request('lends/' + currency, cb)
276 |   }
277 | 
278 |   /**
279 |    * @param {Method} cb
280 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-symbols
281 |    */
282 |   get_symbols (cb) {
283 |     return this.make_public_request('symbols', cb)
284 |   }
285 | 
286 |   /**
287 |    * @param {Method} cb
288 |    * @see https://docs.bitfinex.com/v1/reference#rest-public-symbol-details
289 |    */
290 |   symbols_details (cb) {
291 |     return this.make_public_request('symbols_details', cb)
292 |   }
293 | 
294 |   /**
295 |    * @param {string} symbol
296 |    * @param {number} amount
297 |    * @param {number} price
298 |    * @param {string} exchange
299 |    * @param {string} side
300 |    * @param {string} type
301 |    * @param {boolean} is_hidden
302 |    * @param {boolean} postOnly
303 |    * @param {Method} cb
304 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-order
305 |    */
306 |   new_order (symbol, amount, price, exchange, side, type, is_hidden, postOnly, cb) {
307 |     if (typeof is_hidden === 'function') {
308 |       cb = is_hidden
309 |       is_hidden = false
310 |     }
311 | 
312 |     if (typeof postOnly === 'function') {
313 |       cb = postOnly
314 |       postOnly = false
315 |     }
316 | 
317 |     const params = {
318 |       symbol,
319 |       amount,
320 |       price,
321 |       exchange,
322 |       side,
323 |       type
324 |     }
325 | 
326 |     if (postOnly) params.post_only = true
327 |     if (is_hidden) params.is_hidden = true
328 | 
329 |     return this.make_request('order/new', params, cb)
330 |   }
331 | 
332 |   /**
333 |    * @param {Object[]} orders
334 |    * @param {Method} cb
335 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-multiple-new-orders
336 |    */
337 |   multiple_new_orders (orders, cb) {
338 |     return this.make_request('order/new/multi', { orders }, cb)
339 |   }
340 | 
341 |   /**
342 |    * @param {number} order_id
343 |    * @param {Method} cb
344 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-order
345 |    */
346 |   cancel_order (order_id, cb) {
347 |     return this.make_request('order/cancel', {
348 |       order_id: parseInt(order_id)
349 |     }, cb)
350 |   }
351 | 
352 |   /**
353 |    * @param {Method} cb
354 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-all-orders
355 |    */
356 |   cancel_all_orders (cb) {
357 |     return this.make_request('order/cancel/all', {}, cb)
358 |   }
359 | 
360 |   /**
361 |    * @param {number[]} order_ids
362 |    * @param {Method} cb
363 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-multiple-orders
364 |    */
365 |   cancel_multiple_orders (order_ids, cb) {
366 |     return this.make_request('order/cancel/multi', {
367 |       order_ids: order_ids.map(id => parseInt(id))
368 |     }, cb)
369 |   }
370 | 
371 |   /**
372 |    * @param {number} order_id
373 |    * @param {string} symbol
374 |    * @param {number} amount
375 |    * @param {number} price
376 |    * @param {string} exchange
377 |    * @param {string} side
378 |    * @param {string} type
379 |    * @param {Method} cb
380 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-replace-order
381 |    */
382 |   replace_order (order_id, symbol, amount, price, exchange, side, type, cb) {
383 |     return this.make_request('order/cancel/replace', {
384 |       order_id: parseInt(order_id),
385 |       symbol,
386 |       amount,
387 |       price,
388 |       exchange,
389 |       side,
390 |       type
391 |     }, cb)
392 |   }
393 | 
394 |   /**
395 |    * @param {string} order_id
396 |    * @param {Method} cb
397 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-order-status
398 |    */
399 |   order_status (order_id, cb) {
400 |     return this.make_request('order/status', {
401 |       order_id: parseInt(order_id)
402 |     }, cb)
403 |   }
404 | 
405 |   /**
406 |    * @param {Method} cb
407 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-orders
408 |    */
409 |   active_orders (cb) {
410 |     return this.make_request('orders', {}, cb)
411 |   }
412 | 
413 |   /**
414 |    * @param {Method} cb
415 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-orders-history
416 |    */
417 |   orders_history (cb) {
418 |     return this.make_request('orders/hist', {}, cb)
419 |   }
420 | 
421 |   /**
422 |    * @param {Method} cb
423 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-positions
424 |    */
425 |   active_positions (cb) {
426 |     return this.make_request('positions', {}, cb)
427 |   }
428 | 
429 |   /**
430 |    * @param {string} position_id
431 |    * @param {number} amount
432 |    * @param {Method} cb
433 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-claim-position
434 |    */
435 |   claim_position (position_id, amount, cb) {
436 |     return this.make_request('position/claim', {
437 |       position_id: parseInt(position_id),
438 |       amount
439 |     }, cb)
440 |   }
441 | 
442 |   /**
443 |    * @param {string} currency
444 |    * @param {Object} options
445 |    * @param {Method} cb
446 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-balance-history
447 |    */
448 |   balance_history (currency, options, cb) {
449 |     const params = { currency }
450 | 
451 |     if (typeof options === 'function') {
452 |       cb = options
453 |     } else if (options && options.constructor.name === 'Object') {
454 |       Object.assign(params, options)
455 |     }
456 | 
457 |     return this.make_request('history', params, cb)
458 |   }
459 | 
460 |   /**
461 |    * @param {string} currency
462 |    * @param {Object} options
463 |    * @param {Method} cb
464 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit-withdrawal-history
465 |    */
466 |   movements (currency, options, cb) {
467 |     const params = { currency }
468 | 
469 |     if (typeof options === 'function') {
470 |       cb = options
471 |     } else if (options && options.constructor.name === 'Object') {
472 |       Object.assign(params, options)
473 |     }
474 | 
475 |     return this.make_request('history/movements', params, cb)
476 |   }
477 | 
478 |   /**
479 |    * @param {string} symbol
480 |    * @param {Object} options
481 |    * @param {Method} cb
482 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-past-trades
483 |    */
484 |   past_trades (symbol, options, cb) {
485 |     const params = { symbol }
486 | 
487 |     if (typeof options === 'function') {
488 |       cb = options
489 |     } else if (options && options.constructor.name === 'Object') {
490 |       Object.assign(params, options)
491 |     }
492 | 
493 |     return this.make_request('mytrades', params, cb)
494 |   }
495 | 
496 |   /**
497 |    * @param {string} currency
498 |    * @param {string} method
499 |    * @param {string} wallet_name
500 |    * @param {Method} cb
501 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit
502 |    */
503 |   new_deposit (currency, method, wallet_name, cb) {
504 |     return this.make_request('deposit/new', {
505 |       currency,
506 |       method,
507 |       wallet_name
508 |     }, cb)
509 |   }
510 | 
511 |   /**
512 |    * @param {string} currency
513 |    * @param {number} amount
514 |    * @param {number} rate
515 |    * @param {number} period
516 |    * @param {string} direction
517 |    * @param {Method} cb
518 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-offer
519 |    */
520 |   new_offer (currency, amount, rate, period, direction, cb) {
521 |     return this.make_request('offer/new', {
522 |       currency,
523 |       amount,
524 |       rate,
525 |       period,
526 |       direction
527 |     }, cb)
528 |   }
529 | 
530 |   /**
531 |    * @param {string} offer_id
532 |    * @param {Method} cb
533 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-offer
534 |    */
535 |   cancel_offer (offer_id, cb) {
536 |     return this.make_request('offer/cancel', {
537 |       offer_id: parseInt(offer_id)
538 |     }, cb)
539 |   }
540 | 
541 |   /**
542 |    * @param {string} offer_id
543 |    * @param {Method} cb
544 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-offer-status
545 |    */
546 |   offer_status (offer_id, cb) {
547 |     return this.make_request('offer/status', {
548 |       offer_id: parseInt(offer_id)
549 |     }, cb)
550 |   }
551 | 
552 |   /**
553 |    * @param {Method} cb
554 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-offers
555 |    */
556 |   active_offers (cb) {
557 |     return this.make_request('offers', {}, cb)
558 |   }
559 | 
560 |   /**
561 |    * @param {Method} cb
562 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-credits
563 |    */
564 |   active_credits (cb) {
565 |     return this.make_request('credits', {}, cb)
566 |   }
567 | 
568 |   /**
569 |    * @param {Method} cb
570 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-wallet-balances
571 |    */
572 |   wallet_balances (cb) {
573 |     return this.make_request('balances', {}, cb)
574 |   }
575 | 
576 |   /**
577 |    * @param {Method} cb
578 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-funding-used-in-a-margin-position
579 |    */
580 |   taken_swaps (cb) {
581 |     return this.make_request('taken_funds', {}, cb)
582 |   }
583 | 
584 |   /**
585 |    * @param {Method} cb
586 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-total-taken-funds
587 |    */
588 |   total_taken_swaps (cb) {
589 |     return this.make_request('total_taken_funds', {}, cb)
590 |   }
591 | 
592 |   /**
593 |    * @param {string} swap_id
594 |    * @param {Method} cb
595 |    */
596 |   close_swap (swap_id, cb) {
597 |     return this.make_request('swap/close', {
598 |       swap_id: parseInt(swap_id)
599 |     }, cb)
600 |   }
601 | 
602 |   /**
603 |    * @param {Method} cb
604 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-account-info
605 |    */
606 |   account_infos (cb) {
607 |     return this.make_request('account_infos', {}, cb)
608 |   }
609 | 
610 |   /**
611 |    * @param {Method} cb
612 |    * @see https://docs.bitfinex.com/v1/reference#rest-auth-margin-information
613 |    */
614 |   margin_infos (cb) {
615 |     return this.make_request('margin_infos', {}, cb)
616 |   }
617 | 
618 |   /**
619 |    * POST /v1/withdraw
620 |    *
621 |    * @param {string} withdrawType "bitcoin", "litecoin", "darkcoin" or "mastercoin"
622 |    * @param {string} walletSelected origin of the wallet to withdraw from, can be "trading", "exchange", or "deposit"
623 |    * @param {number} amount amount to withdraw
624 |    * @param {string} address destination address for withdrawal
625 |    */
626 |   withdraw (withdrawType, walletSelected, amount, address, cb) {
627 |     return this.make_request('withdraw', {
628 |       withdrawType,
629 |       walletSelected,
630 |       amount,
631 |       address
632 |     }, cb)
633 |   }
634 | 
635 |   /**
636 |    * POST /v1/transfer
637 |    *
638 |    * @param {number} amount amount to transfer
639 |    * @param {string} currency currency of funds to transfer
640 |    * @param {string} walletFrom wallet to transfer from
641 |    * @param {string} walletTo wallet to transfer to
642 |    */
643 |   transfer (amount, currency, walletFrom, walletTo, cb) {
644 |     return this.make_request('transfer', {
645 |       amount,
646 |       currency,
647 |       walletFrom,
648 |       walletTo
649 |     }, cb)
650 |   }
651 | }
652 | 
653 | module.exports = RESTv1
654 | 
655 |
656 |
657 | 658 | 659 | 660 | 661 | 662 | 663 |
664 | 665 |
666 | 667 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | -------------------------------------------------------------------------------- /docs/scripts/collapse.js: -------------------------------------------------------------------------------- 1 | function hideAllButCurrent () { 2 | //by default all submenut items are hidden 3 | //but we need to rehide them for search 4 | document.querySelectorAll("nav > ul").forEach(function (parent) { 5 | if (parent.className.indexOf("collapse_top") !== -1) { 6 | parent.style.display = "none" 7 | } 8 | }) 9 | document.querySelectorAll("nav > ul > li > ul li").forEach(function (parent) { 10 | parent.style.display = "none" 11 | }) 12 | document.querySelectorAll("nav > h3").forEach(function (section) { 13 | if (section.className.indexOf("collapsed_header") !== -1) { 14 | section.addEventListener("click", function () { 15 | if (section.nextSibling.style.display === "none") { 16 | section.nextSibling.style.display = "block" 17 | } else { 18 | section.nextSibling.style.display = "none" 19 | } 20 | }) 21 | } 22 | }) 23 | 24 | //only current page (if it exists) should be opened 25 | var file = window.location.pathname.split("/").pop().replace(/\.html/, '') 26 | document.querySelectorAll("nav > ul > li > a").forEach(function (parent) { 27 | var href = parent.attributes.href.value.replace(/\.html/, '') 28 | if (file === href) { 29 | if (parent.parentNode.parentNode.className.indexOf("collapse_top") !== -1) { 30 | parent.parentNode.parentNode.style.display = "block" 31 | } 32 | parent.parentNode.querySelectorAll("ul li").forEach(function (elem) { 33 | elem.style.display = "block" 34 | }) 35 | } 36 | }) 37 | } 38 | 39 | hideAllButCurrent() 40 | -------------------------------------------------------------------------------- /docs/scripts/commonNav.js: -------------------------------------------------------------------------------- 1 | if (typeof fetch === 'function') { 2 | const init = () => { 3 | if (typeof scrollToNavItem !== 'function') return false 4 | scrollToNavItem() 5 | // hideAllButCurrent not always loaded 6 | if (typeof hideAllButCurrent === 'function') hideAllButCurrent() 7 | return true 8 | } 9 | fetch('./nav.inc.html') 10 | .then(response => response.ok ? response.text() : `${response.url} => ${response.status} ${response.statusText}`) 11 | .then(body => { 12 | document.querySelector('nav').innerHTML += body 13 | // nav.js should be quicker to load than nav.inc.html, a fallback just in case 14 | return init() 15 | }) 16 | .then(done => { 17 | if (done) return 18 | let i = 0 19 | ;(function waitUntilNavJs () { 20 | if (init()) return 21 | if (i++ < 100) return setTimeout(waitUntilNavJs, 300) 22 | console.error(Error('nav.js not loaded after 30s waiting for it')) 23 | })() 24 | }) 25 | .catch(error => console.error(error)) 26 | } else { 27 | console.error(Error('Browser too old to display commonNav (remove commonNav docdash option)')) 28 | } 29 | -------------------------------------------------------------------------------- /docs/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/scripts/nav.js: -------------------------------------------------------------------------------- 1 | function scrollToNavItem() { 2 | var path = window.location.href.split('/').pop().replace(/\.html/, ''); 3 | document.querySelectorAll('nav a').forEach(function(link) { 4 | var href = link.attributes.href.value.replace(/\.html/, ''); 5 | if (path === href) { 6 | link.scrollIntoView({block: 'center'}); 7 | return; 8 | } 9 | }) 10 | } 11 | 12 | scrollToNavItem(); 13 | -------------------------------------------------------------------------------- /docs/scripts/polyfill.js: -------------------------------------------------------------------------------- 1 | //IE Fix, src: https://www.reddit.com/r/programminghorror/comments/6abmcr/nodelist_lacks_foreach_in_internet_explorer/ 2 | if (typeof(NodeList.prototype.forEach)!==typeof(alert)){ 3 | NodeList.prototype.forEach=Array.prototype.forEach; 4 | } -------------------------------------------------------------------------------- /docs/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p ul > li:not(.level-hide)").forEach(function(elem) { 16 | elem.style.display = "block"; 17 | }); 18 | 19 | if (typeof hideAllButCurrent === "function"){ 20 | //let's do what ever collapse wants to do 21 | hideAllButCurrent(); 22 | } else { 23 | //menu by default should be opened 24 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 25 | elem.style.display = "block"; 26 | }); 27 | } 28 | } else { 29 | //we are searching 30 | document.documentElement.setAttribute(searchAttr, ''); 31 | 32 | //show all parents 33 | document.querySelectorAll("nav > ul > li").forEach(function(elem) { 34 | elem.style.display = "block"; 35 | }); 36 | document.querySelectorAll("nav > ul").forEach(function(elem) { 37 | elem.style.display = "block"; 38 | }); 39 | //hide all results 40 | document.querySelectorAll("nav > ul > li > ul li").forEach(function(elem) { 41 | elem.style.display = "none"; 42 | }); 43 | //show results matching filter 44 | document.querySelectorAll("nav > ul > li > ul a").forEach(function(elem) { 45 | if (!contains(elem.parentNode, search)) { 46 | return; 47 | } 48 | elem.parentNode.style.display = "block"; 49 | }); 50 | //hide parents without children 51 | document.querySelectorAll("nav > ul > li").forEach(function(parent) { 52 | var countSearchA = 0; 53 | parent.querySelectorAll("a").forEach(function(elem) { 54 | if (contains(elem, search)) { 55 | countSearchA++; 56 | } 57 | }); 58 | 59 | var countUl = 0; 60 | var countUlVisible = 0; 61 | parent.querySelectorAll("ul").forEach(function(ulP) { 62 | // count all elements that match the search 63 | if (contains(ulP, search)) { 64 | countUl++; 65 | } 66 | 67 | // count all visible elements 68 | var children = ulP.children 69 | for (i=0; i ul.collapse_top").forEach(function(parent) { 86 | var countVisible = 0; 87 | parent.querySelectorAll("li").forEach(function(elem) { 88 | if (elem.style.display !== "none") { 89 | countVisible++; 90 | } 91 | }); 92 | 93 | if (countVisible == 0) { 94 | //has no child at all and does not contain text 95 | parent.style.display = "none"; 96 | } 97 | }); 98 | } 99 | }); -------------------------------------------------------------------------------- /docs/styles/jsdoc.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box 3 | } 4 | 5 | html, body { 6 | height: 100%; 7 | width: 100%; 8 | } 9 | 10 | body { 11 | color: #4d4e53; 12 | background-color: white; 13 | margin: 0 auto; 14 | padding: 0 20px; 15 | font-family: 'Helvetica Neue', Helvetica, sans-serif; 16 | font-size: 16px; 17 | } 18 | 19 | img { 20 | max-width: 100%; 21 | } 22 | 23 | a, 24 | a:active { 25 | color: #606; 26 | text-decoration: none; 27 | } 28 | 29 | a:hover { 30 | text-decoration: none; 31 | } 32 | 33 | article a { 34 | border-bottom: 1px solid #ddd; 35 | } 36 | 37 | article a:hover, article a:active { 38 | border-bottom-color: #222; 39 | } 40 | 41 | article .description a { 42 | word-break: break-word; 43 | } 44 | 45 | p, ul, ol, blockquote { 46 | margin-bottom: 1em; 47 | line-height: 160%; 48 | } 49 | 50 | h1, h2, h3, h4, h5, h6 { 51 | font-family: 'Montserrat', sans-serif; 52 | } 53 | 54 | h1, h2, h3, h4, h5, h6 { 55 | color: #000; 56 | font-weight: 400; 57 | margin: 0; 58 | } 59 | 60 | h1 { 61 | font-weight: 300; 62 | font-size: 48px; 63 | margin: 1em 0 .5em; 64 | } 65 | 66 | h1.page-title { 67 | font-size: 48px; 68 | margin: 1em 30px; 69 | line-height: 100%; 70 | word-wrap: break-word; 71 | } 72 | 73 | h2 { 74 | font-size: 24px; 75 | margin: 1.5em 0 .3em; 76 | } 77 | 78 | h3 { 79 | font-size: 24px; 80 | margin: 1.2em 0 .3em; 81 | } 82 | 83 | h4 { 84 | font-size: 18px; 85 | margin: 1em 0 .2em; 86 | color: #4d4e53; 87 | } 88 | 89 | h4.name { 90 | color: #fff; 91 | background: #6d426d; 92 | box-shadow: 0 .25em .5em #d3d3d3; 93 | border-top: 1px solid #d3d3d3; 94 | border-bottom: 1px solid #d3d3d3; 95 | margin: 1.5em 0 0.5em; 96 | padding: .75em 0 .75em 10px; 97 | } 98 | 99 | h4.name a { 100 | color: #fc83ff; 101 | } 102 | 103 | h4.name a:hover { 104 | border-bottom-color: #fc83ff; 105 | } 106 | 107 | h5, .container-overview .subsection-title { 108 | font-size: 120%; 109 | letter-spacing: -0.01em; 110 | margin: 8px 0 3px 0; 111 | } 112 | 113 | h6 { 114 | font-size: 100%; 115 | letter-spacing: -0.01em; 116 | margin: 6px 0 3px 0; 117 | font-style: italic; 118 | } 119 | 120 | .usertext h1 { 121 | font-family: "Source Sans Pro"; 122 | font-size: 24px; 123 | margin: 2.5em 0 1em; 124 | font-weight: 400; 125 | } 126 | 127 | .usertext h2 { 128 | font-family: "Source Sans Pro"; 129 | font-size: 18px; 130 | margin: 2em 0 0.5em; 131 | font-weight: 400; 132 | 133 | } 134 | 135 | .usertext h3 { 136 | font-family: "Source Sans Pro"; 137 | font-size: 15px; 138 | margin: 1.5em 0 0; 139 | font-weight: 400; 140 | } 141 | 142 | .usertext h4 { 143 | font-family: "Source Sans Pro"; 144 | font-size: 14px; 145 | margin: 0 0 0; 146 | font-weight: 400; 147 | } 148 | 149 | .usertext h5 { 150 | font-size: 12px; 151 | margin: 1em 0 0; 152 | font-weight: normal; 153 | color: #666; 154 | } 155 | 156 | .usertext h6 { 157 | font-size: 11px; 158 | margin: 1em 0 0; 159 | font-weight: normal; 160 | font-style: normal; 161 | color: #666; 162 | } 163 | 164 | 165 | tt, code, kbd, samp, pre { 166 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 167 | background: #f4f4f4; 168 | } 169 | 170 | tt, code, kbd, samp{ 171 | padding: 1px 5px; 172 | } 173 | 174 | pre { 175 | padding-bottom: 1em; 176 | } 177 | 178 | .class-description { 179 | font-size: 130%; 180 | line-height: 140%; 181 | margin-bottom: 1em; 182 | margin-top: 1em; 183 | } 184 | 185 | .class-description:empty { 186 | margin: 0 187 | } 188 | 189 | #main { 190 | float: right; 191 | width: calc(100% - 240px); 192 | } 193 | 194 | header { 195 | display: block 196 | } 197 | 198 | section { 199 | display: block; 200 | background-color: #fff; 201 | padding: 0 0 0 30px; 202 | } 203 | 204 | .variation { 205 | display: none 206 | } 207 | 208 | .signature-attributes { 209 | font-size: 60%; 210 | color: #eee; 211 | font-style: italic; 212 | font-weight: lighter; 213 | } 214 | 215 | nav { 216 | float: left; 217 | display: block; 218 | width: 250px; 219 | background: #fff; 220 | overflow: auto; 221 | position: fixed; 222 | height: 100%; 223 | } 224 | 225 | nav #nav-search{ 226 | width: 210px; 227 | height: 30px; 228 | padding: 5px 10px; 229 | font-size: 12px; 230 | line-height: 1.5; 231 | border-radius: 3px; 232 | margin-right: 20px; 233 | margin-top: 20px; 234 | } 235 | 236 | nav.wrap a{ 237 | word-wrap: break-word; 238 | } 239 | 240 | nav h3 { 241 | margin-top: 12px; 242 | font-size: 13px; 243 | text-transform: uppercase; 244 | letter-spacing: 1px; 245 | font-weight: 700; 246 | line-height: 24px; 247 | margin: 15px 0 10px; 248 | padding: 0; 249 | color: #000; 250 | } 251 | 252 | nav h3.collapsed_header { 253 | cursor: pointer; 254 | } 255 | 256 | nav ul { 257 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 258 | font-size: 100%; 259 | line-height: 17px; 260 | padding: 0; 261 | margin: 0; 262 | list-style-type: none; 263 | } 264 | 265 | nav ul a, 266 | nav ul a:active { 267 | font-family: 'Montserrat', sans-serif; 268 | line-height: 18px; 269 | padding: 0; 270 | display: block; 271 | font-size: 12px; 272 | } 273 | 274 | nav a:hover, 275 | nav a:active { 276 | color: #606; 277 | } 278 | 279 | nav > ul { 280 | padding: 0 10px; 281 | } 282 | 283 | nav > ul > li > a { 284 | color: #606; 285 | margin-top: 10px; 286 | } 287 | 288 | nav ul ul a { 289 | color: hsl(207, 1%, 60%); 290 | border-left: 1px solid hsl(207, 10%, 86%); 291 | } 292 | 293 | nav ul ul a, 294 | nav ul ul a:active { 295 | padding-left: 20px 296 | } 297 | 298 | nav h2 { 299 | font-size: 13px; 300 | margin: 10px 0 0 0; 301 | padding: 0; 302 | } 303 | 304 | nav > h2 > a { 305 | margin: 10px 0 -10px; 306 | color: #606 !important; 307 | } 308 | 309 | footer { 310 | color: hsl(0, 0%, 28%); 311 | margin-left: 250px; 312 | display: block; 313 | padding: 15px; 314 | font-style: italic; 315 | font-size: 90%; 316 | } 317 | 318 | .ancestors { 319 | color: #999 320 | } 321 | 322 | .ancestors a { 323 | color: #999 !important; 324 | } 325 | 326 | .clear { 327 | clear: both 328 | } 329 | 330 | .important { 331 | font-weight: bold; 332 | color: #950B02; 333 | } 334 | 335 | .yes-def { 336 | text-indent: -1000px 337 | } 338 | 339 | .type-signature { 340 | color: #CA79CA 341 | } 342 | 343 | .type-signature:last-child { 344 | color: #eee; 345 | } 346 | 347 | .name, .signature { 348 | font-family: Consolas, Monaco, 'Andale Mono', monospace 349 | } 350 | 351 | .signature { 352 | color: #fc83ff; 353 | } 354 | 355 | .details { 356 | margin-top: 6px; 357 | border-left: 2px solid #DDD; 358 | line-height: 20px; 359 | font-size: 14px; 360 | } 361 | 362 | .details dt { 363 | width: auto; 364 | float: left; 365 | padding-left: 10px; 366 | } 367 | 368 | .details dd { 369 | margin-left: 70px; 370 | margin-top: 6px; 371 | margin-bottom: 6px; 372 | } 373 | 374 | .details ul { 375 | margin: 0 376 | } 377 | 378 | .details ul { 379 | list-style-type: none 380 | } 381 | 382 | .details pre.prettyprint { 383 | margin: 0 384 | } 385 | 386 | .details .object-value { 387 | padding-top: 0 388 | } 389 | 390 | .description { 391 | margin-bottom: 1em; 392 | margin-top: 1em; 393 | } 394 | 395 | .code-caption { 396 | font-style: italic; 397 | font-size: 107%; 398 | margin: 0; 399 | } 400 | 401 | .prettyprint { 402 | font-size: 14px; 403 | overflow: auto; 404 | } 405 | 406 | .prettyprint.source { 407 | width: inherit; 408 | line-height: 18px; 409 | display: block; 410 | background-color: #0d152a; 411 | color: #aeaeae; 412 | } 413 | 414 | .prettyprint code { 415 | line-height: 18px; 416 | display: block; 417 | background-color: #0d152a; 418 | color: #4D4E53; 419 | } 420 | 421 | .prettyprint > code { 422 | padding: 15px; 423 | } 424 | 425 | .prettyprint .linenums code { 426 | padding: 0 15px 427 | } 428 | 429 | .prettyprint .linenums li:first-of-type code { 430 | padding-top: 15px 431 | } 432 | 433 | .prettyprint code span.line { 434 | display: inline-block 435 | } 436 | 437 | .prettyprint.linenums { 438 | padding-left: 70px; 439 | -webkit-user-select: none; 440 | -moz-user-select: none; 441 | -ms-user-select: none; 442 | user-select: none; 443 | } 444 | 445 | .prettyprint.linenums ol { 446 | padding-left: 0 447 | } 448 | 449 | .prettyprint.linenums li { 450 | border-left: 3px #34446B solid; 451 | } 452 | 453 | .prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { 454 | background-color: #34446B; 455 | } 456 | 457 | .prettyprint.linenums li * { 458 | -webkit-user-select: text; 459 | -moz-user-select: text; 460 | -ms-user-select: text; 461 | user-select: text; 462 | } 463 | 464 | .prettyprint.linenums li code:empty:after { 465 | content:""; 466 | display:inline-block; 467 | width:0px; 468 | } 469 | 470 | table { 471 | border-spacing: 0; 472 | border: 1px solid #ddd; 473 | border-collapse: collapse; 474 | border-radius: 3px; 475 | box-shadow: 0 1px 3px rgba(0,0,0,0.1); 476 | width: 100%; 477 | font-size: 14px; 478 | margin: 1em 0; 479 | } 480 | 481 | td, th { 482 | margin: 0px; 483 | text-align: left; 484 | vertical-align: top; 485 | padding: 10px; 486 | display: table-cell; 487 | } 488 | 489 | thead tr, thead tr { 490 | background-color: #fff; 491 | font-weight: bold; 492 | border-bottom: 1px solid #ddd; 493 | } 494 | 495 | .params .type { 496 | white-space: nowrap; 497 | } 498 | 499 | .params code { 500 | white-space: pre; 501 | } 502 | 503 | .params td, .params .name, .props .name, .name code { 504 | color: #4D4E53; 505 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 506 | font-size: 100%; 507 | } 508 | 509 | .params td { 510 | border-top: 1px solid #eee 511 | } 512 | 513 | .params td.description > p:first-child, .props td.description > p:first-child { 514 | margin-top: 0; 515 | padding-top: 0; 516 | } 517 | 518 | .params td.description > p:last-child, .props td.description > p:last-child { 519 | margin-bottom: 0; 520 | padding-bottom: 0; 521 | } 522 | 523 | span.param-type, .params td .param-type, .param-type dd { 524 | color: #606; 525 | font-family: Consolas, Monaco, 'Andale Mono', monospace 526 | } 527 | 528 | .param-type dt, .param-type dd { 529 | display: inline-block 530 | } 531 | 532 | .param-type { 533 | margin: 14px 0; 534 | } 535 | 536 | .disabled { 537 | color: #454545 538 | } 539 | 540 | /* navicon button */ 541 | .navicon-button { 542 | display: none; 543 | position: relative; 544 | padding: 2.0625rem 1.5rem; 545 | transition: 0.25s; 546 | cursor: pointer; 547 | -webkit-user-select: none; 548 | -moz-user-select: none; 549 | -ms-user-select: none; 550 | user-select: none; 551 | opacity: .8; 552 | } 553 | .navicon-button .navicon:before, .navicon-button .navicon:after { 554 | transition: 0.25s; 555 | } 556 | .navicon-button:hover { 557 | transition: 0.5s; 558 | opacity: 1; 559 | } 560 | .navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { 561 | transition: 0.25s; 562 | } 563 | .navicon-button:hover .navicon:before { 564 | top: .825rem; 565 | } 566 | .navicon-button:hover .navicon:after { 567 | top: -.825rem; 568 | } 569 | 570 | /* navicon */ 571 | .navicon { 572 | position: relative; 573 | width: 2.5em; 574 | height: .3125rem; 575 | background: #000; 576 | transition: 0.3s; 577 | border-radius: 2.5rem; 578 | } 579 | .navicon:before, .navicon:after { 580 | display: block; 581 | content: ""; 582 | height: .3125rem; 583 | width: 2.5rem; 584 | background: #000; 585 | position: absolute; 586 | z-index: -1; 587 | transition: 0.3s 0.25s; 588 | border-radius: 1rem; 589 | } 590 | .navicon:before { 591 | top: .625rem; 592 | } 593 | .navicon:after { 594 | top: -.625rem; 595 | } 596 | 597 | /* open */ 598 | .nav-trigger:checked + label:not(.steps) .navicon:before, 599 | .nav-trigger:checked + label:not(.steps) .navicon:after { 600 | top: 0 !important; 601 | } 602 | 603 | .nav-trigger:checked + label .navicon:before, 604 | .nav-trigger:checked + label .navicon:after { 605 | transition: 0.5s; 606 | } 607 | 608 | /* Minus */ 609 | .nav-trigger:checked + label { 610 | -webkit-transform: scale(0.75); 611 | transform: scale(0.75); 612 | } 613 | 614 | /* × and + */ 615 | .nav-trigger:checked + label.plus .navicon, 616 | .nav-trigger:checked + label.x .navicon { 617 | background: transparent; 618 | } 619 | 620 | .nav-trigger:checked + label.plus .navicon:before, 621 | .nav-trigger:checked + label.x .navicon:before { 622 | -webkit-transform: rotate(-45deg); 623 | transform: rotate(-45deg); 624 | background: #FFF; 625 | } 626 | 627 | .nav-trigger:checked + label.plus .navicon:after, 628 | .nav-trigger:checked + label.x .navicon:after { 629 | -webkit-transform: rotate(45deg); 630 | transform: rotate(45deg); 631 | background: #FFF; 632 | } 633 | 634 | .nav-trigger:checked + label.plus { 635 | -webkit-transform: scale(0.75) rotate(45deg); 636 | transform: scale(0.75) rotate(45deg); 637 | } 638 | 639 | .nav-trigger:checked ~ nav { 640 | left: 0 !important; 641 | } 642 | 643 | .nav-trigger:checked ~ .overlay { 644 | display: block; 645 | } 646 | 647 | .nav-trigger { 648 | position: fixed; 649 | top: 0; 650 | clip: rect(0, 0, 0, 0); 651 | } 652 | 653 | .overlay { 654 | display: none; 655 | position: fixed; 656 | top: 0; 657 | bottom: 0; 658 | left: 0; 659 | right: 0; 660 | width: 100%; 661 | height: 100%; 662 | background: hsla(0, 0%, 0%, 0.5); 663 | z-index: 1; 664 | } 665 | 666 | /* nav level */ 667 | .level-hide { 668 | display: none; 669 | } 670 | html[data-search-mode] .level-hide { 671 | display: block; 672 | } 673 | 674 | 675 | @media only screen and (max-width: 680px) { 676 | body { 677 | overflow-x: hidden; 678 | } 679 | 680 | nav { 681 | background: #FFF; 682 | width: 250px; 683 | height: 100%; 684 | position: fixed; 685 | top: 0; 686 | right: 0; 687 | bottom: 0; 688 | left: -250px; 689 | z-index: 3; 690 | padding: 0 10px; 691 | transition: left 0.2s; 692 | } 693 | 694 | .navicon-button { 695 | display: inline-block; 696 | position: fixed; 697 | top: 1.5em; 698 | right: 0; 699 | z-index: 2; 700 | } 701 | 702 | #main { 703 | width: 100%; 704 | } 705 | 706 | #main h1.page-title { 707 | margin: 1em 0; 708 | } 709 | 710 | #main section { 711 | padding: 0; 712 | } 713 | 714 | footer { 715 | margin-left: 0; 716 | } 717 | } 718 | 719 | /** Add a '#' to static members */ 720 | [data-type="member"] a::before { 721 | content: '#'; 722 | display: inline-block; 723 | margin-left: -14px; 724 | margin-right: 5px; 725 | } 726 | 727 | #disqus_thread{ 728 | margin-left: 30px; 729 | } 730 | 731 | @font-face { 732 | font-family: 'Montserrat'; 733 | font-style: normal; 734 | font-weight: 400; 735 | src: url('../fonts/Montserrat/Montserrat-Regular.eot'); /* IE9 Compat Modes */ 736 | src: url('../fonts/Montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 737 | url('../fonts/Montserrat/Montserrat-Regular.woff2') format('woff2'), /* Super Modern Browsers */ 738 | url('../fonts/Montserrat/Montserrat-Regular.woff') format('woff'), /* Pretty Modern Browsers */ 739 | url('../fonts/Montserrat/Montserrat-Regular.ttf') format('truetype'); /* Safari, Android, iOS */ 740 | } 741 | 742 | @font-face { 743 | font-family: 'Montserrat'; 744 | font-style: normal; 745 | font-weight: 700; 746 | src: url('../fonts/Montserrat/Montserrat-Bold.eot'); /* IE9 Compat Modes */ 747 | src: url('../fonts/Montserrat/Montserrat-Bold.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 748 | url('../fonts/Montserrat/Montserrat-Bold.woff2') format('woff2'), /* Super Modern Browsers */ 749 | url('../fonts/Montserrat/Montserrat-Bold.woff') format('woff'), /* Pretty Modern Browsers */ 750 | url('../fonts/Montserrat/Montserrat-Bold.ttf') format('truetype'); /* Safari, Android, iOS */ 751 | } 752 | 753 | @font-face { 754 | font-family: 'Source Sans Pro'; 755 | src: url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot'); 756 | src: url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot?#iefix') format('embedded-opentype'), 757 | url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2') format('woff2'), 758 | url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff') format('woff'), 759 | url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf') format('truetype'), 760 | url('../fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg#source_sans_proregular') format('svg'); 761 | font-weight: 400; 762 | font-style: normal; 763 | } 764 | 765 | @font-face { 766 | font-family: 'Source Sans Pro'; 767 | src: url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot'); 768 | src: url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot?#iefix') format('embedded-opentype'), 769 | url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2') format('woff2'), 770 | url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff') format('woff'), 771 | url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf') format('truetype'), 772 | url('../fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg#source_sans_prolight') format('svg'); 773 | font-weight: 300; 774 | font-style: normal; 775 | 776 | } 777 | -------------------------------------------------------------------------------- /docs/styles/prettify.css: -------------------------------------------------------------------------------- 1 | .pln { 2 | color: #ddd; 3 | } 4 | 5 | /* string content */ 6 | .str { 7 | color: #61ce3c; 8 | } 9 | 10 | /* a keyword */ 11 | .kwd { 12 | color: #fbde2d; 13 | } 14 | 15 | /* a comment */ 16 | .com { 17 | color: #aeaeae; 18 | } 19 | 20 | /* a type name */ 21 | .typ { 22 | color: #8da6ce; 23 | } 24 | 25 | /* a literal value */ 26 | .lit { 27 | color: #fbde2d; 28 | } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #ddd; 33 | } 34 | 35 | /* lisp open bracket */ 36 | .opn { 37 | color: #000000; 38 | } 39 | 40 | /* lisp close bracket */ 41 | .clo { 42 | color: #000000; 43 | } 44 | 45 | /* a markup tag name */ 46 | .tag { 47 | color: #8da6ce; 48 | } 49 | 50 | /* a markup attribute name */ 51 | .atn { 52 | color: #fbde2d; 53 | } 54 | 55 | /* a markup attribute value */ 56 | .atv { 57 | color: #ddd; 58 | } 59 | 60 | /* a declaration */ 61 | .dec { 62 | color: #EF5050; 63 | } 64 | 65 | /* a variable name */ 66 | .var { 67 | color: #c82829; 68 | } 69 | 70 | /* a function name */ 71 | .fun { 72 | color: #4271ae; 73 | } 74 | 75 | /* Specify class=linenums on a pre to get line numbering */ 76 | ol.linenums { 77 | margin-top: 0; 78 | margin-bottom: 0; 79 | padding-bottom: 2px; 80 | } 81 | -------------------------------------------------------------------------------- /examples/cancel_order_multi.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.DEBUG = '*' 4 | 5 | const debug = require('debug')('bfx:api:rest:examples:cancelordermulti') 6 | const { RESTv2 } = require('../') 7 | 8 | /** 9 | * populate apiKey and apiSecret 10 | */ 11 | const rest2 = new RESTv2({ 12 | apiKey: '', 13 | apiSecret: '', 14 | transform: true 15 | }) 16 | 17 | /** 18 | * Cancel orders using internal Order ID 19 | * you can obtain your order id's by calling rest2.activeOrders() 20 | */ 21 | const cancelOrdersById = async () => { 22 | const orderIDs = [123, 124] 23 | const response = await rest2.cancelOrderMulti({ 24 | id: orderIDs 25 | }) 26 | 27 | debug('Cancel orders by ID status: %s', response.status) 28 | debug('Cancel orders by ID message: %s', response.text) 29 | } 30 | 31 | /** 32 | * Cancel orders using client order id and client order id date 33 | * you can obtain your client order id's and client order id date by calling rest2.activeOrders() 34 | */ 35 | const cancelOrdersByClientOrderId = async () => { 36 | const clientOrderID = 7701 37 | const clientOrderIDDate = '2020-05-28' 38 | const response = await rest2.cancelOrderMulti({ 39 | cid: [ 40 | [clientOrderID, clientOrderIDDate] 41 | ] 42 | }) 43 | 44 | debug('Cancel orders by client order ID status: %s', response.status) 45 | debug('Cancel orders by client order ID message: %s', response.text) 46 | } 47 | 48 | /** 49 | * Cancel orders using group id 50 | * you can obtain your group id's by calling rest2.activeOrders() 51 | */ 52 | const cancelOrdersByGroupId = async () => { 53 | const groupIDs = [8800, 8801] 54 | const response = await rest2.cancelOrderMulti({ 55 | gid: groupIDs 56 | }) 57 | 58 | debug('Cancel orders by group ID status: %s', response.status) 59 | debug('Cancel orders by group ID message: %s', response.text) 60 | } 61 | 62 | /** 63 | * Multiple Operations Example 64 | * you can obtain your order, client order, group id's 65 | * and client order id date by calling rest2.activeOrders() 66 | */ 67 | const runMixMultiple = async () => { 68 | const orderIDs = [123] 69 | const groupIDs = [123] 70 | const clientOrderID = 7701 71 | const clientOrderIDDate = '2020-05-28' 72 | const response = await rest2.cancelOrderMulti({ 73 | id: orderIDs, 74 | gid: groupIDs, 75 | cid: [ 76 | [clientOrderID, clientOrderIDDate] 77 | ] 78 | }) 79 | 80 | debug('Mixed operations status: %s', response.status) 81 | debug('Mixed operations message: %s', response.text) 82 | } 83 | 84 | try { 85 | cancelOrdersById() 86 | cancelOrdersByGroupId() 87 | cancelOrdersByClientOrderId() 88 | runMixMultiple() 89 | } catch (error) { 90 | debug('error: %s', error.stack) 91 | } 92 | -------------------------------------------------------------------------------- /examples/candles.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.DEBUG = '*' 4 | 5 | const debug = require('debug')('bfx:api:rest:examples:candles') 6 | const { RESTv2 } = require('../') 7 | const rest = new RESTv2({ transform: true }) 8 | 9 | const SYMBOL = 'tBTCUSD' 10 | const TIME_FRAME = '1m' 11 | 12 | const run = async () => { 13 | const candles = await rest.candles({ 14 | timeframe: TIME_FRAME, 15 | symbol: SYMBOL, 16 | query: { 17 | start: Date.now() - (24 * 60 * 60 * 1000), 18 | end: Date.now(), 19 | limit: 1000 20 | } 21 | }) 22 | 23 | const [lastCandle] = candles 24 | 25 | debug('recv %d candles for %s %s', candles.length, SYMBOL, TIME_FRAME) 26 | debug('latest %s', JSON.stringify({ 27 | mts: new Date(lastCandle.mts).toLocaleString(), 28 | open: lastCandle.open, 29 | high: lastCandle.high, 30 | low: lastCandle.low, 31 | close: lastCandle.close, 32 | volume: lastCandle.volume 33 | }, null, 2)) 34 | } 35 | 36 | try { 37 | run() 38 | } catch (e) { 39 | debug('error: %s', e.stack) 40 | } 41 | -------------------------------------------------------------------------------- /examples/order_multi_op.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.DEBUG = '*' 4 | 5 | const { Order } = require('bfx-api-node-models') 6 | const debug = require('debug')('bfx:api:rest:examples:ordermultiop') 7 | const { RESTv2 } = require('../') 8 | 9 | /** 10 | * populate apiKey and apiSecret 11 | */ 12 | const rest2 = new RESTv2({ 13 | apiKey: '', 14 | apiSecret: '', 15 | transform: true 16 | }) 17 | 18 | /** 19 | * Order New Example 20 | * orders can be plain object literals: { foo: 'bar', ... } 21 | * or instances of Order class: new Order({ foo: 'bar', ... }) 22 | */ 23 | const runOrderNew = async () => { 24 | const ops = [ 25 | [ 26 | 'on', 27 | new Order({ 28 | type: 'EXCHANGE LIMIT', 29 | symbol: 'tBTCUSD', 30 | price: '13', 31 | amount: '0.001', 32 | gid: 7, 33 | cid: 8 34 | }) 35 | ], 36 | [ 37 | 'on', 38 | { 39 | type: 'EXCHANGE LIMIT', 40 | symbol: 'tBTCUSD', 41 | price: '9', 42 | amount: '0.001', 43 | gid: 7, 44 | cid: 8 45 | } 46 | ] 47 | ] 48 | const response = await rest2.orderMultiOp(ops) 49 | 50 | debug('Order new submit status: %s', response.status) 51 | debug('Order new submit message: %s', response.text) 52 | } 53 | 54 | /** 55 | * Order Cancel Example 56 | * you can obtain your order id's by calling rest2.activeOrders() 57 | */ 58 | const runOrderCancel = async () => { 59 | const orderID = 123 60 | const ops = [ 61 | ['oc', { id: orderID }] 62 | ] 63 | const response = await rest2.orderMultiOp(ops) 64 | 65 | debug('Order cancel status: %s', response.status) 66 | debug('Order cancel message: %s', response.text) 67 | } 68 | 69 | /** 70 | * Order Cancel Multi Example 71 | * you can obtain your order id's by calling rest2.activeOrders() 72 | */ 73 | const runOrderCancelMulti = async () => { 74 | const orderIDs = [123, 124] 75 | const ops = [ 76 | ['oc_multi', { id: orderIDs }] 77 | ] 78 | const response = await rest2.orderMultiOp(ops) 79 | 80 | debug('Order cancel multi status: %s', response.status) 81 | debug('Order cancel multi message: %s', response.text) 82 | } 83 | 84 | /** 85 | * Order Update Example 86 | * you can obtain your order id's by calling rest2.activeOrders() 87 | */ 88 | const runOrderUpdate = async () => { 89 | const orderID = 123 90 | const ops = [ 91 | ['ou', { id: orderID, price: '15', amount: '0.001' }] 92 | ] 93 | const response = await rest2.orderMultiOp(ops) 94 | 95 | debug('Order update status: %s', response.status) 96 | debug('Order update message: %s', response.text) 97 | } 98 | 99 | /** 100 | * Multiple Operations Example 101 | * you can obtain your order id's by calling rest2.activeOrders() 102 | */ 103 | const runMixMultiple = async () => { 104 | const orderID = 1189090779 105 | const orderOneID = 1189092193 106 | const orderTwoID = 1189092194 107 | const orderIDs = [1189092195, 1189092196] 108 | const ops = [ 109 | [ 110 | 'on', 111 | new Order({ 112 | type: 'EXCHANGE LIMIT', 113 | symbol: 'tBTCUSD', 114 | price: '13', 115 | amount: '0.001' 116 | }) 117 | ], 118 | ['oc', { id: orderOneID }], 119 | ['oc', { id: orderTwoID }], 120 | ['oc_multi', { id: orderIDs }], 121 | ['ou', { id: orderID, price: '8', amount: '0.001' }] 122 | ] 123 | const response = await rest2.orderMultiOp(ops) 124 | 125 | debug('Mixed operations status: %s', response.status) 126 | debug('Mixed operations message: %s', response.text) 127 | } 128 | 129 | try { 130 | runOrderNew() 131 | runOrderCancel() 132 | runOrderCancelMulti() 133 | runOrderUpdate() 134 | runMixMultiple() 135 | } catch (error) { 136 | debug('error: %s', error.stack) 137 | } 138 | -------------------------------------------------------------------------------- /examples/pay_invoices.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.DEBUG = '*' 4 | 5 | const debug = require('debug')('bfx:api:rest:examples:pay') 6 | const { RESTv2 } = require('../') 7 | 8 | /** 9 | * populate apiKey and apiSecret 10 | */ 11 | const rest2 = new RESTv2({ 12 | apiKey: '', 13 | apiSecret: '', 14 | transform: true 15 | }) 16 | 17 | const runInvoiceNew = async () => { 18 | const params = { 19 | amount: 2, 20 | currency: 'USD', 21 | payCurrencies: ['BTC', 'ETH', 'UST-ETH', 'LNX'], 22 | orderId: 'order123', 23 | duration: 86399, 24 | webhook: 'https://example.com/api/order/myORder12345', 25 | redirectUrl: 'https://example.com/order/myORder12345', 26 | customerInfo: { 27 | nationality: 'DE', 28 | residCountry: 'GB', 29 | residCity: 'London', 30 | residZipCode: 'WC2H 7NA', 31 | residStreet: '5-6 Leicester Square', 32 | fullName: 'John Doe', 33 | email: 'john@example.com' 34 | } 35 | } 36 | 37 | const response = await rest2.payInvoiceCreate(params) 38 | 39 | debug('Invoice: %s', JSON.stringify(response, null, 4)) 40 | } 41 | 42 | const runInvoiceList = async () => { 43 | const response = await rest2.payInvoiceList({ 44 | limit: 1, 45 | start: Date.now() - 60 * 24 * 100 46 | }) 47 | 48 | debug('Invoices: %s', JSON.stringify(response, null, 4)) 49 | } 50 | 51 | const main = async () => { 52 | try { 53 | await runInvoiceNew() 54 | await runInvoiceList() 55 | } catch (error) { 56 | debug('error: %s', error.stack) 57 | } 58 | } 59 | 60 | main() 61 | -------------------------------------------------------------------------------- /examples/trades.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.DEBUG = '*' 4 | 5 | const debug = require('debug')('bfx:api:rest:examples:trades') 6 | const { RESTv2 } = require('../') 7 | const rest = new RESTv2({ transform: true }) 8 | 9 | const SYMBOL = 'tBTCUSD' 10 | 11 | const run = async () => { 12 | const trades = await rest.trades(SYMBOL) 13 | const [lastTrade] = trades 14 | 15 | debug('recv %d trades for %s', trades.length, SYMBOL) 16 | debug('last %s', JSON.stringify({ 17 | mts: new Date(lastTrade.mts).toLocaleString(), 18 | price: lastTrade.price, 19 | amount: lastTrade.amount, 20 | id: lastTrade.id 21 | }, null, 2)) 22 | } 23 | 24 | try { 25 | run() 26 | } catch (e) { 27 | debug('error: %s', e.stack) 28 | } 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | RESTv1: require('./lib/rest1'), 5 | RESTv2: require('./lib/rest2') 6 | } 7 | -------------------------------------------------------------------------------- /lib/rest1.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* eslint-disable */ 3 | /* legacy API interface, not cleaned up with new eslint rules */ 4 | 5 | const fetch = require('node-fetch') 6 | const debug = require('debug')('bfx:rest1') 7 | const { genAuthSig, nonce } = require('bfx-api-node-util') 8 | const API_URL = 'https://api.bitfinex.com' 9 | 10 | /** 11 | * Communicates with v1 of the Bitfinex HTTP API 12 | */ 13 | class RESTv1 { 14 | /** 15 | * Instantiate a new REST v1 transport. 16 | * 17 | * @param {Object} opts 18 | * @param {string?} opts.apiKey 19 | * @param {string?} opts.apiSecret 20 | * @param {string?} opts.url - endpoint URL 21 | * @param {Object?} opts.agent - optional node agent for connection (proxy) 22 | * @param {Method?} opts.nonceGenerator - optional, should return a nonce 23 | */ 24 | constructor (opts = {}) { 25 | this._url = opts.url || API_URL 26 | this._apiKey = opts.apiKey || '' 27 | this._apiSecret = opts.apiSecret || '' 28 | this._agent = opts.agent 29 | this._generateNonce = (typeof opts.nonceGenerator === 'function') 30 | ? opts.nonceGenerator 31 | : nonce 32 | } 33 | 34 | /** 35 | * @param {string} body - raw JSON 36 | * @param {Method} cb 37 | * @private 38 | */ 39 | _parse_req_body (result, cb) { 40 | if (typeof result.message === 'string') { 41 | if (result.message.indexOf('Nonce is too small') !== -1) { 42 | result.message += ' See https://github.com/bitfinexcom/bitfinex-api-node/blob/master/README.md#nonce-too-small for help' 43 | } 44 | 45 | return cb(new Error(result.message)) 46 | } 47 | 48 | return cb(null, result) 49 | } 50 | 51 | /** 52 | * @param {string} path 53 | * @param {Object} params 54 | * @param {Method} cb 55 | * @private 56 | */ 57 | async make_request (path, params, cb) { 58 | if (!this._apiKey || !this._apiSecret) { 59 | return cb(new Error('missing api key or secret')) 60 | } 61 | if (!path) { 62 | return cb(new Error('path is missing')) 63 | } 64 | 65 | const payload = Object.assign({ 66 | request: `/v1/${path}`, 67 | nonce: JSON.stringify(this._generateNonce()) 68 | }, params) 69 | 70 | const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString('base64') 71 | const { sig } = genAuthSig(this._apiSecret, payloadBase64) 72 | const url = `${this._url}/v1/${path}` 73 | 74 | debug('POST %s', url) 75 | 76 | const reqOpts = { 77 | method: 'POST', 78 | timeout: 15000, 79 | agent: this._agent, 80 | headers: { 81 | 'X-BFX-APIKEY': this._apiKey, 82 | 'X-BFX-PAYLOAD': payloadBase64, 83 | 'X-BFX-SIGNATURE': sig 84 | } 85 | } 86 | 87 | try { 88 | const resp = await fetch(url, reqOpts) 89 | if (!resp.ok && +resp.status !== 400) { 90 | throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`) 91 | } 92 | const json = await resp.json() 93 | return this._parse_req_body(json, cb) 94 | } catch (err) { 95 | return cb(err) 96 | } 97 | } 98 | 99 | /** 100 | * @param {string} path 101 | * @param {Method} cb 102 | * @private 103 | */ 104 | async make_public_request (path, cb) { 105 | if (!path) { 106 | return cb(new Error('path is missing')) 107 | } 108 | 109 | const url = `${this._url}/v1/${path}` 110 | 111 | debug('GET %s', url) 112 | 113 | const reqOpts = { 114 | method: 'GET', 115 | agent: this._agent, 116 | timeout: 15000 117 | } 118 | 119 | try { 120 | const resp = await fetch(url, reqOpts) 121 | if (!resp.ok && +resp.status !== 400) { 122 | throw new Error(`HTTP code ${resp.status} ${resp.statusText || ''}`) 123 | } 124 | const json = await resp.json() 125 | return this._parse_req_body(json, cb) 126 | } catch (err) { 127 | return cb(err) 128 | } 129 | } 130 | 131 | /** 132 | * @param {string} symbol 133 | * @param {Method} cb 134 | * @see https://docs.bitfinex.com/v1/reference#rest-public-ticker 135 | */ 136 | ticker (symbol = 'BTCUSD', cb) { 137 | if (!cb) { 138 | cb = (err, data) => { 139 | if (err) { 140 | console.error(err) 141 | } 142 | 143 | console.log(data) 144 | } 145 | } 146 | 147 | return this.make_public_request(`pubticker/${symbol}`, cb) 148 | } 149 | 150 | /** 151 | * @param {string} symbol 152 | * @param {Method} cb 153 | */ 154 | today (symbol, cb) { 155 | return this.make_public_request(`today/${symbol}`, cb) 156 | } 157 | 158 | /** 159 | * @param {string} symbol 160 | * @param {Method} cb 161 | * @see https://docs.bitfinex.com/v1/reference#rest-public-stats 162 | */ 163 | stats (symbol, cb) { 164 | return this.make_public_request(`stats/${symbol}`, cb) 165 | } 166 | 167 | /** 168 | * @param {string} currency 169 | * @param {Object} options 170 | * @param {Method} cb 171 | * @see https://docs.bitfinex.com/v1/reference#rest-public-fundingbook 172 | */ 173 | fundingbook (currency, options, cb) { 174 | let uri = `lendbook/${currency}` 175 | 176 | if (typeof options === 'function') { 177 | cb = options 178 | } else { 179 | const keys = Object.keys(options) 180 | 181 | for (let i = 0; i < keys.length; i++) { 182 | uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}` 183 | } 184 | } 185 | 186 | return this.make_public_request(uri, cb) 187 | } 188 | 189 | /** 190 | * @param {string} symbol 191 | * @param {Object} options 192 | * @param {Method} cb 193 | * @see https://docs.bitfinex.com/v1/reference#rest-public-orderbook 194 | */ 195 | orderbook (symbol, options, cb) { 196 | let uri = `book/${symbol}` 197 | 198 | if (typeof options === 'function') { 199 | cb = options 200 | } else { 201 | const keys = Object.keys(options) 202 | 203 | for (let i = 0; i < keys.length; i++) { 204 | uri += `${i === 0 ? '/?' : '&'}${keys[i]}=${options[keys[i]]}` 205 | } 206 | } 207 | 208 | return this.make_public_request(uri, cb) 209 | } 210 | 211 | /** 212 | * @param {string} symbol 213 | * @param {Method} cb 214 | * @see https://docs.bitfinex.com/v1/reference#rest-public-trades 215 | */ 216 | trades (symbol, cb) { 217 | return this.make_public_request('trades/' + symbol, cb) 218 | } 219 | 220 | /** 221 | * @param {string} symbol 222 | * @param {Method} cb 223 | * @see https://docs.bitfinex.com/v1/reference#rest-public-lends 224 | */ 225 | lends (currency, cb) { 226 | return this.make_public_request('lends/' + currency, cb) 227 | } 228 | 229 | /** 230 | * @param {Method} cb 231 | * @see https://docs.bitfinex.com/v1/reference#rest-public-symbols 232 | */ 233 | get_symbols (cb) { 234 | return this.make_public_request('symbols', cb) 235 | } 236 | 237 | /** 238 | * @param {Method} cb 239 | * @see https://docs.bitfinex.com/v1/reference#rest-public-symbol-details 240 | */ 241 | symbols_details (cb) { 242 | return this.make_public_request('symbols_details', cb) 243 | } 244 | 245 | /** 246 | * @param {string} symbol 247 | * @param {number} amount 248 | * @param {number} price 249 | * @param {string} exchange 250 | * @param {string} side 251 | * @param {string} type 252 | * @param {boolean} is_hidden 253 | * @param {boolean} postOnly 254 | * @param {Method} cb 255 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-order 256 | */ 257 | new_order (symbol, amount, price, exchange, side, type, is_hidden, postOnly, cb) { 258 | if (typeof is_hidden === 'function') { 259 | cb = is_hidden 260 | is_hidden = false 261 | } 262 | 263 | if (typeof postOnly === 'function') { 264 | cb = postOnly 265 | postOnly = false 266 | } 267 | 268 | const params = { 269 | symbol, 270 | amount, 271 | price, 272 | exchange, 273 | side, 274 | type 275 | } 276 | 277 | if (postOnly) params.post_only = true 278 | if (is_hidden) params.is_hidden = true 279 | 280 | return this.make_request('order/new', params, cb) 281 | } 282 | 283 | /** 284 | * @param {Object[]} orders 285 | * @param {Method} cb 286 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-multiple-new-orders 287 | */ 288 | multiple_new_orders (orders, cb) { 289 | return this.make_request('order/new/multi', { orders }, cb) 290 | } 291 | 292 | /** 293 | * @param {number} order_id 294 | * @param {Method} cb 295 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-order 296 | */ 297 | cancel_order (order_id, cb) { 298 | return this.make_request('order/cancel', { 299 | order_id: parseInt(order_id) 300 | }, cb) 301 | } 302 | 303 | /** 304 | * @param {Method} cb 305 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-all-orders 306 | */ 307 | cancel_all_orders (cb) { 308 | return this.make_request('order/cancel/all', {}, cb) 309 | } 310 | 311 | /** 312 | * @param {number[]} order_ids 313 | * @param {Method} cb 314 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-multiple-orders 315 | */ 316 | cancel_multiple_orders (order_ids, cb) { 317 | return this.make_request('order/cancel/multi', { 318 | order_ids: order_ids.map(id => parseInt(id)) 319 | }, cb) 320 | } 321 | 322 | /** 323 | * @param {number} order_id 324 | * @param {string} symbol 325 | * @param {number} amount 326 | * @param {number} price 327 | * @param {string} exchange 328 | * @param {string} side 329 | * @param {string} type 330 | * @param {Method} cb 331 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-replace-order 332 | */ 333 | replace_order (order_id, symbol, amount, price, exchange, side, type, cb) { 334 | return this.make_request('order/cancel/replace', { 335 | order_id: parseInt(order_id), 336 | symbol, 337 | amount, 338 | price, 339 | exchange, 340 | side, 341 | type 342 | }, cb) 343 | } 344 | 345 | /** 346 | * @param {string} order_id 347 | * @param {Method} cb 348 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-order-status 349 | */ 350 | order_status (order_id, cb) { 351 | return this.make_request('order/status', { 352 | order_id: parseInt(order_id) 353 | }, cb) 354 | } 355 | 356 | /** 357 | * @param {Method} cb 358 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-orders 359 | */ 360 | active_orders (cb) { 361 | return this.make_request('orders', {}, cb) 362 | } 363 | 364 | /** 365 | * @param {Method} cb 366 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-orders-history 367 | */ 368 | orders_history (cb) { 369 | return this.make_request('orders/hist', {}, cb) 370 | } 371 | 372 | /** 373 | * @param {Method} cb 374 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-positions 375 | */ 376 | active_positions (cb) { 377 | return this.make_request('positions', {}, cb) 378 | } 379 | 380 | /** 381 | * @param {string} position_id 382 | * @param {number} amount 383 | * @param {Method} cb 384 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-claim-position 385 | */ 386 | claim_position (position_id, amount, cb) { 387 | return this.make_request('position/claim', { 388 | position_id: parseInt(position_id), 389 | amount 390 | }, cb) 391 | } 392 | 393 | /** 394 | * @param {string} currency 395 | * @param {Object} options 396 | * @param {Method} cb 397 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-balance-history 398 | */ 399 | balance_history (currency, options, cb) { 400 | const params = { currency } 401 | 402 | if (typeof options === 'function') { 403 | cb = options 404 | } else if (options && options.constructor.name === 'Object') { 405 | Object.assign(params, options) 406 | } 407 | 408 | return this.make_request('history', params, cb) 409 | } 410 | 411 | /** 412 | * @param {string} currency 413 | * @param {Object} options 414 | * @param {Method} cb 415 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit-withdrawal-history 416 | */ 417 | movements (currency, options, cb) { 418 | const params = { currency } 419 | 420 | if (typeof options === 'function') { 421 | cb = options 422 | } else if (options && options.constructor.name === 'Object') { 423 | Object.assign(params, options) 424 | } 425 | 426 | return this.make_request('history/movements', params, cb) 427 | } 428 | 429 | /** 430 | * @param {string} symbol 431 | * @param {Object} options 432 | * @param {Method} cb 433 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-past-trades 434 | */ 435 | past_trades (symbol, options, cb) { 436 | const params = { symbol } 437 | 438 | if (typeof options === 'function') { 439 | cb = options 440 | } else if (options && options.constructor.name === 'Object') { 441 | Object.assign(params, options) 442 | } 443 | 444 | return this.make_request('mytrades', params, cb) 445 | } 446 | 447 | /** 448 | * @param {string} currency 449 | * @param {string} method 450 | * @param {string} wallet_name 451 | * @param {Method} cb 452 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-deposit 453 | */ 454 | new_deposit (currency, method, wallet_name, cb) { 455 | return this.make_request('deposit/new', { 456 | currency, 457 | method, 458 | wallet_name 459 | }, cb) 460 | } 461 | 462 | /** 463 | * @param {string} currency 464 | * @param {number} amount 465 | * @param {number} rate 466 | * @param {number} period 467 | * @param {string} direction 468 | * @param {Method} cb 469 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-new-offer 470 | */ 471 | new_offer (currency, amount, rate, period, direction, cb) { 472 | return this.make_request('offer/new', { 473 | currency, 474 | amount, 475 | rate, 476 | period, 477 | direction 478 | }, cb) 479 | } 480 | 481 | /** 482 | * @param {string} offer_id 483 | * @param {Method} cb 484 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-cancel-offer 485 | */ 486 | cancel_offer (offer_id, cb) { 487 | return this.make_request('offer/cancel', { 488 | offer_id: parseInt(offer_id) 489 | }, cb) 490 | } 491 | 492 | /** 493 | * @param {string} offer_id 494 | * @param {Method} cb 495 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-offer-status 496 | */ 497 | offer_status (offer_id, cb) { 498 | return this.make_request('offer/status', { 499 | offer_id: parseInt(offer_id) 500 | }, cb) 501 | } 502 | 503 | /** 504 | * @param {Method} cb 505 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-offers 506 | */ 507 | active_offers (cb) { 508 | return this.make_request('offers', {}, cb) 509 | } 510 | 511 | /** 512 | * @param {Method} cb 513 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-credits 514 | */ 515 | active_credits (cb) { 516 | return this.make_request('credits', {}, cb) 517 | } 518 | 519 | /** 520 | * @param {Method} cb 521 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-wallet-balances 522 | */ 523 | wallet_balances (cb) { 524 | return this.make_request('balances', {}, cb) 525 | } 526 | 527 | /** 528 | * @param {Method} cb 529 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-active-funding-used-in-a-margin-position 530 | */ 531 | taken_swaps (cb) { 532 | return this.make_request('taken_funds', {}, cb) 533 | } 534 | 535 | /** 536 | * @param {Method} cb 537 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-total-taken-funds 538 | */ 539 | total_taken_swaps (cb) { 540 | return this.make_request('total_taken_funds', {}, cb) 541 | } 542 | 543 | /** 544 | * @param {string} swap_id 545 | * @param {Method} cb 546 | */ 547 | close_swap (swap_id, cb) { 548 | return this.make_request('swap/close', { 549 | swap_id: parseInt(swap_id) 550 | }, cb) 551 | } 552 | 553 | /** 554 | * @param {Method} cb 555 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-account-info 556 | */ 557 | account_infos (cb) { 558 | return this.make_request('account_infos', {}, cb) 559 | } 560 | 561 | /** 562 | * @param {Method} cb 563 | * @see https://docs.bitfinex.com/v1/reference#rest-auth-margin-information 564 | */ 565 | margin_infos (cb) { 566 | return this.make_request('margin_infos', {}, cb) 567 | } 568 | 569 | /** 570 | * POST /v1/withdraw 571 | * 572 | * @param {string} withdrawType "bitcoin", "litecoin", "darkcoin" or "mastercoin" 573 | * @param {string} walletSelected origin of the wallet to withdraw from, can be "trading", "exchange", or "deposit" 574 | * @param {number} amount amount to withdraw 575 | * @param {string} address destination address for withdrawal 576 | */ 577 | withdraw (withdrawType, walletSelected, amount, address, cb) { 578 | return this.make_request('withdraw', { 579 | withdrawType, 580 | walletSelected, 581 | amount, 582 | address 583 | }, cb) 584 | } 585 | 586 | /** 587 | * POST /v1/transfer 588 | * 589 | * @param {number} amount amount to transfer 590 | * @param {string} currency currency of funds to transfer 591 | * @param {string} walletFrom wallet to transfer from 592 | * @param {string} walletTo wallet to transfer to 593 | */ 594 | transfer (amount, currency, walletFrom, walletTo, cb) { 595 | return this.make_request('transfer', { 596 | amount, 597 | currency, 598 | walletFrom, 599 | walletTo 600 | }, cb) 601 | } 602 | } 603 | 604 | module.exports = RESTv1 605 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bfx-api-node-rest", 3 | "version": "6.0.0", 4 | "description": "Official Bitfinex REST v1 & v2 API interfaces", 5 | "engines": { 6 | "node": ">=8.3.0" 7 | }, 8 | "main": "./index.js", 9 | "husky": { 10 | "hooks": { 11 | "pre-commit": "npm test" 12 | } 13 | }, 14 | "scripts": { 15 | "lint": "standard", 16 | "lint:fix": "standard --fix", 17 | "test": "npm run lint && npm run unit", 18 | "unit": "NODE_ENV=test nyc --check-coverage --lines 45 --branches 19 --functions 39 --statements 44 --reporter=lcov --reporter=html mocha -b --recursive", 19 | "docs": "rm -rf docs && jsdoc --configure .jsdoc.json --verbose", 20 | "prepare": "husky install" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/bitfinexcom/bfx-api-node-rest.git" 25 | }, 26 | "keywords": [ 27 | "bitfinex", 28 | "bitcoin", 29 | "BTC" 30 | ], 31 | "author": "Bitfinex", 32 | "contributors": [ 33 | "Ezequiel Wernicke (https://www.bitfinex.com)", 34 | "Josh Rossi (https://www.bitfinex.com)", 35 | "Cris Mihalache (https://www.bitfinex.com)", 36 | "Robert Kowalski (https://www.bitfinex.com)", 37 | "Simone Poggi (https://www.bitfinex.com)", 38 | "Paolo Ardoino (https://www.bitfinex.com)", 39 | "Jacob Plaster (https://www.bitfinex.com)", 40 | "Abhishek Shrestha (https://www.bitfinex.com)", 41 | "Vigan Abdurrahmani (https://www.bitfinex.com)", 42 | "Sergio López (https://www.bitfinex.com)" 43 | ], 44 | "license": "MIT", 45 | "bugs": { 46 | "url": "https://github.com/bitfinexcom/bfx-api-node-rest/issues" 47 | }, 48 | "devDependencies": { 49 | "bfx-api-mock-srv": "^1.6.0", 50 | "chai": "^4.3.7", 51 | "chai-as-promised": "^7.1.1", 52 | "docdash": "^2.0.2", 53 | "husky": "^9.1.6", 54 | "jsdoc-to-markdown": "^9.0.1", 55 | "mocha": "^10.7.3", 56 | "nyc": "^17.0.0", 57 | "proxyquire": "^2.1.3", 58 | "sinon": "^19.0.2", 59 | "socks-proxy-agent": "^8.0.4", 60 | "standard": "^17.1.2" 61 | }, 62 | "dependencies": { 63 | "bfx-api-node-models": "^2.0.0", 64 | "bfx-api-node-util": "^1.0.10", 65 | "debug": "4.3.3", 66 | "dirty-chai": "^2.0.1", 67 | "lodash": "^4.17.15", 68 | "node-fetch": "^2.6.9" 69 | }, 70 | "standard": { 71 | "ignore": [ 72 | "/docs/**/*.js" 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/lib/rest-1-integration.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const PORT = 1339 5 | 6 | const assert = require('assert') 7 | const http = require('http') 8 | const RESTv1 = require('../../lib/rest1') 9 | 10 | describe('rest integration test', () => { 11 | let server = null 12 | 13 | afterEach((done) => { 14 | if (server) { 15 | server.close(() => { 16 | server = null 17 | done() 18 | }) 19 | } else { 20 | done() 21 | } 22 | }) 23 | 24 | it('should get the fundingbook asks, zero bids, 100 asks', (done) => { 25 | const opts = { limit_bids: 0, limit_asks: 10 } 26 | const bhttp = new RESTv1({ url: `http://localhost:${PORT}` }) 27 | 28 | const testResBody = [ 29 | '{"bids":[],"asks":[', 30 | '{"rate":"72.25","amount":"67.5","period":30,"timestamp":"1495565109.0","frr":"No"},', 31 | '{"rate":"72.2501","amount":"297.58737203","period":2,"timestamp":"1495565054.0","frr":"No"},', 32 | '{"rate":"72.2601","amount":"200.0","period":2,"timestamp":"1495565002.0","frr":"No"},', 33 | '{"rate":"72.9535","amount":"211.8299","period":2,"timestamp":"1495565037.0","frr":"No"},', 34 | '{"rate":"73.0","amount":"1319.59397488","period":30,"timestamp":"1495564972.0","frr":"No"},', 35 | '{"rate":"76.0827","amount":"1511.9115692","period":30,"timestamp":"1495564965.0","frr":"Yes"},', 36 | '{"rate":"76.0827","amount":"19849.81630455","period":30,"timestamp":"1495564972.0","frr":"Yes"},', 37 | '{"rate":"76.0827","amount":"1052.68464219","period":30,"timestamp":"1495564972.0","frr":"Yes"},', 38 | '{"rate":"109.5","amount":"55.33131648","period":3,"timestamp":"1495565103.0","frr":"No"},', 39 | '{"rate":"153.1999","amount":"134.86","period":2,"timestamp":"1495565106.0","frr":"No"}]}' 40 | ].join('') 41 | 42 | server = http.createServer((req, res) => { 43 | res.writeHead(200, { 'Content-Type': 'application/json' }) 44 | res.end(testResBody) 45 | }) 46 | 47 | server.listen(PORT, () => { 48 | bhttp.fundingbook('USD', opts, (err, data) => { 49 | if (err) throw err 50 | assert.ok(data) 51 | assert.strictEqual(data.bids.length, 0) 52 | assert.strictEqual(data.asks.length, 10) 53 | done() 54 | }) 55 | }) 56 | }) 57 | 58 | it('new_order -- post_only: postonly used and true', (done) => { 59 | const testResBody = '{}' 60 | const bhttp = new RESTv1({ 61 | apiKey: 'dummykey', 62 | apiSecret: 'dummysecret', 63 | url: `http://localhost:${PORT}` 64 | }) 65 | 66 | server = http.createServer((req, res) => { 67 | res.writeHead(200, { 'Content-Type': 'application/json' }) 68 | const payload = JSON.parse( 69 | Buffer.from(req.headers['x-bfx-payload'], 'base64').toString('ascii') 70 | ) 71 | 72 | assert.strictEqual(payload.post_only, true) 73 | res.end(testResBody) 74 | }) 75 | server.listen(PORT, () => { 76 | bhttp.new_order('btcusd', '0.1', '0.1', 'bitfinex', 'buy', 'exchange limit', false, true, (err, data) => { 77 | if (err) throw err 78 | done() 79 | }) 80 | }) 81 | }) 82 | 83 | it('new_order -- post_only: postonly not used and hidden true', (done) => { 84 | const testResBody = '{}' 85 | const bhttp = new RESTv1({ 86 | apiKey: 'dummykey', 87 | apiSecret: 'dummysecret', 88 | url: `http://localhost:${PORT}` 89 | }) 90 | 91 | server = http.createServer((req, res) => { 92 | res.writeHead(200, { 'Content-Type': 'application/json' }) 93 | const payload = JSON.parse( 94 | Buffer.from(req.headers['x-bfx-payload'], 'base64').toString('ascii') 95 | ) 96 | 97 | assert.strictEqual(payload.is_hidden, true) 98 | assert.strictEqual(payload.post_only, undefined) 99 | res.end(testResBody) 100 | }) 101 | 102 | server.listen(PORT, () => { 103 | bhttp.new_order('btcusd', '0.1', '0.1', 'bitfinex', 'buy', 'exchange limit', true, (err, data) => { 104 | if (err) throw err 105 | done() 106 | }) 107 | }) 108 | }) 109 | 110 | it('new_order -- post_only: postonly not used and hidden not used', (done) => { 111 | const testResBody = '{}' 112 | const bhttp = new RESTv1({ 113 | apiKey: 'dummykey', 114 | apiSecret: 'dummysecret', 115 | url: `http://localhost:${PORT}` 116 | }) 117 | 118 | server = http.createServer((req, res) => { 119 | res.writeHead(200, { 'Content-Type': 'application/json' }) 120 | const payload = JSON.parse( 121 | Buffer.from(req.headers['x-bfx-payload'], 'base64').toString('ascii') 122 | ) 123 | 124 | assert.strictEqual(payload.is_hidden, undefined) 125 | assert.strictEqual(payload.post_only, undefined) 126 | res.end(testResBody) 127 | }) 128 | server.listen(PORT, () => { 129 | bhttp.new_order('btcusd', '0.1', '0.1', 'bitfinex', 'buy', 'exchange limit', (err, data) => { 130 | if (err) throw err 131 | done() 132 | }) 133 | }) 134 | }) 135 | }) 136 | -------------------------------------------------------------------------------- /test/lib/rest-2-integration.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const assert = require('assert') 5 | const RESTv2 = require('../../lib/rest2') 6 | const { MockRESTv2Server } = require('bfx-api-mock-srv') 7 | const { Order } = require('bfx-api-node-models') 8 | 9 | const getTestREST2 = (args = {}) => { 10 | return new RESTv2({ 11 | apiKey: 'dummy', 12 | apiSecret: 'dummy', 13 | url: 'http://localhost:9999', 14 | ...args 15 | }) 16 | } 17 | 18 | const getTestFundingOffer = () => ([ 19 | 41215275, 'fUSD', 1524784806000, 1524784806000, 1000, 1000, 'FRRDELTAVAR', 20 | null, null, 0, 'ACTIVE', null, null, null, 0, 30, 0, 0, null, 0, 0.00207328 21 | ]) 22 | 23 | const getTestFundingLoan = () => ([ 24 | 2993678, 'fUSD', -1, 1524786468000, 1524786468000, 200, 0, 'ACTIVE', null, 25 | null, null, 0.002, 7, 1524786468000, 1524786468000, 0, 0, null, 0, 0.002, 0 26 | ]) 27 | 28 | const getTestFundingCredit = () => ([ 29 | 26190108, 'fUSD', -1, 1524846786000, 1524846786000, 32.91281465, 0, 'ACTIVE', 30 | null, null, null, 0.002, 7, 1524786468000, null, 0, 0, null, 0, 0.002, 0, null 31 | ]) 32 | 33 | const auditTestFundingOffer = (fo = {}) => { 34 | assert.strictEqual(fo.id, 41215275) 35 | assert.strictEqual(fo.symbol, 'fUSD') 36 | assert.strictEqual(fo.mtsCreate, 1524784806000) 37 | assert.strictEqual(fo.mtsUpdate, 1524784806000) 38 | assert.strictEqual(fo.amount, 1000) 39 | assert.strictEqual(fo.amountOrig, 1000) 40 | assert.strictEqual(fo.type, 'FRRDELTAVAR') 41 | assert.strictEqual(fo.flags, 0) 42 | assert.strictEqual(fo.status, 'ACTIVE') 43 | assert.strictEqual(fo.rate, 0) 44 | assert.strictEqual(fo.period, 30) 45 | assert.strictEqual(fo.notify, false) 46 | assert.strictEqual(fo.hidden, false) 47 | assert.strictEqual(fo.renew, false) 48 | assert.strictEqual(fo.rateReal, 0.00207328) 49 | } 50 | 51 | const auditTestFundingLoan = (fl = {}) => { 52 | assert.strictEqual(fl.id, 2993678) 53 | assert.strictEqual(fl.symbol, 'fUSD') 54 | assert.strictEqual(fl.side, -1) 55 | assert.strictEqual(fl.mtsCreate, 1524786468000) 56 | assert.strictEqual(fl.mtsUpdate, 1524786468000) 57 | assert.strictEqual(fl.amount, 200) 58 | assert.strictEqual(fl.flags, 0) 59 | assert.strictEqual(fl.status, 'ACTIVE') 60 | assert.strictEqual(fl.rate, 0.002) 61 | assert.strictEqual(fl.period, 7) 62 | assert.strictEqual(fl.mtsOpening, 1524786468000) 63 | assert.strictEqual(fl.mtsLastPayout, 1524786468000) 64 | assert.strictEqual(fl.notify, false) 65 | assert.strictEqual(fl.hidden, false) 66 | assert.strictEqual(fl.renew, false) 67 | assert.strictEqual(fl.rateReal, 0.002) 68 | assert.strictEqual(fl.noClose, false) 69 | } 70 | 71 | const auditTestFundingCredit = (fc = {}) => { 72 | assert.strictEqual(fc.id, 26190108) 73 | assert.strictEqual(fc.symbol, 'fUSD') 74 | assert.strictEqual(fc.side, -1) 75 | assert.strictEqual(fc.mtsCreate, 1524846786000) 76 | assert.strictEqual(fc.mtsUpdate, 1524846786000) 77 | assert.strictEqual(fc.amount, 32.91281465) 78 | assert.strictEqual(fc.flags, 0) 79 | assert.strictEqual(fc.status, 'ACTIVE') 80 | assert.strictEqual(fc.rate, 0.002) 81 | assert.strictEqual(fc.period, 7) 82 | assert.strictEqual(fc.mtsOpening, 1524786468000) 83 | assert.strictEqual(fc.mtsLastPayout, null) 84 | assert.strictEqual(fc.notify, false) 85 | assert.strictEqual(fc.hidden, false) 86 | assert.strictEqual(fc.renew, false) 87 | assert.strictEqual(fc.rateReal, 0.002) 88 | assert.strictEqual(fc.noClose, false) 89 | assert.strictEqual(fc.positionPair, null) 90 | } 91 | 92 | describe('RESTv2 integration (mock server) tests', () => { 93 | let srv = null 94 | 95 | afterEach(async () => { 96 | if (srv) { 97 | await srv.close() 98 | } 99 | 100 | srv = null 101 | }) 102 | 103 | // [rest2MethodName, finalMockResponseKey, rest2MethodArgs] 104 | const methods = [ 105 | // public 106 | ['ticker', 'ticker.tBTCUSD', [{ symbol: 'tBTCUSD' }]], 107 | ['tickers', 'tickers', [{ symbols: ['tBTCUSD', 'tETHUSD'] }]], 108 | ['tickersHistory', 'tickers_hist', [{ symbols: ['tBTCUSD', 'tETHUSD'], start: 'start', end: 'end', limit: 'limit' }]], 109 | ['liquidations', 'liquidations.start.end.limit.sort', [{ start: 'start', end: 'end', limit: 'limit', sort: 'sort' }]], 110 | ['stats', 'stats.key.context', [{ key: 'key', context: 'context' }]], 111 | ['candles', 'candles.trade:30m:tBTCUSD.hist', [{ timeframe: '30m', symbol: 'tBTCUSD', section: 'hist' }]], 112 | ['statusMessages', 'status_messages.deriv.ALL', [{ type: 'deriv', key: ['ALL'] }]], 113 | // mocking server not really aware of public post requests so commenting out for now 114 | // ['marketAveragePrice', 'market_average_price.fUSD.100', [{ symbol: 'fUSD', amount: 100 }]], 115 | 116 | // private 117 | ['alertList', 'alerts.price', [{ type: 'price' }]], 118 | ['alertSet', 'alert_set.type.symbol.price', [{ type: 'type', symbol: 'symbol', price: 'price' }]], 119 | ['alertDelete', 'alert_del.symbol.price', [{ symbol: 'symbol', price: 'price' }]], 120 | ['accountTrades', 'trades.BTCUSD.0.10.50.1', [{ symbol: 'BTCUSD', start: 0, end: 10, limit: 50, sort: 1 }]], 121 | ['wallets', 'wallets'], 122 | ['logins', 'logins_hist', [{ start: 'start', end: 'end', limit: 'limit' }]], 123 | ['changeLogs', 'change_log', [{ start: 'start', end: 'end', limit: 'limit' }]], 124 | ['walletsHistory', 'wallets_hist.end.currency', [{ end: 'end', currency: 'currency' }]], 125 | ['activeOrders', 'active_orders', [{}]], 126 | ['orderHistory', 'orders.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]], 127 | ['positions'], 128 | ['positionsHistory', 'positions_hist.start.end.limit', [{ start: 'start', end: 'end', limit: 'limit' }]], 129 | ['positionsSnapshot', 'positions_snap.start.end.limit', [{ start: 'start', end: 'end', limit: 'limit' }]], 130 | ['positionsAudit', 'positions_audit.id.start.end.limit', [{ id: 'id', start: 'start', end: 'end', limit: 'limit' }]], 131 | ['fundingOffers', 'f_offers.sym', [{ symbol: 'sym' }]], 132 | ['fundingOfferHistory', 'f_offer_hist.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]], 133 | ['fundingLoans', 'f_loans.sym', [{ symbol: 'sym' }]], 134 | ['fundingLoanHistory', 'f_loan_hist.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]], 135 | ['fundingCredits', 'f_credits.sym', [{ symbol: 'sym' }]], 136 | ['fundingCreditHistory', 'f_credit_hist.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]], 137 | ['fundingTrades', 'f_trade_hist.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]], 138 | ['marginInfo', 'margin_info.k', [{ key: 'k' }]], 139 | ['fundingInfo', 'f_info.k', [{ key: 'k' }]], 140 | ['derivsPositionCollateralSet', 'derivs_pos_col_set.symbol.collateral', [{ symbol: 'symbol', collateral: 'collateral' }]], 141 | ['performance'], 142 | ['calcAvailableBalance', 'calc.sym.dir.rate.type', [{ symbol: 'sym', dir: 'dir', rate: 'rate', type: 'type' }]], 143 | ['generateInvoice', 'generate_invoice.LNX.wallet.amount', [{ currency: 'LNX', wallet: 'wallet', amount: 'amount' }]], 144 | ['keepFunding', 'keep_funding.type.id', [{ type: 'type', id: 'id' }]], 145 | ['cancelOrderMulti', 'cancel_order_multi.123', [{ id: [123] }]], 146 | ['orderMultiOp', 'order_multi_op', [{ ops: [[]] }]], 147 | ['accountSummary', 'account_summary', [{}]], 148 | ['symbolDetails', 'info_pairs', [{}]], 149 | ['submitOrder', 'order_submit', [{ order: new Order({ type: 'EXCHANGE_MARKET', symbol: 'tBTCUST', price: 17832, amount: 0.3 }) }]], 150 | ['keyPermissions', 'auth_permissions', [{}]], 151 | ['payInvoiceCreate', 'invoice_submit', [{}]], 152 | ['payInvoiceList', 'invoice_list', [{}]], 153 | ['payInvoiceComplete', 'invoice_complete', [{}]], 154 | ['payCurrencyConversionList', 'pay_settings_convert_list', []], 155 | ['payAddCurrencyConversion', 'pay_settings_convert_create', [{}]], 156 | ['payRemoveCurrencyConversion', 'pay_settings_convert_remove', [{}]], 157 | ['payMerchantDailyLimit', 'pay_settings_daily_limit', [{}]], 158 | ['payMerchantSettingsWrite', 'pay_settings_set', [{}]], 159 | ['payMerchantSettingsWriteBatch', 'pay_settings_set_batch', [{}]], 160 | ['payMerchantSettingsRead', 'pay_settings_get', [{}]], 161 | ['payMerchantSettingsList', 'pay_settings_list', [{}]], 162 | ['getCoreSettings', 'core_settings', [[]]], 163 | ['invalidateAuthToken', 'delete_token', [[{}]]], 164 | ['payDepositsUnlinked', 'deposits_unlinked', [{}]], 165 | ['movementInfo', 'movement_info.id', [{ id: 'id' }]], 166 | ['payRefundInvoice', 'pay_invoice_refund', [{}]], 167 | ['payInvoiceMarkRefunded', 'pay_invoice_mark_refunded', [{}]], 168 | ['payInvoiceEvents', 'pay_invoice_events', [{}]], 169 | ['payPublicInvoiceCurrencyDetailed', 'pay_public_invoice_currency_detailed', [{}]], 170 | ['payInvoiceCurrencyDetailed', 'pay_currency_detailed', [{}]], 171 | ['payCurrencyList', 'pay_currency_list', [{}]], 172 | ['payInvoiceUpdate', 'pay_invoice_update', [{}]], 173 | ['payInvoiceCreatePos', 'pay_invoice_create_pos', [{}]], 174 | ['getWeightedAverages', 'weighted_averages.sym.start.end.limit', [{ symbol: 'sym', start: 'start', end: 'end', limit: 'limit' }]] 175 | ] 176 | 177 | methods.forEach((m) => { 178 | const name = m[0] 179 | const dataKey = m[1] || m[0] 180 | const args = m[2] || [{}] 181 | 182 | it(`${name}: fetches expected data`, (done) => { 183 | srv = new MockRESTv2Server({ listen: true }) 184 | const r = getTestREST2() 185 | srv.setResponse(dataKey, [42]) 186 | 187 | args.push((err, res) => { 188 | if (err) { 189 | return done(err) 190 | } 191 | 192 | assert.deepStrictEqual(res, [42]) 193 | done() 194 | }) 195 | 196 | r[name].apply(r, args) 197 | }) 198 | }) 199 | 200 | it('correctly parses funding offer response', async () => { 201 | srv = new MockRESTv2Server({ listen: true }) 202 | const r = getTestREST2({ transform: true }) 203 | 204 | srv.setResponse('f_offers.fUSD', [getTestFundingOffer()]) 205 | 206 | const [fo] = await r.fundingOffers({ symbol: 'fUSD' }) 207 | auditTestFundingOffer(fo) 208 | }) 209 | 210 | it('correctly parses funding loan response', async () => { 211 | srv = new MockRESTv2Server({ listen: true }) 212 | const r = getTestREST2({ transform: true }) 213 | 214 | srv.setResponse('f_loans.fUSD', [getTestFundingLoan()]) 215 | 216 | const [fo] = await r.fundingLoans({ symbol: 'fUSD' }) 217 | auditTestFundingLoan(fo) 218 | }) 219 | 220 | it('correctly parses funding credit response', async () => { 221 | srv = new MockRESTv2Server({ listen: true }) 222 | const r = getTestREST2({ transform: true }) 223 | 224 | srv.setResponse('f_credits.fUSD', [getTestFundingCredit()]) 225 | 226 | const [fc] = await r.fundingCredits({ symbol: 'fUSD' }) 227 | auditTestFundingCredit(fc) 228 | }) 229 | 230 | it('correctly parses accountSummary response', async () => { 231 | srv = new MockRESTv2Server({ listen: true }) 232 | const r = getTestREST2({ transform: true }) 233 | 234 | srv.setResponse('account_summary', [ 235 | null, null, null, null, 236 | [ 237 | [0.001, 0.001, 0.001, null, null, -0.0002], 238 | [0.002, 0.0021, 0.0022, null, null, 0.00075] 239 | ], 240 | null, null, null, null, 241 | { leo_lev: 0, leo_amount_avg: 0.002 } 242 | ]) 243 | 244 | const summary = await r.accountSummary() 245 | assert.strictEqual(summary.makerFee, 0.001) 246 | assert.strictEqual(summary.derivMakerRebate, -0.0002) 247 | assert.strictEqual(summary.takerFeeToCrypto, 0.002) 248 | assert.strictEqual(summary.takerFeeToStable, 0.0021) 249 | assert.strictEqual(summary.takerFeeToFiat, 0.0022) 250 | assert.strictEqual(summary.derivTakerFee, 0.00075) 251 | assert.strictEqual(summary.leoLev, 0) 252 | assert.strictEqual(summary.leoAmountAvg, 0.002) 253 | }) 254 | 255 | it('correctly parses symbolDetails response', async () => { 256 | srv = new MockRESTv2Server({ listen: true }) 257 | const r = getTestREST2({ transform: true }) 258 | 259 | srv.setResponse('info_pairs', [ 260 | [ 261 | ['BTCUSD', [null, null, null, '0.0002', '2002.0', null, null, null, 0.2, 0.1]], 262 | ['BTCEUR', [null, null, null, '0.00021', '2000.0', null, null, null, null, null]] 263 | ] 264 | ]) 265 | 266 | const details = await r.symbolDetails() 267 | assert.strictEqual(details[0].pair, 'BTCUSD') 268 | assert.strictEqual(details[0].initialMargin, 0.2) 269 | assert.strictEqual(details[0].minimumMargin, 0.1) 270 | assert.strictEqual(details[0].maximumOrderSize, '2002.0') 271 | assert.strictEqual(details[0].minimumOrderSize, '0.0002') 272 | assert.strictEqual(details[0].margin, true) 273 | 274 | assert.strictEqual(details[1].pair, 'BTCEUR') 275 | assert.strictEqual(details[1].initialMargin, null) 276 | assert.strictEqual(details[1].minimumMargin, null) 277 | assert.strictEqual(details[1].maximumOrderSize, '2000.0') 278 | assert.strictEqual(details[1].minimumOrderSize, '0.00021') 279 | assert.strictEqual(details[1].margin, false) 280 | }) 281 | 282 | it('correctly parses keyPermissions response', async () => { 283 | srv = new MockRESTv2Server({ listen: true }) 284 | const r = getTestREST2({ transform: true }) 285 | 286 | srv.setResponse('auth_permissions', [ 287 | ['account', 0, 0], 288 | ['orders', 1, 0], 289 | ['funding', 0, 1], 290 | ['settings', 1, 1] 291 | ]) 292 | 293 | const perms = await r.keyPermissions() 294 | assert.strictEqual(perms[0].key, 'account') 295 | assert.strictEqual(perms[0].read, false) 296 | assert.strictEqual(perms[0].write, false) 297 | 298 | assert.strictEqual(perms[1].key, 'orders') 299 | assert.strictEqual(perms[1].read, true) 300 | assert.strictEqual(perms[1].write, false) 301 | 302 | assert.strictEqual(perms[2].key, 'funding') 303 | assert.strictEqual(perms[2].read, false) 304 | assert.strictEqual(perms[2].write, true) 305 | 306 | assert.strictEqual(perms[3].key, 'settings') 307 | assert.strictEqual(perms[3].read, true) 308 | assert.strictEqual(perms[3].write, true) 309 | }) 310 | 311 | it('correctly executes closePosition method', async () => { 312 | srv = new MockRESTv2Server({ listen: true }) 313 | const r = getTestREST2({ transform: true }) 314 | 315 | const orderRes = [ 316 | 54866913445, null, 1607956105914, 'tBTCUST', 317 | 1607956106828, 1607956106828, -20, -20, 'MARKET', null, 318 | null, null, 512, 'ACTIVE (note:POSCLOSE)', null, null, 319 | 19107, 0, 0, 0, null, null, null, 0, 0, null, null, null, 320 | 'API>BFX', null, null, null 321 | ] 322 | 323 | srv.setResponse('positions', [ 324 | [ 325 | 'tBTCUST', 'ACTIVE', 20, 19112.589492392, 0, 0, -896.029847839973, -0.034477234990170615, 326 | 13768.237954491486, 1.757758329330831, null, 145287699, null, null, null, 0, null, 0, 0, 327 | { 328 | reason: 'TRADE', 329 | order_id: 54864866189, 330 | order_id_oppo: 54866447371, 331 | liq_stage: null, 332 | trade_price: '19119.0', 333 | trade_amount: '0.59455843' 334 | } 335 | ] 336 | ]) 337 | 338 | srv.setResponse('order_submit', [ 339 | 1567590617.442, 'on-req', 1235, null, [orderRes], 340 | null, 'SUCCESS', 'Submitting1 orders.' 341 | ]) 342 | 343 | const order = await r.closePosition({ position_id: 145287699 }) 344 | assert.deepStrictEqual(order.toJS(), new Order(orderRes).toJS()) 345 | }) 346 | 347 | it('correctly executes closePosition method without transform', async () => { 348 | srv = new MockRESTv2Server({ listen: true }) 349 | const r = getTestREST2({ transform: false }) 350 | 351 | const orderRes = [ 352 | 54866913445, null, 1607956105914, 'tBTCUST', 353 | 1607956106828, 1607956106828, -20, -20, 'MARKET', null, 354 | null, null, 512, 'ACTIVE (note:POSCLOSE)', null, null, 355 | 19107, 0, 0, 0, null, null, null, 0, 0, null, null, null, 356 | 'API>BFX', null, null, null 357 | ] 358 | 359 | srv.setResponse('positions', [ 360 | [ 361 | 'tBTCUST', 'ACTIVE', 20, 19112.589492392, 0, 0, -896.029847839973, -0.034477234990170615, 362 | 13768.237954491486, 1.757758329330831, null, 145287699, null, null, null, 0, null, 0, 0, 363 | { 364 | reason: 'TRADE', 365 | order_id: 54864866189, 366 | order_id_oppo: 54866447371, 367 | liq_stage: null, 368 | trade_price: '19119.0', 369 | trade_amount: '0.59455843' 370 | } 371 | ] 372 | ]) 373 | 374 | srv.setResponse('order_submit', [ 375 | 1567590617.442, 'on-req', 1235, null, [orderRes], 376 | null, 'SUCCESS', 'Submitting1 orders.' 377 | ]) 378 | 379 | const order = await r.closePosition({ position_id: 145287699 }) 380 | assert.deepStrictEqual(order, orderRes) 381 | }) 382 | }) 383 | -------------------------------------------------------------------------------- /test/lib/rest-2-issue-80-argument-length.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const assert = require('assert') 5 | const http = require('http') 6 | const REST2 = require('../../lib/rest2') 7 | 8 | const PORT = 1337 9 | const bhttp = new REST2({ 10 | apiKey: 'dummy', 11 | apiSecret: 'dummy', 12 | url: `http://localhost:${PORT}` 13 | }) 14 | 15 | const testResBody = '["ente", "gans", "scholle"]' 16 | 17 | describe('rest2 api client: issue 80 - argumment length auth request', () => { 18 | let server = null 19 | 20 | beforeEach((done) => { 21 | server = http.createServer((req, res) => { 22 | res.writeHead(200, { 'Content-Type': 'text/plain' }) 23 | res.end(testResBody) 24 | }) 25 | 26 | server.listen(PORT, done) 27 | }) 28 | 29 | afterEach(() => { 30 | server.close() 31 | }) 32 | 33 | it('errors if no payload defined', async () => { 34 | await bhttp._makeAuthRequest('/auth/r/orders') 35 | }) 36 | 37 | it('succeeds with the right argument length', async () => { 38 | const res = await bhttp._makeAuthRequest('/auth/r/orders', {}) 39 | 40 | assert.deepStrictEqual(res, ['ente', 'gans', 'scholle']) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/lib/rest-2-smoke-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const assert = require('assert') 5 | const http = require('http') 6 | const REST2 = require('../../lib/rest2') 7 | 8 | const PORT = 1338 9 | const bhttp = new REST2({ 10 | apiKey: 'dummy', 11 | apiSecret: 'dummy', 12 | url: `http://localhost:${PORT}` 13 | }) 14 | 15 | const testResBody = `[1765.3, 16 | 0.56800816, 17 | 1767.6, 18 | 1.3874, 19 | -62.2, 20 | -0.034, 21 | 1765.3, 22 | 14063.54589155, 23 | 1834.2, 24 | 1726.3 ]` 25 | 26 | describe('rest2 api client', () => { 27 | let server = null 28 | 29 | afterEach((done) => { 30 | if (server) { 31 | server.close(() => { 32 | server = null 33 | done() 34 | }) 35 | } else { 36 | done() 37 | } 38 | }) 39 | 40 | it('gets a response as JSON', async () => { 41 | server = http.createServer((req, res) => { 42 | res.writeHead(200, { 'Content-Type': 'text/plain' }) 43 | res.end(testResBody) 44 | }) 45 | 46 | return new Promise((resolve) => { 47 | server.listen(PORT, () => { 48 | bhttp.ticker({ symbol: 'tBTCUSD' }, (err, res) => { 49 | assert.strictEqual(err, null) 50 | assert.deepStrictEqual(res, JSON.parse(testResBody)) 51 | 52 | resolve() 53 | }) 54 | }) 55 | }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /test/lib/rest-2-unit.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const assert = require('assert') 5 | const _isString = require('lodash/isString') 6 | const _isEmpty = require('lodash/isEmpty') 7 | const { SocksProxyAgent } = require('socks-proxy-agent') 8 | const proxyquire = require('proxyquire') 9 | const { stub } = require('sinon') 10 | 11 | const rpStub = stub() 12 | 13 | const RESTv2 = proxyquire('../../lib/rest2', { 14 | 'node-fetch': rpStub.resolves(null) 15 | }) 16 | 17 | // TODO: Move other tests here where appropriate (unit) 18 | 19 | describe('RESTv2', () => { 20 | describe('default connection url', () => { 21 | it('is a static member on the class', () => { 22 | assert.ok(_isString(RESTv2.url) && !_isEmpty(RESTv2.url)) 23 | }) 24 | }) 25 | 26 | describe('getURL', () => { 27 | it('returns the URL the instance was constructed with', () => { 28 | const rest = new RESTv2({ url: 'test' }) 29 | assert.strictEqual(rest.getURL(), 'test', 'instance does not use provided URL') 30 | }) 31 | }) 32 | 33 | describe('usesAgent', () => { 34 | it('returns true if an agent was passed to the constructor', () => { 35 | const rest = new RESTv2({ 36 | agent: new SocksProxyAgent('socks4://127.0.0.1:9998') 37 | }) 38 | 39 | assert.ok(rest.usesAgent(), 'usesAgent() does not indicate agent presence when one was provided') 40 | }) 41 | 42 | it('returns false if no agent was passed to the constructor', () => { 43 | const rest = new RESTv2() 44 | assert.ok(!rest.usesAgent(), 'usesAgent() indicates agent presence when none provided') 45 | }) 46 | }) 47 | 48 | describe('trades', () => { 49 | it('correctly builds query string', (done) => { 50 | const rest = new RESTv2() 51 | 52 | rest._makePublicRequest = (url) => { 53 | assert.strictEqual(url, '/trades/tBTCUSD/hist?start=1&end=2&limit=3&sort=4') 54 | done() 55 | } 56 | 57 | const params = { symbol: 'tBTCUSD', start: 1, end: 2, limit: 3, sort: 4 } 58 | rest.trades(params) 59 | }) 60 | }) 61 | 62 | describe('request body', () => { 63 | it('excludes nullish', () => { 64 | const rest = new RESTv2() 65 | rest._apiKey = 'key' 66 | rest._apiSecret = 'secret' 67 | rest._makeAuthRequest('path', { a: 1, b: '', c: null, d: undefined }) 68 | 69 | assert(rpStub.lastCall, 'should be called') 70 | const reqOpts = rpStub.lastCall.args[1] 71 | 72 | assert.equal(typeof reqOpts, 'object', 'argument isnt an object') 73 | assert.deepStrictEqual(reqOpts.body, JSON.stringify({ a: 1, b: '' })) 74 | }) 75 | }) 76 | 77 | describe('listener methods', () => { 78 | const testMethod = (name, url, method, ...params) => { 79 | describe(name, () => { 80 | it('calls correct endpoint', (done) => { 81 | const rest = new RESTv2() 82 | rest[method] = (reqURL) => { 83 | assert.strictEqual(reqURL, url) 84 | done() 85 | } 86 | rest[name](...params) 87 | }) 88 | }) 89 | } 90 | 91 | // TODO: add rest... 92 | testMethod('symbols', '/conf/pub:list:pair:exchange', '_makePublicRequest', {}) 93 | testMethod('inactiveSymbols', '/conf/pub:list:pair:exchange:inactive', '_makePublicRequest', {}) 94 | testMethod('futures', '/conf/pub:list:pair:futures', '_makePublicRequest', {}) 95 | testMethod('ledgers', '/auth/r/ledgers/hist', '_makeAuthRequest', {}) 96 | testMethod('ledgers', '/auth/r/ledgers/USD/hist', '_makeAuthRequest', { filters: 'USD' }) 97 | testMethod('generateInvoice', '/auth/w/deposit/invoice', '_makeAuthRequest', {}) 98 | testMethod('marketAveragePrice', '/calc/trade/avg?symbol=fUSD&amount=100', '_makePublicPostRequest', { symbol: 'fUSD', amount: 100 }) 99 | testMethod('keepFunding', '/auth/w/funding/keep', '_makeAuthRequest', { type: 'type', id: 'id' }) 100 | testMethod('cancelOrderMulti', '/auth/w/order/cancel/multi', '_makeAuthRequest', { id: [123] }) 101 | testMethod('orderMultiOp', '/auth/w/order/multi', '_makeAuthRequest', { ops: [['oc_multi', { id: [1] }]] }) 102 | testMethod('invalidateAuthToken', '/auth/w/token/del', '_makeAuthRequest', { authToken: 'token' }) 103 | testMethod('payInvoiceCreate', '/auth/w/ext/pay/invoice/create', '_makeAuthRequest', {}) 104 | testMethod('payInvoiceList', '/auth/r/ext/pay/invoices', '_makeAuthRequest', {}) 105 | testMethod('payInvoiceComplete', '/auth/w/ext/pay/invoice/complete', '_makeAuthRequest', {}) 106 | testMethod('payDepositsUnlinked', '/auth/r/ext/pay/deposits/unlinked', '_makeAuthRequest', {}) 107 | }) 108 | }) 109 | -------------------------------------------------------------------------------- /test/lib/rest1.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /* eslint-env mocha */ 3 | /* eslint camelcase: "off" */ 4 | /* eslint-disable no-unused-expressions */ 5 | 6 | const DNS = require('dns') 7 | const assert = require('assert') 8 | const { expect } = require('chai') 9 | .use(require('chai-as-promised')) 10 | .use(require('dirty-chai')) 11 | const _has = require('lodash/has') 12 | const _map = require('lodash/map') 13 | const _keys = require('lodash/keys') 14 | const _every = require('lodash/every') 15 | const _values = require('lodash/values') 16 | const _isArray = require('lodash/isArray') 17 | const RESTv1 = require('../../lib/rest1') 18 | 19 | describe('REST v1', () => { 20 | let skipPublic = process.env.SKIP_PUBLIC_REST 21 | 22 | if (!skipPublic) { 23 | before((done) => { 24 | DNS.resolve('api.bitfinex.com', (err) => { 25 | if (err) skipPublic = true 26 | done() 27 | }) 28 | }) 29 | } 30 | 31 | describe('errors', function () { 32 | const bfx_rest = new RESTv1() 33 | this.timeout(5000) 34 | it('should error out if a bad endpoint is given', async () => { 35 | await expect(bfx_rest.make_public_request()).to.be.rejected() 36 | }) 37 | it('should fail on authenticated requests if no api_key and api_secret', async () => { 38 | await expect(bfx_rest.account_infos()).to.be.rejected() 39 | }) 40 | }) 41 | 42 | describe('public endpoints', function () { 43 | const bfx_rest = new RESTv1() 44 | this.timeout(10000) // bumped from 5k for moments of platform lag 45 | it('should get a ticker', (done) => { 46 | if (skipPublic) return done() 47 | 48 | bfx_rest.ticker('BTCUSD', (error, data) => { 49 | assert(!error) 50 | expect(data).to.exist 51 | expect(_has(data, [ // eslint-disable-line 52 | 'mid', 53 | 'bid', 54 | 'ask', 55 | 'last_price', 56 | 'low', 57 | 'high', 58 | 'volume', 59 | 'timestamp'])) 60 | done() 61 | }) 62 | }) 63 | it('should get the today endpoint', (done) => { 64 | if (skipPublic) return done() 65 | 66 | bfx_rest.today('BTCUSD', (error, data) => { 67 | assert(!error) 68 | expect(data).to.exist 69 | done() 70 | }) 71 | }) 72 | it('should get the stats', (done) => { 73 | if (skipPublic) return done() 74 | 75 | bfx_rest.stats('BTCUSD', (error, data) => { 76 | assert(!error) 77 | expect(data).to.exist 78 | expect(_has(data[0], ['period', 'volume'])) // eslint-disable-line 79 | expect(_has(data[1], ['period', 'volume'])) // eslint-disable-line 80 | expect(_has(data[2], ['period', 'volume'])) // eslint-disable-line 81 | done() 82 | }) 83 | }) 84 | it('should get the fundingbook', (done) => { 85 | if (skipPublic) return done() 86 | 87 | bfx_rest.fundingbook('USD', (error, data) => { 88 | assert(!error) 89 | expect(data).to.exist 90 | expect(_has(data, ['bids', 'asks'])) // eslint-disable-line 91 | expect(_keys(data.bids[0])).is.eql(['rate', 'amount', 'period', 'timestamp', 'frr']) 92 | expect(_keys(data.asks[0])).is.eql(['rate', 'amount', 'period', 'timestamp', 'frr']) 93 | expect(_every([data.asks[0] + data.bids[0]]), !NaN).ok 94 | done() 95 | }) 96 | }) 97 | 98 | it('should get the orderbook', (done) => { 99 | if (skipPublic) return done() 100 | 101 | bfx_rest.orderbook('BTCUSD', (error, data) => { 102 | assert(!error) 103 | expect(data).to.exist 104 | expect(_keys(data)).is.eql(['bids', 'asks']) 105 | expect(_keys(data.bids[0])).is.eql(['price', 'amount', 'timestamp']) 106 | expect(_keys(data.asks[0])).is.eql(['price', 'amount', 'timestamp']) 107 | expect(_every([data.asks[0] + data.bids[0]]), !NaN).ok 108 | done() 109 | }) 110 | }) 111 | it('should get recent trades', (done) => { 112 | if (skipPublic) return done() 113 | 114 | bfx_rest.trades('BTCUSD', (error, data) => { 115 | assert(!error) 116 | assert(_isArray(data)) 117 | expect(data.length).to.eql(100) 118 | expect(_keys(data[0])).to.eql(['timestamp', 'tid', 'price', 'amount', 'exchange', 'type']) 119 | expect( 120 | _map(_values(data[0]), (v) => typeof (v)) 121 | ).is.eql(['number', 'number', 'string', 'string', 'string', 'string']) 122 | done() 123 | }) 124 | }) 125 | it('should get recent lends', (done) => { 126 | if (skipPublic) return done() 127 | 128 | bfx_rest.lends('USD', (error, data) => { 129 | assert(!error) 130 | expect(data).to.exist 131 | assert(_isArray(data)) 132 | expect(data.length).to.eql(50) 133 | expect(_keys(data[0])).to.eql(['rate', 'amount_lent', 'amount_used', 'timestamp']) 134 | expect( 135 | _map(_values(data[0]), (v) => typeof (v)) 136 | ).is.eql(['string', 'string', 'string', 'number']) 137 | done() 138 | }) 139 | }) 140 | it('should get symbols', (done) => { 141 | if (skipPublic) return done() 142 | 143 | bfx_rest.get_symbols((error, data) => { 144 | assert(!error) 145 | expect(data[0]).to.eql('btcusd') 146 | done() 147 | }) 148 | }) 149 | it('should get symbol details', (done) => { 150 | if (skipPublic) return done() 151 | 152 | bfx_rest.symbols_details((error, data) => { 153 | assert(!error) 154 | expect(data).to.exist 155 | expect(data[0].pair).to.eql('btcusd') 156 | done() 157 | }) 158 | }) 159 | }) 160 | }) 161 | --------------------------------------------------------------------------------