├── .circleci └── config.yml ├── .dockerignore ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .istanbul.yml ├── .npmignore ├── Dockerfile ├── LICENSE ├── README.md ├── default.conf ├── demos ├── advanced │ ├── demo.js │ └── index.html ├── basic │ ├── index.html │ └── index.js ├── node │ ├── node-demo.js │ ├── package.json │ └── webpack.config.js └── oauth │ ├── index.html │ └── index.js ├── docs └── networkcalls.md ├── flow-typed ├── binary-api.js.flow └── binary-live-api.js.flow ├── gulpfile.js ├── mocha.opts ├── package.json ├── src ├── ApiState.js ├── LiveApi.js ├── LiveEvents.js ├── OAuth.js ├── ServerError.js ├── __tests__ │ ├── LiveApi-test.js │ ├── LiveEvents-test.js │ ├── ServerError-test.js │ ├── admin-test.js │ ├── custom-test.js │ ├── oauth-test.js │ ├── read-test.js │ ├── resubscribe-test.js │ ├── stateful-test.js │ ├── trade-test.js │ ├── unauthenticated-test.js │ └── useRx-test.js ├── calls │ ├── admin.js │ ├── index.js │ ├── payments.js │ ├── read.js │ ├── trade.js │ └── unauthenticated.js ├── custom.js └── index.js ├── wallaby.conf.js ├── webpack.config.js └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | k8s: circleci/kubernetes@0.7.0 4 | slack: circleci/slack@3.4.2 5 | commands: 6 | npm_install: 7 | description: "Install npm modules" 8 | steps: 9 | - restore_cache: 10 | name: Restore npm cache 11 | keys: 12 | - npm-v1-{{ checksum "package.json" }} 13 | - npm-v1- 14 | - run: 15 | name: Install npm modules 16 | command: yarn 17 | - save_cache: 18 | name: Save NPM cache 19 | key: npm-v1-{{ checksum "package.json" }} 20 | paths: 21 | - "node_modules" 22 | build: 23 | description: "Build" 24 | steps: 25 | - run: 26 | name: "yarn build" 27 | command: yarn build 28 | versioning: 29 | description: "Add version to build" 30 | parameters: 31 | target_branch: 32 | type: string 33 | steps: 34 | - run: 35 | name: Tag build 36 | command: echo "<< parameters.target_branch >> $(date -u +'%Y-%m-%dT%H:%M:%SZ')" > lib/version 37 | docker_build_push: 38 | description: "Build and Push image to docker hub" 39 | steps: 40 | - setup_remote_docker 41 | - run: 42 | name: Building docker image 43 | command: | 44 | docker build -t ${DOCKHUB_ORGANISATION}/binary-static-liveapi:${CIRCLE_SHA1} -t ${DOCKHUB_ORGANISATION}/binary-static-liveapi:latest . 45 | - run: 46 | name: Pushing Image to docker hub 47 | command: | 48 | echo $DOCKERHUB_PASSWORD | docker login -u $DOCKERHUB_USERNAME --password-stdin 49 | docker push ${DOCKHUB_ORGANISATION}/binary-static-liveapi:${CIRCLE_SHA1} 50 | docker push ${DOCKHUB_ORGANISATION}/binary-static-liveapi:latest 51 | k8s_deploy: 52 | description: "Deploy to k8s cluster" 53 | parameters: 54 | k8s_namespace: 55 | type: string 56 | default: "liveapi-binary-com-production" 57 | k8s_service: 58 | type: string 59 | default: "binary-static-liveapi" 60 | steps: 61 | - k8s/install-kubectl 62 | - run: 63 | name: Deploying to k8s cluster for service binary-liveapi 64 | command: | 65 | export NAMESPACE=<< parameters.k8s_namespace >> 66 | git clone https://github.com/binary-com/devops-ci-scripts 67 | cd devops-ci-scripts/k8s-build_tools 68 | echo $CA_CRT | base64 --decode > ca.crt 69 | ./release.sh << parameters.k8s_service >> ${CIRCLE_SHA1} 70 | notify_slack: 71 | description: "Notify slack" 72 | steps: 73 | - slack/status: 74 | include_project_field: false 75 | failure_message: "Release failed for liveapi.binary.com with version *$(cat lib/version)*" 76 | success_message: "Release succeeded for liveapi.binary.com with version *$(cat lib/version)*" 77 | webhook: ${SLACK_WEBHOOK} 78 | publish_to_pages_staging: 79 | description: "Publish to cloudflare pages" 80 | steps: 81 | - run: 82 | name: "Publish to cloudflare pages (staging)" 83 | command: | 84 | cd lib 85 | npx wrangler pages publish . --project-name=binary-live-api-pages --branch=staging 86 | echo "New staging website - http://staging.cf-pages-binary-live-api.binary.com" 87 | 88 | publish_to_pages_production: 89 | description: "Publish to cloudflare pages" 90 | steps: 91 | - run: 92 | name: "Publish to cloudflare pages (production)" 93 | command: | 94 | cd lib 95 | npx wrangler pages publish . --project-name=binary-live-api-pages --branch=main 96 | echo "New website - http://cf-pages-binary-live-api.binary.com" 97 | jobs: 98 | build: 99 | docker: 100 | - image: circleci/node:8.10.0-stretch 101 | steps: 102 | - checkout 103 | - npm_install 104 | - build 105 | release_staging: 106 | docker: 107 | - image: circleci/node:8.10.0-stretch 108 | steps: 109 | - checkout 110 | - npm_install 111 | - build 112 | - versioning: 113 | target_branch: "staging" 114 | - persist_to_workspace: 115 | root: lib 116 | paths: 117 | - . 118 | release_production: 119 | docker: 120 | - image: circleci/node:8.10.0-stretch 121 | steps: 122 | - checkout 123 | - npm_install 124 | - build 125 | - versioning: 126 | target_branch: "production" 127 | - persist_to_workspace: 128 | root: lib 129 | paths: 130 | - . 131 | - docker_build_push 132 | - k8s_deploy 133 | - notify_slack 134 | publish_cloudflare_staging: 135 | docker: 136 | - image: cimg/node:18.4.0 137 | steps: 138 | - attach_workspace: 139 | at: lib 140 | - publish_to_pages_staging 141 | publish_cloudflare_production: 142 | docker: 143 | - image: cimg/node:18.4.0 144 | steps: 145 | - attach_workspace: 146 | at: lib 147 | - publish_to_pages_production 148 | 149 | workflows: 150 | build: 151 | jobs: 152 | - build: 153 | filters: 154 | branches: 155 | ignore: /^master$/ 156 | release_staging: 157 | jobs: 158 | - release_staging: 159 | filters: 160 | branches: 161 | only: /^master$/ 162 | context: binary-frontend-artifact-upload 163 | - publish_cloudflare_staging: 164 | requires: 165 | - release_staging 166 | filters: 167 | branches: 168 | only: /^master$/ 169 | context: binary-frontend-artifact-upload 170 | release_production: 171 | jobs: 172 | - release_production: 173 | filters: 174 | branches: 175 | ignore: /.*/ 176 | tags: 177 | only: /^production.*/ 178 | context: binary-frontend-artifact-upload 179 | - publish_cloudflare_production: 180 | requires: 181 | - release_production 182 | filters: 183 | branches: 184 | ignore: /.*/ 185 | tags: 186 | only: /^production.*/ 187 | context: binary-frontend-artifact-upload 188 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | Dockerfile 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | lib 4 | demos 5 | node_modules 6 | **/*/all.js 7 | webpack.*.js 8 | server.js 9 | karma.*.js 10 | test/integration 11 | ./src/_constants/texts.js 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "binary", 4 | "env": { 5 | "browser": true, 6 | "es6": true, 7 | "jest": true 8 | }, 9 | "rules": { 10 | "camelcase": 0, 11 | "no-undef": 0, 12 | "react/jsx-indent": [0, "tab"], 13 | "react/prefer-stateless-function": 0, 14 | "react/jsx-indent-props": [0, "tab"], 15 | "react/jsx-filename-extension": 0, 16 | "import/named": 0, 17 | "import/default": 0, 18 | "import/namespace": 0, 19 | "import/prefer-default-export": 0, 20 | "import/no-extraneous-dependencies": 0, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/wallaby.conf.js 3 | .*/node_modules 4 | .*/coverage 5 | .*/__tests__ 6 | .*/lib 7 | .*/demos 8 | .*/webpack.config.js 9 | 10 | [include] 11 | 12 | [libs] 13 | ./node_modules/binary-utils/flow-typed/binary.js.flow 14 | ./node_modules/binary-utils/flow-typed/binary-utils.js.flow 15 | ./flow-typed/binary-api.js.flow 16 | ./flow-typed/binary-live-api.js.flow 17 | 18 | [options] 19 | esproposal.class_static_fields=enable 20 | esproposal.export_star_as=enable 21 | esproposal.class_instance_fields=enable 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | *.log 4 | .DS_Store 5 | lib 6 | coverage 7 | demos/node/dist 8 | demos/advanced/dist 9 | .publish -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | root: src 3 | include-all-sources: true 4 | verbose: true 5 | excludes: 6 | - index.js 7 | - __tests__/*.js 8 | reporting: 9 | dir: "coverage" 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | src 4 | test 5 | demos 6 | examples 7 | coverage 8 | node_modules 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | COPY ./lib /usr/share/nginx/html 3 | COPY ./default.conf /etc/nginx/conf.d/default.conf 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Binary Ltd. 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 | # binary-live-api 2 | 3 | This repository is **deprecated**, please use https://github.com/binary-com/deriv-api/ instead. 4 | 5 | [![Build Status](https://travis-ci.org/binary-com/binary-live-api.svg?branch=master)](https://travis-ci.org/binary-com/binary-live-api) 6 | 7 | [![Coverage Status](https://coveralls.io/repos/github/binary-com/binary-live-api/badge.svg?branch=master)](https://coveralls.io/github/binary-com/binary-live-api?branch=master) 8 | 9 | This library is a high-level abstraction over the [Binary.com Websockets API](https://developers.binary.com) 10 | 11 | ## 12 | 13 | ## Features 14 | 15 | 1. Promise based, all network calls return a promise that is resolved when response is received, request response mapping is handled out of the box 16 | 2. Automatic reconnect when disconnection, including resubscribe to subscription made before disconnection 17 | 18 | ## Usage in the Browser 19 | 20 | ``` 21 | var api = new LiveApi(); 22 | api.authorize('yourtoken'); 23 | api.getPortfolio(); 24 | api.events.on('portfolio', function(data) { 25 | // do stuff with portfolio data 26 | }); 27 | ``` 28 | 29 | ## Usage From Node 30 | 31 | Install a WebSockets library like 'ws' 32 | 33 | ``` 34 | npm init 35 | npm install ws --save 36 | npm install binary-live-api --save 37 | ``` 38 | 39 | Alternatively, you can add the library to your project with the following link: [https://liveapi.binary.com/binary-live-api.js](https://liveapi.binary.com/binary-live-api.js) - or to fix to a specific version, put the version number in the URL as follows: [https://liveapi.binary.com/27.0.0/binary-live-api.js](https://liveapi.binary.com/27.0.0/binary-live-api.js) 40 | 41 | Require the library and then pass it to LiveApi's constructor. 42 | 43 | ``` 44 | var ws = require('ws'); 45 | var LiveApi = require('binary-live-api').LiveApi; 46 | 47 | var api = new LiveApi({ websocket: ws }); 48 | api.authorize('yourtoken'); 49 | api.getPortfolio(); 50 | api.events.on('portfolio', function(data) { 51 | // do stuff with portfolio data 52 | }); 53 | ``` 54 | 55 | For all available calls, please check [here](docs/networkcalls.md) 56 | 57 | ## Experimental feature (Not for production) 58 | support [RxJs](https://github.com/Reactive-Extensions/RxJS) 59 | 60 | User can opt to use observables API instead of Promise API by passing `useRx = true` in constructor, like below 61 | 62 | ``` 63 | var api = new LiveApi({ useRx: true }); 64 | api.ping() // return Observable, instead of Promise 65 | ``` 66 | 67 | No more global events ~!! as Stream is now modelled as observables, you can pass it around, instead of listening to global event. 68 | This will allow better composition of streams, right now it only include rx.lite, thus not all observables operator are supported, 69 | all supported operators can be check [here](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/libraries/lite/rx.lite.md) 70 | 71 | Example 72 | 73 | ``` 74 | var api = new LiveApi({ useRx: true }); 75 | var r100TickStream = api.subscribeToTicks('R_100'); 76 | 77 | // silly example, but to illustrate you can now operate on them independently 78 | var epochs = r100TickStream.map(function(json){return json.tick.epoch}); 79 | var quotes = r100TickStream.map(function(json){return json.tick.quote}); 80 | 81 | ``` 82 | 83 | ## To deploy as library on gh pages 84 | run `gulp deploy` to deploy library to origin/gh-pages 85 | 86 | run `gulp deploy-prod` to deploy library to upstream/gh-pages 87 | -------------------------------------------------------------------------------- /default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | add_header Cache-Control "public, max-age=7200, s-maxage=600, must-revalidate"; 6 | charset UTF-8; 7 | 8 | error_page 404 /404.html; 9 | 10 | location @custom_error_503 { 11 | return 503; 12 | } 13 | 14 | location ~ /\.git { 15 | return 404; 16 | } 17 | 18 | location / { 19 | root /usr/share/nginx/html; 20 | index index.html index.htm; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/advanced/demo.js: -------------------------------------------------------------------------------- 1 | var LiveApi = window['binary-live-api'].LiveApi; 2 | 3 | var api = new LiveApi(); 4 | 5 | const token = 'qdJ86Avvrsh0Le4'; 6 | api.authorize(token).then( 7 | () => console.log('Authorized!'), 8 | () => console.log('Not Authorized') 9 | ); 10 | 11 | function tickHistoryDemo() { 12 | api.events.on('history', function(response) { 13 | console.log(response); 14 | }); 15 | api.getTickHistory({symbol: 'frxUSDJPY', end: 'latest', count: 10}); 16 | } 17 | 18 | function tickHistoryPromiseDemo() { 19 | api.getTickHistory('frxUSDJPY', {end: 'latest', count: 10}).then(function(response) { 20 | console.log(response); 21 | }); 22 | } 23 | 24 | function forgetDemo() { 25 | api.unsubscribeFromAllTicks(); 26 | } 27 | 28 | function tickStreamDemo() { 29 | api.events.on('tick', function(response) { 30 | console.log(response); 31 | }); 32 | api.subscribeToTick('frxUSDJPY'); 33 | } 34 | 35 | function pingDemo() { 36 | api.events.on('ping', function(response) { 37 | console.log(response); 38 | }); 39 | api.ping(); 40 | } 41 | 42 | function pingPromiseDemo() { 43 | api.ping().then(response => { 44 | console.log(response) 45 | }); 46 | } 47 | 48 | function openPositionsDemo() { 49 | api.events.on('portfolio', function(response) { 50 | console.log(response); 51 | }); 52 | api.getPortfolio(); 53 | } 54 | 55 | function tradingTimesDemo() { 56 | api.events.on('trading_times', function(response) { 57 | console.log(response); 58 | }); 59 | api.getTradingTimes(); 60 | } 61 | 62 | api.events.on('*', function(response) { 63 | console.log('all', response); 64 | }); 65 | 66 | console.log(api.events.messageHandlers); 67 | 68 | // tickHistoryPromiseDemo(); 69 | pingPromiseDemo(); 70 | -------------------------------------------------------------------------------- /demos/advanced/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demos/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /demos/basic/index.js: -------------------------------------------------------------------------------- 1 | var LiveApi = window['binary-live-api'].LiveApi; 2 | var api = new LiveApi(); 3 | 4 | function pingWithEventHandlers() { 5 | api.events.on('ping', function(response) { 6 | console.log(response); 7 | }); 8 | api.ping(); 9 | } 10 | 11 | function pingWithPromises() { 12 | api.ping().then(function(response) { 13 | console.log(response); 14 | }); 15 | } 16 | 17 | function foreverPing() { 18 | setInterval(() => api.ping().then(response => console.log(response)), 1000); 19 | } 20 | 21 | api.subscribeToTick('R_100'); 22 | api.unsubscribeFromTick('R_100'); 23 | 24 | api.resubscribe(); 25 | -------------------------------------------------------------------------------- /demos/node/node-demo.js: -------------------------------------------------------------------------------- 1 | var ws = require('ws'); 2 | var LiveApi = require('binary-live-api').LiveApi; 3 | 4 | var api = new LiveApi({ websocket: ws }); 5 | 6 | function pingWithEventHandlers() { 7 | api.events.on('ping', function(response) { 8 | console.log(response); 9 | }); 10 | api.ping(); 11 | } 12 | 13 | function pingWithPromises() { 14 | api.ping().then(function(response) { 15 | console.log(response); 16 | }); 17 | } 18 | 19 | pingWithEventHandlers(); 20 | -------------------------------------------------------------------------------- /demos/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "node-demo.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "Boris Yankov (https://github.com/borisyankov)", 11 | "license": "ISC", 12 | "dependencies": { 13 | "binary-live-api": "*", 14 | "ws": "^7.0.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demos/node/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'eval', 6 | entry: [ 7 | './demo' 8 | ], 9 | output: { 10 | path: path.join(__dirname, 'dist'), 11 | filename: 'bundle.js', 12 | publicPath: '/static/' 13 | }, 14 | plugins: [ 15 | new webpack.HotModuleReplacementPlugin(), 16 | new webpack.NoErrorsPlugin() 17 | ], 18 | resolve: { 19 | alias: { 20 | 'library-boilerplate': path.join(__dirname, '..', '..', 'src') 21 | }, 22 | extensions: ['', '.js'] 23 | }, 24 | module: { 25 | loaders: [{ 26 | test: /\.js$/, 27 | loaders: ['babel'], 28 | exclude: /node_modules/, 29 | include: __dirname 30 | }, { 31 | test: /\.js$/, 32 | loaders: ['babel'], 33 | include: path.join(__dirname, '..', '..', 'src') 34 | }] 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /demos/oauth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /demos/oauth/index.js: -------------------------------------------------------------------------------- 1 | console.log(window['binary-live-api']); 2 | const { oauthUrl, parseOAuthResponse } = window['binary-live-api'].OAuth; 3 | 4 | const url = oauthUrl('id-ud5PPOTeBcEnkam7ArXIc4AO9e9gw'); 5 | 6 | window.location = url; 7 | 8 | // const accounts = parseOAuthResponse(returnUrl); 9 | -------------------------------------------------------------------------------- /docs/networkcalls.md: -------------------------------------------------------------------------------- 1 | ## Network calls api 2 | 3 | This library provide JS friendly wrapper for all network calls 4 | 5 | Details are documented at https://developers.binary.com/api/ 6 | 7 | ### ADMIN 8 | 9 | * `deleteApiToken(token: string)` 10 | 11 | * `getApiTokens()` 12 | 13 | * `createApiToken(token: string, scopes: string[])` 14 | 15 | * `changePassword(oldPassword: string, newPassword: string)` 16 | 17 | * `registerApplication(options: Object)` 18 | 19 | * `getAllAppList()` 20 | 21 | * `getAppslistById(appid: number)` 22 | 23 | * `deleteApplication(appid: number)` 24 | 25 | * `createRealAccountMaltaInvest(options: Object)` 26 | 27 | * `createRealAccount(options: Object)` 28 | 29 | * `setAccountCurrency(currency: string)` 30 | 31 | * `setSelfExclusion(options: Object)` 32 | 33 | * `setAccountSettings(options: Object)` 34 | 35 | * `setTnCApproval()` 36 | 37 | ---- 38 | 39 | ### PAYMENT 40 | 41 | * `getCashierLockStatus()` 42 | 43 | * `setCashierLock(options: Object)` 44 | 45 | * `withdrawToPaymentAgent(options: Object)` 46 | 47 | * `paymentAgentTransfer(options: Object)` 48 | 49 | * `transferBetweenAccounts(options: Object)` 50 | 51 | 52 | ----- 53 | 54 | ### READ 55 | 56 | * `getAccountLimits()` 57 | 58 | * `getAccountSettings()` 59 | 60 | * `getAccountStatus()` 61 | 62 | * `getSelfExclusion()` 63 | 64 | * `logOut()` 65 | 66 | * `getStatement(options: Object)` 67 | 68 | * `getPortfolio()` 69 | 70 | * `getProfitTable(options: Object)` 71 | 72 | * `getRealityCheckSummary()` 73 | 74 | * `unsubscribeFromBalance()` 75 | 76 | * `subscribeToOpenContract(contractId: number)` 77 | 78 | * `getContractInfo(contractId: number)` 79 | 80 | * `subscribeToAllOpenContracts()` 81 | 82 | * `unsubscribeFromAllOpenContracts()` 83 | 84 | * `subscribeToTransactions()` 85 | 86 | * `unsubscribeFromTransactions()` 87 | 88 | 89 | ---- 90 | 91 | ### TRADE 92 | 93 | * `buyContract(contractId: number, price: number)` 94 | 95 | * `sellContract(contractId: number, price: number)` 96 | 97 | * `sellExpiredContracts()` 98 | 99 | * `topUpVirtualAccount()` 100 | 101 | 102 | ----- 103 | 104 | ### UNAUTHENTICATED 105 | 106 | * `getActiveSymbolsBrief()` 107 | 108 | * `getActiveSymbolsFull()` 109 | 110 | * `getAssetIndex()` 111 | 112 | * `authorize(token: string)` 113 | 114 | * `getContractsForSymbol(symbol: string)` 115 | 116 | * `unsubscribeFromTick(symbol: string)` 117 | 118 | * `unsubscribeFromTicks(symbols: string[])` 119 | 120 | * `unsubscribeByID(id: number)` 121 | 122 | * `unsubscribeFromAllTicks()` 123 | 124 | * `unsubscribeFromAllCandles()` 125 | 126 | * `unsubscribeFromAllProposals()` 127 | 128 | * `unsubscribeFromAllPortfolios()` 129 | 130 | * `unsubscribeFromAllProposalsOpenContract()` 131 | 132 | * `getLandingCompany(landingCompany: string)` 133 | 134 | * `getLandingCompanyDetails(landingCompany: string)` 135 | 136 | * `createVirtualAccount(options: Object)` 137 | 138 | * `ping()` 139 | 140 | * `getPaymentAgentsForCountry(countryCode: string)` 141 | 142 | * `getPayoutCurrencies()` 143 | 144 | * `getPriceProposalForContract(options: Object)` 145 | 146 | * `subscribeToPriceForContractProposal(options: Object)` 147 | 148 | * `getResidences()` 149 | 150 | * `getStatesForCountry(countryCode: string)` 151 | 152 | * `subscribeToTick(symbol: string)` 153 | 154 | * `subscribeToTicks(symbols: string[])` 155 | 156 | * `getTickHistory(symbol: string, options: Object)` 157 | 158 | * `getCandles(symbol: string, options: Object)` 159 | 160 | * `getCandlesForLastNDays(symbol: string, ndays: number)` 161 | 162 | * `getServerTime()` 163 | 164 | * `getTradingTimes(date: Date)` 165 | 166 | * `verifyEmail(email: string, type: string)` 167 | 168 | * `getWebsiteStatus()` 169 | 170 | -------------------------------------------------------------------------------- /flow-typed/binary-api.js.flow: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /* eslint-disable max-len */ 3 | 4 | type Epoch = number; 5 | 6 | type TransactionAction = 'deposit' | 'withdrawal' | 'buy' | 'sell'; 7 | 8 | type LandingCompany = 'japan' | 'costarica' | 'malta' | 'maltainvest' | 'iom'; 9 | 10 | type MarketType = 'forex' | 'indices' | 'stocks' | 'commodities' | 'volidx'; 11 | 12 | type DurationUnit = 'd' | 'm' | 's' | 'h' | 't'; 13 | 14 | type AccessScope = 'read' | 'trade' | 'payments' | 'admin'; 15 | 16 | declare class ApiRequest { 17 | req_id?: number, 18 | } 19 | 20 | declare class ApiResponse { 21 | msg_type: string, 22 | echo_req?: Object, 23 | req_id?: number, 24 | } 25 | 26 | type ApiErrorResponse = { 27 | msg_type: string, 28 | echo_req?: Object, 29 | error: { 30 | code: string, 31 | message: string, 32 | }, 33 | } 34 | 35 | 36 | // active_symbols 37 | 38 | declare class ActiveSymbolsRequest extends ApiRequest { 39 | active_symbols: 'brief' | 'full', 40 | landing_company?: LandingCompany, 41 | } 42 | 43 | declare class ActiveSymbol { 44 | symbol: string, 45 | intraday_interval_minutes: number, 46 | symbol_type: string, 47 | exchange_is_open: ?number, 48 | exchange_name: string, 49 | delay_amount: number, 50 | display_name: string, 51 | spot: ?number, 52 | spot_time: string, 53 | is_trading_suspended: number, 54 | quoted_currency_symbol: string, 55 | spot_age: string, 56 | market: string, 57 | market_display_name: string, 58 | submarket: string, 59 | submarket_display_name: string, 60 | pip: number, 61 | } 62 | 63 | declare class ActiveSymbolsResponse extends ApiResponse { 64 | active_symbolsarray: ActiveSymbol[], 65 | } 66 | 67 | // TODO: asset_index 68 | 69 | declare class AssetIndexRequest extends ApiRequest { 70 | asset_index: 1, 71 | landing_company?: LandingCompany, 72 | } 73 | 74 | declare class AssetIndexResponse extends ApiResponse { 75 | asset_index: any[], 76 | } 77 | 78 | // authorize 79 | 80 | declare class AuthorizeRequest extends ApiRequest { 81 | authorize: string, 82 | } 83 | 84 | declare class AuthorizeResponse extends ApiResponse { 85 | authorize: { 86 | email: string, 87 | currency: string, 88 | balance: number, 89 | loginid: string, 90 | is_virtual: 0 | 1, 91 | landing_company_name: string, 92 | fullname: string, 93 | }, 94 | } 95 | 96 | // contracts_for 97 | 98 | declare class ForwardStartingOption { 99 | close: Epoch, 100 | date: Epoch, 101 | open: Epoch, 102 | } 103 | 104 | declare class AvailableContractDetails { 105 | market: MarketType, 106 | contracts_display: string, 107 | max_contract_duration: string, 108 | barrier_category: string, 109 | payout_limit: number, 110 | submarket: string, 111 | exchange_name: string, 112 | contract_category_display: string, 113 | contract_type: string, 114 | min_contract_duration: string, 115 | sentiment: string, 116 | barriers: number, 117 | contract_category: string, 118 | start_type: string, 119 | expiry_type: string, 120 | underlying_symbol: string, 121 | forward_starting_options: ForwardStartingOption[], 122 | available_barriers?: number[], 123 | expired_barriers?: any[], 124 | trading_period?: { 125 | date_expiry: { 126 | date: string, 127 | epoch: Epoch, 128 | }, 129 | date_start: { 130 | date: string, 131 | epoch: Epoch, 132 | }, 133 | duration: string, 134 | } 135 | } 136 | 137 | declare class ContractsForRequest extends ApiRequest { 138 | contracts_for: string, 139 | currency?: string, 140 | region?: 'japan' | 'other', 141 | } 142 | 143 | declare class ContractsForResponse extends ApiResponse { 144 | contracts_for: { 145 | available: AvailableContractDetails, 146 | close: Epoch, 147 | open: Epoch, 148 | hit_count: number, 149 | spot: ?number, 150 | feed_license: string, 151 | }, 152 | } 153 | 154 | // forget 155 | 156 | declare class ForgetRequest extends ApiRequest { 157 | forget: string, 158 | } 159 | 160 | declare class ForgetResponse extends ApiResponse { 161 | forget: 0 | 1, 162 | } 163 | 164 | // forget_all 165 | 166 | declare class ForgetAllRequest extends ApiRequest { 167 | forget_all: 'ticks' | 'candles' | 'proposal' | 'portfolio' | 'proposal_open_contract' | 'balance' | 'transaction' | 'pricing_table', 168 | } 169 | 170 | declare class ForgetAllResponse extends ApiResponse { 171 | forget_all: number[], 172 | } 173 | 174 | // get_corporate_actions 175 | 176 | declare class CorporateAction { 177 | display_date: string, 178 | type: number, 179 | value: number, 180 | modifier: string, 181 | } 182 | 183 | declare class CorporateActionsRequest extends ApiRequest { 184 | get_corporate_actions: 1, 185 | symbol: string, 186 | start: string, 187 | end: string, 188 | } 189 | 190 | declare class CorporateActionsResponse extends ApiResponse { 191 | get_corporate_actions: { 192 | actions: CorporateAction[], 193 | } 194 | } 195 | 196 | // landing_company 197 | 198 | declare class LandingCompanyDetails { 199 | id: string, 200 | name: string, 201 | gaming_company: { 202 | shortcode: string, 203 | name: string, 204 | address: string[], 205 | country: string, 206 | legal_default_currency: string, 207 | legal_allowed_currencies: string[], 208 | legal_allowed_markets: string[], 209 | has_reality_check: 0 | 1, 210 | }, 211 | } 212 | 213 | declare class LandingCompanyRequest extends ApiRequest { 214 | landing_company: string, 215 | } 216 | 217 | declare class LandingCompanyResponse extends ApiResponse { 218 | landing_company: LandingCompanyDetails, 219 | } 220 | 221 | // landing_company_details 222 | 223 | declare class LandingCompanyDetailsRequest extends ApiRequest { 224 | landing_company_details: string, 225 | } 226 | 227 | declare class LandingCompanyDetailsResponse extends ApiResponse { 228 | landing_company_details: LandingCompanyDetails, 229 | } 230 | 231 | // new_account_virtual 232 | 233 | declare class NewAccountVirtualRequest extends ApiRequest { 234 | new_account_virtual: 1, 235 | verification_code: string, 236 | client_password: string, 237 | residence: string, 238 | affiliate_token?: string, 239 | utm_source?: string, 240 | utm_medium?: string, 241 | utm_campaign?: string, 242 | } 243 | 244 | declare class NewAccountVirtualResponse extends ApiResponse { 245 | new_account_virtual: { 246 | client_id: string, 247 | email: string, 248 | currency: string, 249 | balance: number, 250 | oauth_token: string, 251 | }, 252 | } 253 | 254 | // ping 255 | 256 | declare class PingRequest extends ApiRequest { 257 | ping: 1, 258 | } 259 | 260 | declare class PingResponse extends ApiResponse { 261 | ping: 'pong', 262 | } 263 | 264 | // paymentagent_list 265 | 266 | declare class PaymentAgentDetails { 267 | currencies: string, 268 | deposit_commission: string, 269 | email: string, 270 | further_information: string, 271 | name: string, 272 | paymentagent_loginid: string, 273 | summary: string, 274 | supported_banks: string, 275 | telephone: string, 276 | url: string, 277 | withdrawal_commission: string, 278 | } 279 | 280 | declare class PaymentAgentListRequest extends ApiRequest { 281 | paymentagent_list: string, 282 | } 283 | 284 | declare class PaymentAgentListResponse extends ApiResponse { 285 | paymentagent_list: { 286 | available_countries: any[], 287 | list: PaymentAgentDetails[], 288 | } 289 | } 290 | 291 | // payout_currencies 292 | 293 | declare class PayoutCurrenciesRequest extends ApiRequest { 294 | payout_currencies: 1, 295 | } 296 | 297 | declare class PayoutCurrenciesResponse extends ApiResponse { 298 | payout_currencies: string[], 299 | } 300 | 301 | // proposal 302 | 303 | // TODO 304 | 305 | // residence_list 306 | 307 | declare class ResidenceListRequest extends ApiRequest { 308 | residence_list: 1, 309 | } 310 | 311 | declare class ResidenceListResponse extends ApiResponse { 312 | residence_list: string[], 313 | } 314 | 315 | // states_list 316 | 317 | declare class StatesListRequest extends ApiRequest { 318 | states_list: 1, 319 | } 320 | 321 | declare class StatesListResponse extends ApiResponse { 322 | states_list: string[], 323 | } 324 | 325 | // ticks 326 | 327 | declare class TickSpotData { 328 | epoch: Epoch, 329 | id: string, 330 | quote: number, 331 | symbol: string, 332 | } 333 | 334 | declare class TicksRequest extends ApiRequest { 335 | ticks: string | string[], 336 | subscribe?: 1, 337 | } 338 | 339 | declare class TicksResponse extends ApiResponse { 340 | tick: TickSpotData, 341 | } 342 | 343 | // ticks_history 344 | 345 | declare class TickHistoryRequest extends ApiRequest { 346 | ticks_history: string | string[], 347 | end?: Epoch | 'latest', 348 | start: Epoch, 349 | count: number, 350 | style: 'candles' | 'ticks', 351 | granularity?: number, 352 | adjust_start_time?: 1, 353 | subscribe?: 1, 354 | } 355 | 356 | declare class TickHistoryResponse extends ApiResponse { 357 | history?: { 358 | times: Epoch[], 359 | prices: number, 360 | }, 361 | candles?: Candle[], 362 | } 363 | 364 | // time 365 | 366 | declare class TimeRequest extends ApiRequest { 367 | time: 1, 368 | } 369 | 370 | declare class TimeResponse extends ApiResponse { 371 | time: Epoch, 372 | } 373 | 374 | // trading_times 375 | 376 | declare class TradingTimesRequest extends ApiRequest { 377 | trading_times: string, 378 | } 379 | 380 | declare class TradingTimesResponse extends ApiResponse { 381 | trading_times: { 382 | markets: any[], 383 | } 384 | } 385 | 386 | // verify_email 387 | 388 | declare class VerifyEmailRequest extends ApiRequest { 389 | verify_email: string, 390 | type: 'account_opening' | 'reset_password' | 'paymentagent_withdraw' | 'payment_withdraw', 391 | } 392 | 393 | declare class VerifyEmailResponse extends ApiResponse { 394 | verify_email: 1, 395 | } 396 | 397 | // website_status 398 | 399 | declare class WebsiteStatus { 400 | terms_conditions_version: string, 401 | api_call_limits: { 402 | max_proposal_subscription: { 403 | applies_to: string, 404 | max: number, 405 | }, 406 | max_requestes_general: { 407 | applies_to: string, 408 | hourly: number, 409 | minutely: number, 410 | }, 411 | max_requests_outcome: { 412 | applies_to: string, 413 | hourly: number, 414 | minutely: number, 415 | }, 416 | max_requests_pricing: { 417 | applies_to: string, 418 | hourly: number, 419 | minutely: number, 420 | } 421 | }, 422 | clients_country: string, 423 | } 424 | 425 | declare class WebsiteStatusRequest extends ApiRequest { 426 | website_status: 1, 427 | } 428 | 429 | declare class WebsiteStatusResponse extends ApiResponse { 430 | website_status: WebsiteStatus, 431 | } 432 | 433 | // get_limits 434 | 435 | declare class AccountLimits { 436 | account_balance: number, 437 | daily_turnover: number, 438 | open_positions: number, 439 | payout: number, 440 | lifetime_limit: number, 441 | num_of_days: number, 442 | num_of_days_limit: number, 443 | remainder: number, 444 | withdrawal_for_x_days_monetary: number, 445 | } 446 | 447 | declare class AccountLimitsRequest extends ApiRequest { 448 | get_limits: 1, 449 | } 450 | 451 | declare class AccountLimitsResponse extends ApiResponse { 452 | get_limits: AccountLimits, 453 | } 454 | 455 | // get_settings 456 | 457 | declare class AccountSettings { 458 | email: string, 459 | country: ?string, 460 | country_code: ?string, 461 | salutation: string, 462 | first_name: string, 463 | last_name: string, 464 | date_of_birth: ?number, 465 | address_line_1: string, 466 | address_line_2: string, 467 | address_city: string, 468 | address_state: string, 469 | address_postcode: string, 470 | phone: string, 471 | is_authenticated_payment_agent: number, 472 | jp_account_status: { 473 | status: 'activated' | 'jp_knowledge_test_pending' | 'jp_knowledge_test_fail' | 'jp_activation_pending' | 'disabled', 474 | last_test_epoch: number, 475 | next_test_epoch: number, 476 | }, 477 | jp_settings: { 478 | gender: 'm' | 'f', 479 | occupation: 'Office worker' | 'Director' | 'Public worker' | 'Self-employed Housewife / Househusband' | 'Contract / Temporary / Part Time Student' | 'Unemployed' | 'Others', 480 | annual_income: 'Less than 1 million JPY' | '1-3 million JPY' | '3-5 million JPY' | '5-10 million JPY' | '10-30 million JPY' | '30-50 million JPY' | '50-100 million JPY' | 'Over 100 million JPY', 481 | financial_asset: 'Less than 1 million JPY' | '1-3 million JPY' | '3-5 million JPY' | '5-10 million JPY' | '10-30 million JPY' | '30-50 million JPY' | '50-100 million JPY' | 'Over 100 million JPY', 482 | daily_loss_limit: number, 483 | trading_experience_equities: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 484 | trading_experience_commodities: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 485 | trading_experience_foreign_currency_deposit: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 486 | trading_experience_margin_fx: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 487 | trading_experience_investment_trust: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 488 | trading_experience_public_bond: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 489 | trading_experience_option_trading: 'No experience' |'Less than 6 months' | '6 months to 1 year' | '1-3 years' | '3-5 years' | 'Over 5 years', 490 | trading_purpose: 'Targeting short-term profits' | 'Targeting medium-term / long-term profits' | 'Both the above Hedging', 491 | hedge_asset: 'Foreign currency deposit' | 'Margin FX' | 'Other', 492 | hedge_asset_amount: ?number, 493 | } 494 | } 495 | 496 | declare class AccountSettingsRequest extends ApiRequest { 497 | get_settings: 1, 498 | } 499 | 500 | declare class AccountSettingsResponse extends ApiResponse { 501 | get_settings: AccountSettings, 502 | } 503 | 504 | // get_account_status 505 | 506 | declare class AccountStatus { 507 | status: string[], 508 | risk_classification: 'low' | 'standard' | 'high', 509 | } 510 | 511 | declare class AccountStatusRequest extends ApiRequest { 512 | get_account_status: 1, 513 | } 514 | 515 | declare class AccountStatusResponse extends ApiResponse { 516 | get_account_status: AccountStatus, 517 | } 518 | 519 | // balance 520 | 521 | declare class Balance { 522 | balance: number, 523 | currency: string, 524 | loginid: string, 525 | id: string, 526 | } 527 | 528 | declare class BalanceRequest extends ApiRequest { 529 | balance: 1, 530 | subscribe?: 1, 531 | } 532 | 533 | declare class BalanceResponse extends ApiResponse { 534 | balance: Balance, 535 | } 536 | 537 | // get_self_exclusion 538 | 539 | declare class SelfExclusionSettings { 540 | max_balance: number, 541 | max_turnover: number, 542 | max_losses: number, 543 | max_7day_turnover: number, 544 | max_7day_losses: number, 545 | max_30day_turnover: number, 546 | max_30day_losses: number, 547 | max_open_bets: number, 548 | session_duration_limit: number, 549 | exclude_until: string, 550 | timeout_until: number, 551 | } 552 | 553 | declare class SelfExclusionRequest extends ApiRequest { 554 | get_self_exclusion: 1, 555 | } 556 | 557 | declare class SelfExclusionResponse extends ApiResponse { 558 | get_self_exclusion: SelfExclusionSettings, 559 | } 560 | 561 | // login_history 562 | 563 | declare class LoginHistoryEntry { 564 | time: Epoch, 565 | action: string, 566 | environment: string, 567 | status: 0 | 1, 568 | } 569 | 570 | declare class LoginHistoryRequest extends ApiRequest { 571 | login_history: 1, 572 | limit: number, 573 | } 574 | 575 | declare class LoginHistoryResponse extends ApiResponse { 576 | login_history: LoginHistoryEntry[], 577 | } 578 | 579 | // logout 580 | 581 | declare class LogoutRequest extends ApiRequest { 582 | logout: 1, 583 | } 584 | 585 | declare class LogoutResponse extends ApiResponse { 586 | logout: number, 587 | } 588 | 589 | // statement 590 | 591 | declare class StatementTransaction { 592 | balance_after: number, 593 | transaction_id: number, 594 | contract_id: ?number, 595 | transaction_time: Epoch, 596 | purchase_time: Epoch, 597 | action_type: TransactionAction, 598 | amount: number, 599 | longcode: string, 600 | shortcode: ?string, 601 | payout: ?number, 602 | app_id: ?number, 603 | } 604 | 605 | declare class StatementOptions { 606 | description?: 1, 607 | limit: number, 608 | offset: number, 609 | date_from: Epoch, 610 | date_to: Epoch, 611 | action_type: 'buy' | 'sell' | 'deposit' | 'withdrawal', 612 | } 613 | 614 | declare class StatementRequest extends ApiRequest mixins StatementOptions { 615 | statement: 1, 616 | } 617 | 618 | declare class StatementResponse extends ApiResponse { 619 | statement: { 620 | count: number, 621 | transactions: StatementTransaction[], 622 | } 623 | } 624 | 625 | // portfolio 626 | 627 | declare class OpenContract { 628 | contract_id: number, 629 | transaction_id: number, 630 | purchase_time: Epoch, 631 | symbol: string, 632 | payout: number, 633 | buy_price: number, 634 | date_start: Epoch, 635 | expiry_time: Epoch, 636 | contract_type: ContractType, 637 | currency: string, 638 | longcode: string, 639 | app_id: ?number, 640 | } 641 | 642 | declare class PortfolioRequest extends ApiRequest { 643 | portfolio: 1, 644 | } 645 | 646 | declare class PortfolioResponse extends ApiResponse { 647 | portfolio: { 648 | contracts: OpenContract[], 649 | } 650 | } 651 | 652 | // profit_table 653 | 654 | declare class ProfitTableTransaction { 655 | transaction_id: number, 656 | contract_id: ?number, 657 | purchase_time: Epoch, 658 | sell_time: Epoch, 659 | buy_price: number, 660 | sell_price: number, 661 | longcode: string, 662 | shortcode: string, 663 | payout: number, 664 | app_id: ?number, 665 | } 666 | 667 | declare class ProfitTableOptions { 668 | description?: 1, 669 | limit: number, 670 | offset: number, 671 | date_from: string, 672 | date_to: string, 673 | sort: 'ASC' | 'DESC', 674 | } 675 | 676 | declare class ProfitTableRequest extends ApiRequest mixins ProfitTableOptions { 677 | profit_table: 1, 678 | } 679 | 680 | declare class ProfitTableResponse extends ApiResponse { 681 | profit_table: { 682 | count: number, 683 | transactions: ProfitTableTransaction[], 684 | } 685 | } 686 | 687 | // proposal_open_contract 688 | 689 | declare class PriceProposal { 690 | high_barrier?: number, 691 | low_barrier?: number, 692 | barrier?: number, 693 | original_high_barrier?: number, 694 | original_low_barrier?: number, 695 | original_barrier?: number, 696 | barrier_count: number, 697 | bid_price: number, 698 | contract_id: number, 699 | contract_type: string, 700 | currency: string, 701 | current_spot?: number, 702 | current_spot_time: Epoch, 703 | entry_spot?: number, 704 | date_expiry?: Epoch, 705 | date_settlement?: Epoch, 706 | date_start?: Epoch, 707 | id: string, 708 | has_corporate_actions: 0 | 1, 709 | is_expired: 0 | 1, 710 | is_forward_starting: 0 | 1, 711 | is_intraday: 0 | 1, 712 | is_path_dependent: number, 713 | is_valid_to_sell: 0 | 1, 714 | longcode: string, 715 | payout: number, 716 | shortcode: string, 717 | display_value: number, 718 | underlying: string, 719 | display_name: string, 720 | entry_tick: number, 721 | entry_tick_time: Epoch, 722 | exit_tick: number, 723 | exit_tick_time: Epoch, 724 | tick_count: number, 725 | validation_error?: string, 726 | sell_price?: number, 727 | buy_price: number, 728 | purchase_time?: Epoch, 729 | sell_time?: Epoch, 730 | sell_spot: number, 731 | sell_spot_time?: Epoch, 732 | entry_level?: number, 733 | amount_per_point?: string, 734 | stop_loss_level?: number, 735 | stop_profit_level?: number, 736 | current_level?: number, 737 | exit_level?: number, 738 | current_value_in_dollar?: number, 739 | current_value_in_point?: number, 740 | transaction_ids: string[], 741 | } 742 | 743 | declare class ProposalOpenContractRequest extends ApiRequest { 744 | proposal_open_contract: 1, 745 | contract_id: number, 746 | subscribe?: 1, 747 | } 748 | 749 | declare class ProposalOpenContractResponse extends ApiResponse { 750 | proposal_open_contract: LoginHistoryEntry[], 751 | } 752 | 753 | // reality_check 754 | 755 | declare class RealityCheckSummary { 756 | start_time: Epoch, 757 | loginid: string, 758 | currency: string, 759 | buy_count: number, 760 | buy_amount: number, 761 | sell_count: number, 762 | sell_amount: number, 763 | potential_profit: number, 764 | open_contract_count: number, 765 | } 766 | 767 | declare class RealityCheckRequest extends ApiRequest { 768 | reality_check: 1, 769 | } 770 | 771 | declare class RealityCheckResponse extends ApiResponse { 772 | reality_check: RealityCheckSummary, 773 | } 774 | 775 | // transaction 776 | 777 | declare class TrasnactionUpdate { 778 | balance: number, 779 | action: TransactionAction, 780 | contract_id: number, 781 | transaction_id: number, 782 | amount: number, 783 | id: string, 784 | transaction_time: Epoch, 785 | purchase_time: Epoch, 786 | currency: string, 787 | longcode: string, 788 | symbol: string, 789 | display_name: string, 790 | date_expiry: Epoch, 791 | } 792 | 793 | declare class TransactionRequest extends ApiRequest { 794 | transaction: 1, 795 | subscribe?: 1, 796 | } 797 | 798 | declare class TransactionResponse extends ApiResponse { 799 | transaction: TrasnactionUpdate, 800 | } 801 | 802 | // buy 803 | 804 | declare class BuyContractParameters { 805 | amount: number, 806 | basis: 'payout' | 'stake', 807 | contract_type: string, 808 | currency: string, 809 | date_start: Epoch, 810 | date_expiry: Epoch, 811 | duration: number, 812 | duration_unit: DurationUnit, 813 | symbol: string, 814 | barrier: string, 815 | barrier2: string, 816 | } 817 | 818 | declare class BuySpreadContractParameters extends BuyContractParameters { 819 | amount_per_point: number, 820 | stop_typee: 'dollar' | 'point', 821 | stop_profit: number, 822 | stop_loss: number, 823 | } 824 | 825 | declare class ReceiptConfirmation { 826 | balance_after: number, 827 | longcode: string, 828 | shortcode: string, 829 | start_time: Epoch, 830 | contract_id: number, 831 | buy_price: number, 832 | purchase_time: Epoch, 833 | transaction_id: number, 834 | } 835 | 836 | declare class SpreadReceiptConfirmation extends ReceiptConfirmation { 837 | amount_per_point: number, 838 | stop_profit_level: number, 839 | stop_loss_level: number, 840 | payout: number, 841 | } 842 | 843 | declare class BuyContractRequest extends ApiRequest { 844 | buy: string, 845 | price: number, 846 | parameters: BuyContractParameters, 847 | } 848 | 849 | declare class BuyContractResponse extends ApiResponse { 850 | buy: ReceiptConfirmation, 851 | } 852 | 853 | // buy_contract_for_multiple_accounts 854 | 855 | declare class BuyContractForMultipleAccountsRequest extends ApiRequest { 856 | buy_contract_for_multiple_accounts: 1, 857 | tokens: string[], 858 | price: number, 859 | parameters: BuyContractParameters, 860 | } 861 | 862 | declare class BuyContractForMultipleAccountsResponse extends ApiResponse { 863 | buy_contract_for_multiple_accounts: { 864 | result: ReceiptConfirmation[], 865 | }, 866 | } 867 | 868 | // sell 869 | 870 | declare class SellReceipt { 871 | balance_after: number, 872 | contract_id: number, 873 | sold_for: number, 874 | transaction_id: number, 875 | } 876 | 877 | declare class SellContractRequest extends ApiRequest { 878 | sell: number, 879 | price: number, 880 | } 881 | 882 | declare class SellContractResponse extends ApiResponse { 883 | sell: SellReceipt, 884 | } 885 | 886 | // sell_expired 887 | 888 | declare class SellExpiredContractsRequest extends ApiRequest { 889 | sell_expired: 1, 890 | } 891 | 892 | declare class SellExpiredContractsResponse extends ApiResponse { 893 | sell_expired: { 894 | count: number, 895 | }, 896 | } 897 | 898 | // topup_virtual 899 | 900 | declare class TopUpVirtualRequest extends ApiRequest { 901 | topup_virtual: 1, 902 | } 903 | 904 | declare class TopUpVirtualResponse extends ApiResponse { 905 | topup_virtual: { 906 | currency: string, 907 | amount: number, 908 | }, 909 | } 910 | 911 | // api_token 912 | 913 | declare class ApiTokenManagementRequest extends ApiRequest { 914 | api_token: 1, 915 | new_token: string, 916 | new_token_scope: AccessScope[], 917 | delete_token: string, 918 | sub_account: string, 919 | } 920 | 921 | declare class ApiTokenManagementResponse extends ApiResponse { 922 | api_token: { 923 | tokens: string[], 924 | new_token?: 1, 925 | delete_token?: 1, 926 | sub_account: string, 927 | }, 928 | } 929 | 930 | // app_register 931 | 932 | declare class ApplicationDetails { 933 | name: string, 934 | scopes: AccessScope[], 935 | homepage?: string, 936 | github?: string, 937 | appstore?: string, 938 | googleplay: string, 939 | redirect_uri: string, 940 | app_markup_percentage?: number, 941 | } 942 | 943 | declare class ApplicationRegisterRequest extends ApiRequest mixins ApplicationDetails { 944 | app_register: 1, 945 | } 946 | 947 | declare class ApplicationRegisterResponse extends ApiResponse { 948 | app_register: ApplicationDetails, 949 | } 950 | 951 | // app_list 952 | 953 | declare class ApplicationListRequest extends ApiRequest { 954 | app_list: 1, 955 | } 956 | 957 | declare class ApplicationListResponse extends ApiResponse { 958 | app_list: ApplicationDetails[], 959 | } 960 | 961 | // app_get 962 | 963 | declare class ApplicationDetailsRequest extends ApiRequest { 964 | app_get: number, 965 | } 966 | 967 | declare class ApplicationDetailsResponse extends ApiResponse { 968 | app_get: ApplicationDetails, 969 | } 970 | 971 | // app_delete 972 | 973 | declare class ApplicationDeleteRequest extends ApiRequest { 974 | app_delete: number, 975 | } 976 | 977 | declare class ApplicationDeleteResponse extends ApiResponse { 978 | app_delete: 1, 979 | } 980 | 981 | // app_update 982 | 983 | declare class ApplcationUpdateRequest extends ApiRequest mixins ApplicationDetails { 984 | app_update: 1, 985 | } 986 | 987 | declare class ApplcationUpdateResponse extends ApiResponse { 988 | app_update: ApplicationDetails, 989 | } 990 | 991 | // oauth_apps 992 | 993 | declare class OAuthApplication { 994 | name: string, 995 | app_id: number, 996 | last_used?: string, 997 | scopes: AccessScope[], 998 | app_markup_percentage: number, 999 | } 1000 | 1001 | declare class OAuthApplicationsRequest extends ApiRequest { 1002 | oauth_apps: 1, 1003 | revoke_app: number, 1004 | } 1005 | 1006 | declare class OAuthApplicationsResponse extends ApiResponse { 1007 | oauth_apps: OAuthApplication[], 1008 | } 1009 | 1010 | // change_password 1011 | 1012 | declare class ChangePasswordRequest extends ApiRequest { 1013 | change_password: 1, 1014 | old_password: string, 1015 | new_password: string, 1016 | } 1017 | 1018 | declare class ChangePasswordResponse extends ApiResponse { 1019 | change_password: 1, 1020 | } 1021 | 1022 | // jp_knowledge_test 1023 | 1024 | declare class JapanKnowledgeTestRequest extends ApiRequest { 1025 | jp_knowledge_test: 1, 1026 | score: number, 1027 | status: 'pass' | 'fail', 1028 | } 1029 | 1030 | declare class JapanKnowledgeTestResponse extends ApiResponse { 1031 | jp_knowledge_test: { 1032 | test_taken_epoch: Epoch, 1033 | }, 1034 | } 1035 | 1036 | // get_financial_assessment 1037 | 1038 | declare class FinancialAssessmentDetails { 1039 | score: number, 1040 | forex_trading_frequency: string, 1041 | forex_trading_experience: string, 1042 | indices_trading_frequency: string, 1043 | indices_trading_experience: string, 1044 | commodities_trading_frequency: string, 1045 | commodities_trading_experience: string, 1046 | stocks_trading_experience: string, 1047 | stocks_trading_frequency: string, 1048 | other_derivatives_trading_frequency: string, 1049 | other_derivatives_trading_experience: string, 1050 | other_instruments_trading_frequency: string, 1051 | other_instruments_trading_experience: string, 1052 | employment_industry: string, 1053 | education_level: string, 1054 | income_source: string, 1055 | net_income: string, 1056 | estimated_worth: string, 1057 | } 1058 | 1059 | declare class GetFinancialAssesmentRequest extends ApiRequest { 1060 | get_financial_assessment: 1, 1061 | } 1062 | 1063 | declare class GetFinancialAssesmentResponse extends ApiResponse { 1064 | get_financial_assessment: FinancialAssessmentDetails, 1065 | } 1066 | 1067 | // set_financial_assessment 1068 | 1069 | declare class SetFinancialAssessmentRequest extends ApiRequest mixins FinancialAssessmentDetails { 1070 | set_financial_assessment: 1, 1071 | } 1072 | 1073 | declare class SetFinancialAssessmentResponse extends ApiResponse { 1074 | set_financial_assessment: FinancialAssessmentDetails, 1075 | } 1076 | 1077 | // new_account_maltainvest 1078 | 1079 | declare class CreateMaltainvestAccountOptions { 1080 | salutation: 'Mr' | 'Mrs' | 'Ms' | 'Miss', 1081 | first_name: string, 1082 | last_name: string, 1083 | date_of_birth: string, 1084 | residence: string, 1085 | address_line_1: string, 1086 | address_line_2?: string, 1087 | address_city: string, 1088 | address_state?: string, 1089 | address_postcode?: string, 1090 | phone: string, 1091 | secret_question: string, 1092 | secret_answer: string, 1093 | affiliate_token?: string, 1094 | forex_trading_experience: string, 1095 | forex_trading_frequency: string, 1096 | indices_trading_experience: string, 1097 | indices_trading_frequency: string, 1098 | commodities_trading_experience: string, 1099 | commodities_trading_frequency: string, 1100 | stocks_trading_experience: string, 1101 | stocks_trading_frequency: string, 1102 | other_derivatives_trading_experience: string, 1103 | other_derivatives_trading_frequency: string, 1104 | other_instruments_trading_experience: string, 1105 | other_instruments_trading_frequency: string, 1106 | employment_industry: string, 1107 | education_level: string, 1108 | income_source: string, 1109 | net_income: string, 1110 | estimated_worth: string, 1111 | accept_risk: number, 1112 | } 1113 | 1114 | declare class CreateMaltainvestAccountRequest extends ApiRequest mixins CreateMaltainvestAccountOptions { 1115 | new_account_maltainvest: 1, 1116 | } 1117 | 1118 | declare class CreateMaltainvestAccountResponse extends ApiResponse { 1119 | new_account_maltainvest: { 1120 | client_id: string, 1121 | landing_company: string, 1122 | landing_company_short: string, 1123 | oauth_token: string, 1124 | }, 1125 | } 1126 | 1127 | // new_account_real 1128 | 1129 | declare class CreateRealAccountOptions { 1130 | salutation: 'Mr' | 'Mrs' | 'Ms' | 'Miss', 1131 | first_name: string, 1132 | last_name: string, 1133 | date_of_birth: string, 1134 | residence: string, 1135 | address_line_1: string, 1136 | address_line_2?: string, 1137 | address_city: string, 1138 | address_state?: string, 1139 | address_postcode?: string, 1140 | phone: string, 1141 | secret_question: string, 1142 | secret_answer: string, 1143 | affiliate_token?: string, 1144 | } 1145 | 1146 | declare class CreateRealAccountRequest extends ApiRequest mixins CreateRealAccountOptions { 1147 | new_account_real: 1, 1148 | } 1149 | 1150 | declare class CreateRealAccountResponse extends ApiResponse { 1151 | new_account_real: { 1152 | client_id: string, 1153 | landing_company: string, 1154 | landing_company_short: string, 1155 | oauth_token: string, 1156 | }, 1157 | } 1158 | 1159 | // new_sub_account 1160 | 1161 | declare class CreateRealSubAccountRequest extends ApiRequest { 1162 | new_account_real: 1, 1163 | salutation: 'Mr' | 'Mrs' | 'Ms' | 'Miss', 1164 | first_name: string, 1165 | last_name: string, 1166 | date_of_birth: string, 1167 | residence: string, 1168 | address_line_1: string, 1169 | address_line_2?: string, 1170 | address_city: string, 1171 | address_state?: string, 1172 | address_postcode?: string, 1173 | phone: string, 1174 | secret_question: string, 1175 | secret_answer: string, 1176 | affiliate_token?: string, 1177 | } 1178 | 1179 | declare class CreateRealSubAccountResponse extends ApiResponse { 1180 | new_account_real: { 1181 | client_id: string, 1182 | landing_company: string, 1183 | landing_company_short: string, 1184 | }, 1185 | } 1186 | 1187 | // set_account_currency 1188 | 1189 | declare class SetAccountCurrencyRequest extends ApiRequest { 1190 | set_account_currency: string, 1191 | } 1192 | 1193 | declare class SetAccountCurrencyResponse extends ApiResponse { 1194 | set_account_currency: 0 | 1, 1195 | } 1196 | 1197 | // set_self_exclusion 1198 | 1199 | declare class SetSelfExclusionRequest extends ApiRequest mixins SelfExclusionSettings { 1200 | set_self_exclusion: 1, 1201 | } 1202 | 1203 | declare class SetSelfExclusionResponse extends ApiResponse { 1204 | set_self_exclusion: 1, 1205 | } 1206 | 1207 | // set_settings 1208 | 1209 | declare class SetSettingsRequest extends ApiRequest mixins AccountSettings { 1210 | set_settings: 1, 1211 | } 1212 | 1213 | declare class SetSettingsResponse extends ApiResponse { 1214 | set_settings: 1, 1215 | } 1216 | 1217 | // tnc_approval 1218 | 1219 | declare class TncApprovalRequest extends ApiRequest { 1220 | tnc_approval: 1, 1221 | ukgc_funds_protection: 1, 1222 | } 1223 | 1224 | declare class TncApprovalResponse extends ApiResponse { 1225 | tnc_approval: 1, 1226 | } 1227 | 1228 | // cashier 1229 | 1230 | declare class CashierRequest extends ApiRequest { 1231 | cashier: 'deposit' | 'withdraw', 1232 | verification_code: string, 1233 | } 1234 | 1235 | declare class CashierResponse extends ApiResponse { 1236 | cashier: string, 1237 | } 1238 | 1239 | // cashier_password 1240 | 1241 | declare class CashierPasswordRequest extends ApiRequest { 1242 | cashier_password: 1, 1243 | unlock_password: string, 1244 | lock_password: string, 1245 | } 1246 | 1247 | declare class CashierPasswordResponse extends ApiResponse { 1248 | cashier_password: 0 | 1, 1249 | } 1250 | 1251 | // paymentagent_withdraw 1252 | 1253 | declare class PaymentAgentWithdrawRequest extends ApiRequest { 1254 | paymentagent_withdraw: 1, 1255 | paymentagent_loginid: string, 1256 | currency: string, 1257 | amount: number, 1258 | verification_code: string, 1259 | description: string, 1260 | dry_run?: 1, 1261 | } 1262 | 1263 | declare class PaymentAgentWithdrawResponse extends ApiResponse { 1264 | paymentagent_withdraw: 1 | 2, 1265 | paymentagent_name: string, 1266 | transaction_id: number, 1267 | } 1268 | 1269 | // paymentagent_transfer 1270 | 1271 | declare class PaymentAgentTransferRequest extends ApiRequest { 1272 | paymentagent_transfer: 1, 1273 | transfer_to: string, 1274 | currency: string, 1275 | amount: number, 1276 | dry_run?: 1, 1277 | } 1278 | 1279 | declare class PaymentAgentTransferResponse extends ApiResponse { 1280 | paymentagent_transfer: 1 | 2, 1281 | client_to_full_name: string, 1282 | client_to_loginid: string, 1283 | transaction_id: number, 1284 | } 1285 | 1286 | // transfer_between_accounts 1287 | 1288 | declare class Account { 1289 | loginid: string, 1290 | balance: string, 1291 | currency: string, 1292 | } 1293 | 1294 | declare class TransferBetweenAccountsRequest extends ApiRequest { 1295 | transfer_between_accounts: 1, 1296 | account_from: string, 1297 | account_to: string, 1298 | currency: string, 1299 | amount: number, 1300 | } 1301 | 1302 | declare class TransferBetweenAccountsResponse extends ApiResponse { 1303 | transfer_between_accounts: number, 1304 | accounts: Account[], 1305 | client_to_full_name: string, 1306 | client_to_loginid: string, 1307 | transaction_id: number, 1308 | } 1309 | -------------------------------------------------------------------------------- /flow-typed/binary-live-api.js.flow: -------------------------------------------------------------------------------- 1 | type LivePromise = Promise; 2 | 3 | declare class InitParams { 4 | apiUrl?: string, 5 | language?: string, 6 | appId?: number, 7 | websocket?: WebSocket, 8 | connection?: WebSocket, 9 | keepAlive?: boolean, 10 | } 11 | 12 | declare class WebsocketMessage { 13 | data: string, 14 | } 15 | 16 | declare interface ApiCalls { 17 | getActiveSymbolsBrief(): Promise; 18 | getActiveSymbolsFull(): Promise; 19 | getAssetIndex(): Promise; 20 | authorize(token: string): Promise; 21 | 22 | getContractsForSymbol(symbol: string): Promise; 23 | unsubscribeFromTick(symbol: string): LivePromise; 24 | unsubscribeFromTicks(symbols: string[]): LivePromise; 25 | unsubscribeByID(id: number): LivePromise; 26 | unsubscribeFromAllTicks(): LivePromise; 27 | unsubscribeFromAllProposals(): LivePromise; 28 | unsubscribeFromAllPortfolios(): LivePromise; 29 | unsubscribeFromAllProposalsOpenContract(): LivePromise; 30 | getLandingCompany(landingCompany: string): Promise; 31 | getLandingCompanyDetails(landingCompany: string): Promise; 32 | createVirtualAccount(options: Object): Promise; 33 | ping(): Promise; 34 | getPaymentAgentsForCountry(countryCode: string): Promise; 35 | getPayoutCurrencies(): Promise; 36 | getPriceProposalForContract(options: Object): Promise; 37 | subscribeToPriceForContractProposal(options: Object): Promise; 38 | getResidences(): Promise; 39 | getStatesForCountry(countryCode: string): Promise; 40 | subscribeToTick(symbol: string): LivePromise; 41 | subscribeToTicks(symbols: string[]): LivePromise; 42 | getTickHistory(symbol: string, options: Object): Promise; 43 | getCandles(symbol: string, options: Object): Promise; 44 | getCandlesForLastNDays(symbol: string, ndays: number): Promise; 45 | getServerTime(): Promise; 46 | getTradingTimes(date: Date): Promise; 47 | verifyEmail(email: string, type: string): Promise; 48 | getWebsiteStatus(): Promise; 49 | 50 | getAccountLimits(): Promise; 51 | getAccountSettings(): Promise; 52 | getAccountStatus(): Promise; 53 | getSelfExclusion(): Promise; 54 | logOut(): Promise; 55 | getStatement(options: StatementOptions): Promise; 56 | getPortfolio(): Promise; 57 | getProfitTable(options: ProfitTableOptions): Promise; 58 | getRealityCheckSummary (): Promise; 59 | subscribeToBalance(): Promise; 60 | unsubscribeFromBalance(): LivePromise; 61 | subscribeToOpenContract(contractId: number): LivePromise; 62 | getContractInfo(contractId: number): LivePromise; 63 | subscribeToAllOpenContracts(): LivePromise; 64 | unsubscribeFromAllOpenContracts(): LivePromise; 65 | subscribeToTransactions(): Promise; 66 | unsubscribeFromTransactions(): LivePromise; 67 | 68 | buyContract(contractId: number, price: number): Promise; 69 | sellContract(contractId: number, price: number): Promise; 70 | sellExpiredContracts(): Promise; 71 | topUpVirtualAccount(): Promise; 72 | 73 | getCashierLockStatus(): Promise; 74 | setCashierLock(options: Object): LivePromise; 75 | withdrawToPaymentAgent(options: Object): Promise; 76 | paymentAgentTransfer(options: Object): Promise; 77 | transferBetweenAccounts(options: Object): Promise; 78 | 79 | deleteApiToken(token: string): Promise; 80 | getApiTokens(): Promise; 81 | createApiToken(token: string, scopes: string[]): Promise; 82 | changePassword(oldPassword: string, newPassword: string): Promise; 83 | registerApplication(options: ApplicationDetails): Promise; 84 | getAllAppList(): Promise; 85 | getAppslistById(appid: number): Promise; 86 | deleteApplication(appid: number): Promise; 87 | createRealAccountMaltaInvest(options: CreateMaltainvestAccountOptions): Promise; 88 | createRealAccount(options: CreateRealAccountOptions): Promise; 89 | setAccountCurrency(currency: string): Promise; 90 | setSelfExclusion(options: SelfExclusionSettings): Promise; 91 | setAccountSettings(options: AccountSettings): Promise; 92 | setTnCApproval(): Promise; 93 | } 94 | 95 | declare module 'binary-live-api' { 96 | declare class LiveApi extends ApiCalls { 97 | [key: string]: LivePromise; 98 | 99 | constructor(initParams: InitParams): void; 100 | connect(connection: ?WebSocket): void; 101 | disconnect(): void; 102 | changeLanguage(ln: string): void; 103 | isReady(): boolean; 104 | sendRaw(json: Object): ?LivePromise; 105 | send(json: Object): ?LivePromise; 106 | execute(func: () => void): void; 107 | 108 | } 109 | 110 | declare class ApiState { 111 | constructor(): void; 112 | resetState(): void; 113 | getState(): Object; 114 | authorize(token: string): void; 115 | subscribeToBalance(): void; 116 | unsubscribeFromBalance(): void; 117 | subscribeToAllOpenContracts(): void; 118 | unsubscribeFromAllOpenContracts(): void; 119 | subscribeToTransactions(): void; 120 | unsubscribeFromTransactions(): void; 121 | subscribeToTick(symbol: string): void; 122 | subscribeToTicks(symbol: string[]): void; 123 | unsubscribeFromTick(symbol: string): void; 124 | unsubscribeFromTicks(symbol: string[]): void; 125 | unsubscribeFromAllTicks():void; 126 | subscribeToPriceForContractProposal(options: Object): void; 127 | unsubscribeFromAllProposals(): void; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gh = require('gulp-gh-pages'); 3 | var webpack = require('webpack'); 4 | var gutil = require('gulp-util'); 5 | var pkg = require('./package.json'); 6 | /** 7 | * Push build to gh-pages 8 | */ 9 | 10 | gulp.task('build', function(callback) { 11 | webpack(require('./webpack.config.js'), function(err, stats) { 12 | if(err) throw new gutil.PluginError("webpack", err); 13 | gutil.log("[webpack]", stats.toString()); 14 | 15 | callback(); 16 | }); 17 | }); 18 | 19 | gulp.task('versioning', ['build'], function () { 20 | var v = pkg.version; 21 | return gulp.src(['lib/*.*']) 22 | .pipe(gulp.dest('lib/' + v)); 23 | }); 24 | 25 | gulp.task('deploy', ['versioning'], function () { 26 | return gulp.src(["./lib/**/*", "./CNAME"]) 27 | .pipe(gh({ force: true })); 28 | }); 29 | 30 | gulp.task('deploy-prod', ['versioning'], function () { 31 | return gulp.src(["./lib/**/*", "./CNAME"]) 32 | .pipe(gh({ force: true, origin: 'upstream' })); 33 | }); 34 | -------------------------------------------------------------------------------- /mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 10000 ./src/**/__tests__/*.js 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "binary-live-api", 3 | "version": "29.0.2", 4 | "description": "Library to consume Binary.com WebSocket API", 5 | "main": "lib/binary-live-api.js", 6 | "devDependencies": { 7 | "babel-cli": "^6.26.0", 8 | "babel-core": "^6.24.0", 9 | "babel-eslint": "10.0.3", 10 | "babel-loader": "^6.4.1", 11 | "babel-plugin-transform-class-properties": "^6.23.0", 12 | "babel-plugin-transform-export-extensions": "^6.22.0", 13 | "babel-plugin-transform-flow-strip-types": "^6.22.0", 14 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 15 | "babel-polyfill": "^6.23.0", 16 | "babel-preset-env": "^1.4.0", 17 | "coveralls": "^3.0.5", 18 | "eslint": "~4.18.2", 19 | "eslint-config-airbnb": "^14.1.0", 20 | "eslint-config-binary": "^1.0.2", 21 | "eslint-config-prettier": "^1.7.0", 22 | "eslint-loader": "^1.6.3", 23 | "eslint-plugin-import": "^2.2.0", 24 | "eslint-plugin-jsx-a11y": "^4.0.0", 25 | "eslint-plugin-react": "^6.10.3", 26 | "husky": "^0.13.3", 27 | "jest-cli": "^19.0.2", 28 | "lint-staged": "^3.4.0", 29 | "prettier-eslint-cli": "^3.3.0", 30 | "rimraf": "^2.6.1", 31 | "webpack": "^2.3.0", 32 | "ws": "^7.0.0" 33 | }, 34 | "scripts": { 35 | "clean": "rimraf lib dist", 36 | "test": "jest --forceExit", 37 | "test:coverage": "jest --forceExit --coverage", 38 | "test:coveralls": "npm run test:coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", 39 | "start": "webpack --watch", 40 | "test:eslint": "eslint src/*.js src/calls/*.js", 41 | "test:flow": "flow check --all --show-all-errors", 42 | "test:full": "npm run test:eslint && npm run test:coveralls", 43 | "build": "webpack", 44 | "lint": "eslint src", 45 | "prepublish": "webpack", 46 | "precommit": "lint-staged" 47 | }, 48 | "repository": { 49 | "type": "git", 50 | "url": "git+https://github.com/binary-com/binary-live-api.git" 51 | }, 52 | "author": "Boris @ Binary.com", 53 | "license": "MIT", 54 | "bugs": { 55 | "url": "https://github.com/binary-com/binary-live-api/issues" 56 | }, 57 | "homepage": "https://github.com/binary-com/binary-live-api#readme", 58 | "babel": { 59 | "babelrc": false, 60 | "presets": [ 61 | [ 62 | "env", 63 | { 64 | "targets": { 65 | "browsers": [ 66 | "last 2 versions", 67 | "ios_saf >= 8", 68 | "not IE <= 10", 69 | "chrome >= 49", 70 | "firefox >= 49", 71 | "> 1%" 72 | ] 73 | }, 74 | "loose": true 75 | } 76 | ] 77 | ], 78 | "plugins": [ 79 | "transform-export-extensions", 80 | "transform-object-rest-spread", 81 | "transform-class-properties", 82 | "transform-flow-strip-types" 83 | ] 84 | }, 85 | "lint-staged": { 86 | "*.js": [ 87 | "prettier-eslint --write", 88 | "git add" 89 | ] 90 | }, 91 | "dependencies": { 92 | "binary-utils": "^4.21.0", 93 | "gulp": "^4.0.2", 94 | "gulp-gh-pages": "^0.5.4", 95 | "rx-lite": "^4.0.8" 96 | }, 97 | "resolutions": { 98 | "braces": "3.0.2", 99 | "acorn": "5.7.4", 100 | "lodash.template": "4.5.0", 101 | "minimist": "0.2.1" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ApiState.js: -------------------------------------------------------------------------------- 1 | const getInitialState = () => ({ 2 | token : undefined, 3 | balance : false, 4 | contracts : new Set(), 5 | allContract : false, 6 | transactions : false, 7 | ticks : new Set(), 8 | ticksHistory : new Map(), 9 | candlesHistory : new Map(), 10 | proposals : new Set(), 11 | streamIdMapping: new Map(), 12 | }); 13 | 14 | export default class ApiState { 15 | constructor() { 16 | this.state = getInitialState(); 17 | } 18 | 19 | resetState = () => { 20 | this.state = getInitialState(); 21 | }; 22 | 23 | getState = () => this.state; 24 | 25 | authorize = (token: string) => { 26 | this.state.token = token; 27 | }; 28 | 29 | subscribeToBalance = () => { 30 | this.state.balance = true; 31 | }; 32 | 33 | unsubscribeFromBalance = () => { 34 | this.state.balance = false; 35 | }; 36 | 37 | subscribeToOpenContract = (contractId: string, streamId: string) => { 38 | if (streamId) { 39 | this.state.contracts.add(contractId); 40 | this.state.streamIdMapping.set(streamId, contractId); 41 | } 42 | }; 43 | 44 | unsubscribeFromAllOpenContracts = () => { 45 | this.state.contracts.clear(); 46 | this.state.allContract = false; 47 | }; 48 | 49 | subscribeToAllOpenContracts = () => { 50 | this.state.allContract = true; 51 | }; 52 | 53 | subscribeToTransactions = () => { 54 | this.state.transactions = true; 55 | }; 56 | 57 | unsubscribeFromTransactions = () => { 58 | this.state.transactions = false; 59 | }; 60 | 61 | subscribeToTick = (symbol: string) => { 62 | this.state.ticks.add(symbol); 63 | }; 64 | 65 | subscribeToTicks = (symbols: string[]) => { 66 | symbols.forEach(this.subscribeToTick); 67 | }; 68 | 69 | unsubscribeFromAllTicks = () => { 70 | this.state.ticks.clear(); 71 | this.state.ticksHistory.clear(); 72 | }; 73 | 74 | unsubscribeFromAllCandles = () => { 75 | this.state.candlesHistory.clear(); 76 | }; 77 | 78 | getTickHistory = (symbol: string, params: Object) => { 79 | if (params && params.subscribe === 1) { 80 | if (params.style === 'candles') { 81 | this.state.candlesHistory.set(symbol, params); 82 | } else { 83 | this.state.ticksHistory.set(symbol, params); 84 | } 85 | } 86 | }; 87 | 88 | subscribeToPriceForContractProposal = (options: Object, streamId: string) => { 89 | if (streamId) { 90 | this.state.proposals.add(options); 91 | this.state.streamIdMapping.set(streamId, options); 92 | } 93 | }; 94 | 95 | unsubscribeFromAllProposals = () => { 96 | this.state.proposals.clear(); 97 | }; 98 | 99 | // special care needed to forget subscription, as backends rely on 100 | // and id instead of more natural keys like symbol and payload 101 | unsubscribeByID = id => { 102 | this.state.streamIdMapping.forEach((payload, streamId) => { 103 | if (streamId === id) { 104 | this.state.contracts.delete(payload); 105 | this.state.proposals.delete(payload); 106 | } 107 | }); 108 | this.state.streamIdMapping.delete(id); 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /src/LiveApi.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rx-lite'; 2 | import { getUniqueId } from 'binary-utils'; 3 | import LiveEvents from './LiveEvents'; 4 | import ServerError from './ServerError'; 5 | import * as calls from './calls'; 6 | import ApiState from './ApiState'; 7 | import * as customCalls from './custom'; 8 | 9 | getUniqueId(); // skip 0 value 10 | const defaultApiUrl = 'wss://frontend.binaryws.com/websockets/v3'; 11 | const nullFunc = () => {}; 12 | const MockWebSocket = nullFunc; 13 | let WebSocket = typeof window !== 'undefined' ? window.WebSocket : MockWebSocket; 14 | 15 | const shouldIgnoreError = (error: Error): boolean => error.code === 'AlreadySubscribed'; 16 | 17 | export default class LiveApi { 18 | token: string; 19 | apiUrl: string; 20 | language: string; 21 | appId: number; 22 | brand: string; 23 | socket: WebSocket; 24 | bufferedSends: Object[]; 25 | bufferedExecutes: () => void[]; 26 | unresolvedPromises: Object; 27 | events: LiveEvents; 28 | apiState: ApiState; 29 | 30 | constructor(initParams: InitParams) { 31 | const { 32 | apiUrl = defaultApiUrl, 33 | language = 'en', 34 | appId = 0, 35 | brand = '', 36 | sendSpy = () => {}, 37 | websocket, 38 | connection, 39 | keepAlive, 40 | useRx = false, 41 | } = initParams || {}; 42 | 43 | this.apiUrl = apiUrl; 44 | this.language = language; 45 | this.appId = appId; 46 | this.brand = brand; 47 | this.sendSpy = sendSpy; 48 | 49 | // experimental: use at your own risk 50 | this.useRx = useRx; 51 | 52 | if (keepAlive) { 53 | setInterval(() => this.ping(), 60 * 1000); 54 | } 55 | 56 | if (websocket) { 57 | WebSocket = websocket; 58 | } 59 | 60 | this.bufferedSends = []; 61 | this.bufferedExecutes = []; 62 | this.unresolvedPromises = {}; 63 | 64 | this.events = new LiveEvents(); 65 | this.apiState = new ApiState(); 66 | 67 | if (useRx) { 68 | this.uncompleteOneTimeObs = {}; 69 | this.uncompleteStreamObs = {}; 70 | } 71 | 72 | this.authStream = Observable.create(observer => { 73 | this.onAuth = observer; 74 | 75 | return (): void => { 76 | this.onAuth = null; 77 | }; 78 | }); 79 | 80 | this.bindCallsAndStateMutators(); 81 | 82 | this.connect(connection); 83 | } 84 | 85 | bindCallsAndStateMutators(): void { 86 | Object.keys(calls).forEach(callName => { 87 | this[callName] = (...params) => this.sendAndUpdateState(callName, ...params); 88 | }); 89 | 90 | Object.keys(customCalls).forEach(callName => { 91 | this[callName] = (...params) => customCalls[callName](this, ...params); // seems to be a good place to do some simple cache 92 | }); 93 | } 94 | 95 | connect(connection: ?WebSocket): void { 96 | const optionalParam = this.brand ? `&brand=${this.brand}` : ''; 97 | const urlPlusParams = `${this.apiUrl}?l=${this.language}&app_id=${this.appId}${optionalParam}`; 98 | 99 | Object.keys(this.unresolvedPromises).forEach(reqId => { 100 | const disconnectedError = new Error('Websocket disconnected before response received.'); 101 | disconnectedError.name = 'DisconnectError'; 102 | this.unresolvedPromises[reqId].reject(disconnectedError); 103 | delete this.unresolvedPromises[reqId]; 104 | }); 105 | 106 | try { 107 | this.socket = connection || new WebSocket(urlPlusParams); 108 | } catch (err) { 109 | // swallow connection error, we can't do anything about it 110 | } finally { 111 | this.socket.onopen = this.onOpen; 112 | this.socket.onclose = () => this.connect(); 113 | this.socket.onmessage = this.onMessage; 114 | } 115 | } 116 | 117 | onOpen = (): void => { 118 | this.resubscribe(); 119 | this.executeBufferedExecutes(); 120 | }; 121 | 122 | disconnect = (): void => { 123 | this.token = ''; 124 | this.socket.onclose = nullFunc; 125 | try { 126 | this.socket.close(); 127 | } catch (e) { 128 | // ignore the error 129 | } 130 | }; 131 | resubscribe = (): void => { 132 | const { token } = this.apiState.getState(); 133 | 134 | const { authorized, unauthorized } = this.resubscriptions; 135 | 136 | this.authStream.take(1).subscribe(() => { 137 | Object.keys(authorized).forEach(msgType => authorized[msgType]()); 138 | this.sendBufferedSends(); 139 | }); 140 | 141 | Object.keys(unauthorized).forEach(msgType => unauthorized[msgType]()); 142 | 143 | if (token) { 144 | this.authorize(token); 145 | } else { 146 | // this need to be called last as it mayb mutate 147 | // ticksHistory, candlesHistory and proposals 148 | this.sendBufferedSends(); 149 | } 150 | }; 151 | 152 | getInState = name => this.apiState.getState()[name]; 153 | resubscriptions = { 154 | authorized: { 155 | balance: (): void => { 156 | if (this.getInState('balance')) { 157 | this.subscribeToBalance(); 158 | } 159 | }, 160 | transaction: (): void => { 161 | if (this.getInState('transactions')) { 162 | this.subscribeToTransactions(); 163 | } 164 | }, 165 | proposal_open_contract: (): void => { 166 | if (this.getInState('allContract')) { 167 | this.subscribeToAllOpenContracts(); 168 | } 169 | this.getInState('contracts').forEach(id => this.subscribeToOpenContract(id)); 170 | }, 171 | }, 172 | unauthorized: { 173 | tick: (): void => { 174 | if (this.getInState('ticks').size !== 0) { 175 | this.subscribeToTicks([...this.getInState('ticks')]); 176 | } 177 | 178 | this.getInState('ticksHistory').forEach((param, symbol) => this.getTickHistory(symbol, param)); 179 | }, 180 | ohlc: (): void => { 181 | this.getInState('candlesHistory').forEach((param, symbol) => this.getTickHistory(symbol, param)); 182 | }, 183 | proposal: (): void => { 184 | this.getInState('proposals').forEach(proposal => this.subscribeToPriceForContractProposal(proposal)); 185 | }, 186 | }, 187 | }; 188 | 189 | queuedResubscriptions = new Set(); 190 | shouldResubscribeOnError = (json: Object): void => { 191 | const { msg_type: msgType, error } = json; 192 | 193 | const shouldResubscribe = [ 194 | 'CallError', 195 | 'WrongResponse', 196 | 'GetProposalFailure', 197 | 'ProposalArrayFailure', 198 | 'ContractValidationError', 199 | 'ContractCreationFailure', 200 | ].includes(error.code); 201 | 202 | if (!shouldResubscribe) { 203 | return false; 204 | } 205 | 206 | Object.keys(this.resubscriptions).forEach(k => { 207 | const stream = this.resubscriptions[k]; 208 | const type = Object.keys(stream).find(t => t === msgType); 209 | 210 | if (type && !this.queuedResubscriptions.has(type)) { 211 | this.queuedResubscriptions.add(type); 212 | setTimeout(() => { 213 | this.queuedResubscriptions.delete(type); 214 | stream[type](); 215 | }, 500); 216 | } 217 | }); 218 | 219 | return true; 220 | }; 221 | changeLanguage = (ln: string): void => { 222 | if (ln === this.language) { 223 | return; 224 | } 225 | this.socket.onclose = nullFunc; 226 | try { 227 | this.socket.close(); 228 | } catch (e) { 229 | // ignore the error 230 | } 231 | this.language = ln; 232 | this.connect(); 233 | }; 234 | 235 | isReady = (): boolean => !!this.socket && this.socket.readyState === 1; 236 | 237 | sendBufferedSends = (): void => { 238 | while (this.bufferedSends.length > 0) { 239 | this.bufferedSends.shift()(); 240 | } 241 | }; 242 | 243 | executeBufferedExecutes = (): void => { 244 | while (this.bufferedExecutes.length > 0) { 245 | this.bufferedExecutes.shift()(); 246 | } 247 | }; 248 | 249 | resolvePromiseForResponse = (json: Object): LivePromise => { 250 | if (typeof json.req_id === 'undefined') { 251 | return Promise.resolve(); 252 | } 253 | 254 | const reqId = json.req_id.toString(); 255 | const promise = this.unresolvedPromises[reqId]; 256 | 257 | if (!promise) { 258 | if (json.error) { 259 | this.shouldResubscribeOnError(json); 260 | } 261 | 262 | return Promise.resolve(); 263 | } 264 | 265 | delete this.unresolvedPromises[reqId]; 266 | if (!json.error) { 267 | return promise.resolve(json); 268 | } 269 | 270 | if (!shouldIgnoreError(json.error)) { 271 | return promise.reject(new ServerError(json)); 272 | } 273 | 274 | return Promise.resolve(); 275 | }; 276 | 277 | publishToObservables = (json: Object): void => { 278 | if (typeof json.req_id === 'undefined') { 279 | return; 280 | } 281 | 282 | const reqId = json.req_id.toString(); 283 | 284 | const onetimeObs = this.uncompleteOneTimeObs[reqId]; 285 | 286 | const streamObs = this.uncompleteStreamObs[reqId]; 287 | 288 | if (!onetimeObs && !streamObs) { 289 | return; 290 | } 291 | 292 | if (!json.error) { 293 | if (onetimeObs) { 294 | onetimeObs.onNext(json); 295 | onetimeObs.onCompleted(); 296 | } else { 297 | streamObs.onNext(json); 298 | } 299 | return; 300 | } 301 | 302 | if (!shouldIgnoreError(json.error)) { 303 | if (onetimeObs) { 304 | onetimeObs.onError(new ServerError(json)); 305 | } else { 306 | streamObs.onError(new ServerError(json)); 307 | } 308 | } 309 | }; 310 | 311 | onMessage = (message: MessageEvent): LivePromise => { 312 | const json = JSON.parse(message.data); 313 | 314 | if (json.msg_type === 'authorize' && this.onAuth) { 315 | if (!json.error) { 316 | this.onAuth.next(); 317 | } 318 | } 319 | 320 | if (!json.error) { 321 | this.events.emit(json.msg_type, json); 322 | } else { 323 | this.events.emit('error', json); 324 | } 325 | 326 | if (this.useRx) { 327 | return this.publishToObservables(json); 328 | } 329 | 330 | return this.resolvePromiseForResponse(json); 331 | }; 332 | 333 | generatePromiseOrObservable = (json: Object): LivePromise => { 334 | const reqId = json.req_id.toString(); 335 | 336 | if (this.useRx) { 337 | const obs = Observable.create(observer => { 338 | // if call is an subscription, store it in uncompleteStreamObs 339 | // ticks is a special case that's a stream without subscribe keyword 340 | if (json.subscribe || json.ticks) { 341 | this.uncompleteStreamObs[reqId] = observer; 342 | } else { 343 | this.uncompleteOneTimeObs[reqId] = observer; 344 | } 345 | 346 | return () => { 347 | delete this.uncompleteOneTimeObs[reqId]; 348 | delete this.uncompleteStreamObs[reqId]; 349 | }; 350 | }); 351 | 352 | const published = obs.publish(); 353 | 354 | return published; // use hot observables 355 | } 356 | 357 | return new Promise((resolve, reject) => { 358 | this.unresolvedPromises[reqId] = { resolve, reject }; 359 | }); 360 | }; 361 | 362 | sendAndUpdateState = (callName: string, ...param: Object): ?LivePromise => { 363 | const reqId = getUniqueId(); 364 | const actualPaylod = calls[callName](...param); 365 | const json = { 366 | req_id: reqId, 367 | ...actualPaylod, 368 | }; 369 | 370 | const socketSend = () => { 371 | this.sendSpy(JSON.stringify(json)); 372 | this.socket.send(JSON.stringify(json)); 373 | if (this.apiState[callName]) { 374 | this.apiState[callName](...param); 375 | } 376 | }; 377 | 378 | if (this.isReady()) { 379 | socketSend(); 380 | } else { 381 | this.bufferedSends.push(socketSend); 382 | } 383 | 384 | if (typeof json.req_id !== 'undefined') { 385 | // Set the stream id into ApiState so that we can unsubscribe later 386 | // TODO: hackish and need redo, this depends on object identity to works!!! 387 | const setState = r => { 388 | if (!this.apiState[callName]) return r; 389 | 390 | if (r.proposal_open_contract && r.proposal_open_contract.id) { 391 | this.apiState[callName](...param, r.proposal_open_contract.id); 392 | } else if (r.proposal && r.proposal.id) { 393 | this.apiState[callName](...param, r.proposal.id); 394 | } 395 | return r; 396 | }; 397 | 398 | if (this.useRx) { 399 | const connectableObs = this.generatePromiseOrObservable(json); 400 | connectableObs.take(1).forEach(setState); 401 | return connectableObs; 402 | } 403 | 404 | return this.generatePromiseOrObservable(json).then(setState); 405 | } 406 | 407 | return undefined; 408 | }; 409 | 410 | execute = (func: () => void): void => { 411 | if (this.isReady()) { 412 | func(); 413 | } else { 414 | this.bufferedExecutes.push(func); 415 | } 416 | }; 417 | 418 | // TODO: should we deprecate this? preserve for backward compatibility 419 | send = (json: Object): ?LivePromise => { 420 | console.warn('This method is deprecated, use high-level methods'); // eslint-disable-line 421 | const reqId = getUniqueId(); 422 | return this.sendRaw({ 423 | req_id: reqId, 424 | ...json, 425 | }); 426 | }; 427 | 428 | // TODO: should we deprecate this? preserve for backward compatibility 429 | sendRaw = (json: Object): ?LivePromise => { 430 | console.warn('This method is deprecated, use high-level methods'); // eslint-disable-line 431 | const socketSend = () => { 432 | this.sendSpy(JSON.stringify(json)); 433 | this.socket.send(JSON.stringify(json)); 434 | }; 435 | if (this.isReady()) { 436 | socketSend(); 437 | } else { 438 | this.bufferedSends.push(socketSend); 439 | } 440 | 441 | if (typeof json.req_id !== 'undefined') { 442 | return this.generatePromiseOrObservable(json); 443 | } 444 | 445 | return undefined; 446 | }; 447 | } 448 | -------------------------------------------------------------------------------- /src/LiveEvents.js: -------------------------------------------------------------------------------- 1 | type LivEventHandler = (msgData: Object) => void; 2 | 3 | export default class LiveEvents { 4 | messageHandlers: Object; 5 | 6 | constructor() { 7 | this.messageHandlers = {}; 8 | } 9 | 10 | emitSingle(msgType: string, msgData: Object) { 11 | const handlers = this.messageHandlers[msgType] || []; 12 | handlers.forEach(handler => { 13 | handler(msgData); 14 | }); 15 | } 16 | 17 | emitWildcard(msgData: Object) { 18 | const handlers = this.messageHandlers['*'] || []; 19 | handlers.forEach(handler => { 20 | handler(msgData); 21 | }); 22 | } 23 | 24 | emit(msgType: string, msgData: Object) { 25 | this.emitSingle(msgType, msgData); 26 | this.emitWildcard(msgData); 27 | } 28 | 29 | on(msgType: string, callback: LivEventHandler) { 30 | if (!this.messageHandlers[msgType]) { 31 | this.messageHandlers[msgType] = [callback]; 32 | } else { 33 | this.messageHandlers[msgType].push(callback); 34 | } 35 | } 36 | 37 | ignoreAll(msgType: string) { 38 | delete this.messageHandlers[msgType]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/OAuth.js: -------------------------------------------------------------------------------- 1 | export const oauthUrl = (appId: number): string => `https://www.binary.com/oauth2/authorize?app_id=${appId}`; 2 | 3 | export const oauthUrlWithLanguage = (appId: number, langCode: string): string => 4 | `https://www.binary.com/oauth2/authorize?app_id=${appId}&l=${langCode}`; 5 | 6 | type Account = { 7 | account: string, 8 | token: string, 9 | }; 10 | 11 | export const parseOAuthResponse = (responseUrl: string): Account[] => { 12 | const matcher = /acct\d=(\w+)&token\d=([\w-]+)/g; 13 | const urlParts = responseUrl.split('/redirect?'); 14 | if (urlParts.length !== 2) throw new Error('Not a valid url'); 15 | 16 | const params = urlParts[1].split(matcher); 17 | 18 | const accounts = []; 19 | 20 | for (let i = 1; i < params.length; i += 3) { 21 | accounts.push({ 22 | account: params[i], 23 | token : params[i + 1], 24 | }); 25 | } 26 | 27 | return accounts; 28 | }; 29 | -------------------------------------------------------------------------------- /src/ServerError.js: -------------------------------------------------------------------------------- 1 | export default class ServerError extends Error { 2 | stack: any; 3 | error: ApiErrorResponse; 4 | name: string; 5 | message: string; 6 | 7 | constructor(errorObj: ApiErrorResponse) { 8 | super(errorObj); 9 | 10 | this.stack = new Error().stack; 11 | this.error = errorObj; 12 | this.name = errorObj.error.code; 13 | 14 | const { error: { message }, echo_req } = errorObj; 15 | 16 | const echoStr = JSON.stringify(echo_req, null, 2); 17 | this.message = `[ServerError] ${message}\n${echoStr}`; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/__tests__/LiveApi-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('LiveApi', () => { 5 | let liveApi; 6 | 7 | beforeAll(() => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | }); 10 | 11 | it('can be created', () => { 12 | expect(liveApi).toBeDefined(); 13 | }); 14 | 15 | it('can be connected to', () => { 16 | expect(() => liveApi.connect()).not.toThrow(); 17 | }); 18 | 19 | it('can change language', () => { 20 | expect(() => liveApi.changeLanguage()).not.toThrow(); 21 | }); 22 | 23 | it('using api calls returns a Promise', () => { 24 | const response = liveApi.ping(); 25 | expect(typeof response.then).toBe('function'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/__tests__/LiveEvents-test.js: -------------------------------------------------------------------------------- 1 | import LiveEvents from '../LiveEvents'; 2 | 3 | describe('LiveEvents', () => { 4 | let liveEvents; 5 | 6 | beforeEach(() => { 7 | liveEvents = new LiveEvents(); 8 | }); 9 | 10 | it('can create object', () => { 11 | expect(liveEvents).toBeTruthy(); 12 | }); 13 | 14 | it('can subscribe to events', () => { 15 | expect(() => liveEvents.on('message', () => {})).not.toThrow(); 16 | }); 17 | 18 | it('can emit events', () => { 19 | expect(() => liveEvents.emit('message', {})).not.toThrow(); 20 | }); 21 | 22 | it('can receive emitted events', done => { 23 | liveEvents.on('message', done); 24 | liveEvents.emit('message'); 25 | }); 26 | 27 | it('wildcard event handler should catch any event', done => { 28 | liveEvents.on('*', done); 29 | liveEvents.emit('message'); 30 | }); 31 | 32 | it('can have multiple handlers per message', done => { 33 | let handleCount = 0; 34 | const handler = () => { 35 | handleCount++; 36 | if (handleCount === 3) done(); 37 | }; 38 | liveEvents.on('message', handler); 39 | liveEvents.on('message', handler); 40 | liveEvents.on('message', handler); 41 | liveEvents.emit('message'); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/__tests__/ServerError-test.js: -------------------------------------------------------------------------------- 1 | import ServerError from '../ServerError'; 2 | 3 | describe('ServerError', () => { 4 | it('can be thrown', () => { 5 | expect(() => { 6 | throw new ServerError({ error: {} }); 7 | }).toThrow(/ServerError/); 8 | }); 9 | 10 | it('contains all elements of the error response', () => { 11 | const errorResponse = { 12 | echo_req: { 13 | some_key: 'some_value', 14 | }, 15 | error: { 16 | message: 'Unrecognised request.', 17 | code : 'UnrecognisedRequest', 18 | }, 19 | msg_type: 'error', 20 | }; 21 | 22 | const error = new ServerError(errorResponse); 23 | const str = error.toString(); 24 | 25 | expect(str).toContain('ServerError'); 26 | expect(str).toContain('some_value'); 27 | expect(str).toContain('Unrecognised request.'); 28 | expect(error.name).toEqual('UnrecognisedRequest'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/__tests__/admin-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('admin', () => { 5 | let liveApi; 6 | 7 | beforeAll(() => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | }); 10 | 11 | it('should be able to call getApiToken without an error', async () => { 12 | expect(() => liveApi.getApiTokens()).not.toThrow(); 13 | }); 14 | 15 | it('should be able to call deleteApiToken function without an error', () => { 16 | expect(() => liveApi.deleteApiToken('token')).not.toThrow(); 17 | }); 18 | 19 | it('should be able to call createApiToken without an issue', () => { 20 | expect(() => liveApi.createApiToken('TokenName')).not.toThrow(); 21 | }); 22 | 23 | it('should be able to call changePassword', () => { 24 | expect(() => liveApi.changePassword('oldpassword', 'newpassword')).not.toThrow(); 25 | }); 26 | 27 | it('should be able to call the function registerApplication with no error', () => { 28 | expect(() => liveApi.registerApplication({ name: 'AppName', link: 'Applink' })).not.toThrow(); 29 | }); 30 | 31 | it('should be able to call getAllAppList function without throwing error ', () => { 32 | expect(() => liveApi.getAllAppList()).not.toThrow(); 33 | }); 34 | 35 | it('should be able to call getAppslistById function without throwing error', () => { 36 | expect(() => liveApi.getAppslistById(0)).not.toThrow(); 37 | }); 38 | 39 | it('should be able to call the deleteApplication function without throwing error', () => { 40 | expect(() => liveApi.deleteApplication(0)).not.toThrow(); 41 | }); 42 | 43 | it('it should be able to call the function createRealAccountMaltaInvest without error', () => { 44 | expect(() => 45 | liveApi.createRealAccountMaltaInvest({ 46 | name : 'name', 47 | username: 'username', 48 | }) 49 | ).not.toThrow(); 50 | }); 51 | 52 | it.skip('should be able to call createRealAccount function with no error', async () => 53 | expect( 54 | await liveApi.createRealAccount({ 55 | name : 'name', 56 | username: 'username', 57 | }) 58 | ).not.toThrow() 59 | ); 60 | 61 | it('should be able to call the function setAccountCurrency with no error', () => { 62 | expect(() => liveApi.setAccountCurrency('EUR')).not.toThrow(); 63 | }); 64 | 65 | it('should be able to call the function setSelfExclusion without error', () => { 66 | expect(() => liveApi.setSelfExclusion({ balance: 300, limit: 30 })).not.toThrow(); 67 | }); 68 | 69 | it('should be able to call setAccountSettings without throwing error', () => { 70 | expect(() => 71 | liveApi.setAccountSettings({ 72 | option1: 'option1', 73 | option2: 'option2', 74 | }) 75 | ).not.toThrow(); 76 | }); 77 | 78 | it('should be able to call setTnCApproval function without thrwoing error', () => { 79 | expect(() => liveApi.setTnCApproval()).not.toThrow(); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /src/__tests__/custom-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('custom', () => { 5 | let liveApi; 6 | const token = 'qdJ86Avvrsh0Le4'; 7 | beforeAll(() => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | }); 10 | 11 | describe('getDataForContract', () => { 12 | it('should get more extra ticks for non-tick-contract', async () => { 13 | await liveApi.authorize(token); 14 | const nonTickContractID = '8686424368'; 15 | const { ticks } = await liveApi.getDataForContract(() => 16 | liveApi.getContractInfo(nonTickContractID).then(r => r.proposal_open_contract) 17 | ); 18 | expect(ticks.length).toBe(165); 19 | }); 20 | 21 | it('should get exact number of ticks for tick-contract', async () => { 22 | await liveApi.authorize(token); 23 | const tickContractID = '8818581808'; 24 | const { ticks } = await liveApi.getDataForContract(() => 25 | liveApi.getContractInfo(tickContractID).then(r => r.proposal_open_contract) 26 | ); 27 | expect(ticks.length).toBe(11); 28 | }); 29 | 30 | it('should return candles if user request candles', async () => { 31 | await liveApi.authorize(token); 32 | const nonTickContractID = '8686424368'; 33 | const { candles } = await liveApi.getDataForContract( 34 | () => liveApi.getContractInfo(nonTickContractID).then(r => r.proposal_open_contract), 35 | undefined, 36 | 'candles' 37 | ); 38 | expect(candles.length).toBe(6); 39 | expect(candles[0].open).toBeTruthy(); 40 | expect(candles[0].close).toBeTruthy(); 41 | expect(candles[0].epoch).toBeTruthy(); 42 | expect(candles[0].high).toBeTruthy(); 43 | expect(candles[0].low).toBeTruthy(); 44 | }); 45 | 46 | it('should return even if contract does not have end time', async () => { 47 | await liveApi.authorize(token); 48 | const nonTickContractID = '8686424368'; 49 | const { candles } = await liveApi.getDataForContract( 50 | () => 51 | liveApi.getContractInfo(nonTickContractID).then(r => { 52 | const cloned = Object.assign({}, r.proposal_open_contract); 53 | delete cloned.exit_tick_time; 54 | delete cloned.date_expiry; 55 | return cloned; 56 | }), 57 | undefined, 58 | 'candles' 59 | ); 60 | expect(candles.length).toBeLessThan(700); 61 | expect(candles[0].open).toBeTruthy(); 62 | expect(candles[0].close).toBeTruthy(); 63 | expect(candles[0].epoch).toBeTruthy(); 64 | expect(candles[0].high).toBeTruthy(); 65 | expect(candles[0].low).toBeTruthy(); 66 | }); 67 | 68 | it('should return isSold == true if contract sold', async () => { 69 | await liveApi.authorize(token); 70 | const tickContractID = '8818581808'; 71 | const { isSold } = await liveApi.getDataForContract(() => 72 | liveApi.getContractInfo(tickContractID).then(r => r.proposal_open_contract) 73 | ); 74 | expect(isSold).toBeTruthy(); 75 | }); 76 | }); 77 | 78 | describe('getDataForSymbol', () => { 79 | it('should get data for specified market', async () => { 80 | await liveApi.authorize(token); 81 | const { ticks } = await liveApi.getDataForSymbol('R_100'); 82 | expect(ticks.length).toBeLessThan(700); 83 | }); 84 | 85 | it('should get data for specified market using given duration params', async () => { 86 | await liveApi.authorize(token); 87 | const { ticks } = await liveApi.getDataForSymbol('R_100'); 88 | expect(ticks.length).toBeGreaterThan(29); 89 | }); 90 | 91 | it('should get candles for specified market if requested candles', async () => { 92 | await liveApi.authorize(token); 93 | const { candles } = await liveApi.getDataForSymbol('R_100', 60 * 60, 'candles'); 94 | expect(candles.length).toBeGreaterThan(59); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /src/__tests__/oauth-test.js: -------------------------------------------------------------------------------- 1 | import { oauthUrl, oauthUrlWithLanguage, parseOAuthResponse } from '../OAuth'; 2 | 3 | describe('OAuth', () => { 4 | const appId = 'id-ud5PPOTeBcEnkam7ArXIc4AO9e9gw'; 5 | 6 | it('should be able to get the OAuth url', () => { 7 | const url = oauthUrl(appId); 8 | expect(url).toContain('binary.com'); 9 | }); 10 | 11 | it('should be able to get the OAuth url with language', () => { 12 | const url = oauthUrlWithLanguage(appId, 'RU'); 13 | expect(url).toContain('binary.com'); 14 | expect(url).toContain('RU'); 15 | }); 16 | 17 | it('should be able to parse the simplest response url', () => { 18 | const response = '/redirect?acct1=vr123&token1=a1-456'; 19 | const parsed = parseOAuthResponse(response); 20 | const expected = [{ account: 'vr123', token: 'a1-456' }]; 21 | expect(expected).toEqual(parsed); 22 | }); 23 | 24 | it('should throw an exception if the url is not valid', () => { 25 | expect(() => parseOAuthResponse('not valid')).toThrow(); 26 | }); 27 | 28 | it('should parse multipe account url', () => { 29 | const response = '/redirect?acct1=vr123&token1=a1-456&acct2=cc123&token2=a1-bob&acct3=ml123&token3=a1-hello'; 30 | const parsed = parseOAuthResponse(response); 31 | const expected = [ 32 | { account: 'vr123', token: 'a1-456' }, 33 | { account: 'cc123', token: 'a1-bob' }, 34 | { account: 'ml123', token: 'a1-hello' }, 35 | ]; 36 | expect(expected).toEqual(parsed); 37 | }); 38 | 39 | it('should parse actual url response', () => { 40 | const response = 41 | 'https://test.example.com/redirect?' + 42 | 'acct1=CR300810&token1=a1-isZfteMh8GOxnpPUIi1rlUqepWXKW&' + 43 | 'acct2=VRTC547953&token2=a1-LVaOlP2v56wDwE7Fv8VftJNDdIt2G'; 44 | const parsed = parseOAuthResponse(response); 45 | const expected = [ 46 | { account: 'CR300810', token: 'a1-isZfteMh8GOxnpPUIi1rlUqepWXKW' }, 47 | { 48 | account: 'VRTC547953', 49 | token : 'a1-LVaOlP2v56wDwE7Fv8VftJNDdIt2G', 50 | }, 51 | ]; 52 | expect(expected).toEqual(parsed); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/__tests__/read-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | const token = 'if9yO8qFuejRCDk'; 5 | 6 | describe('read', () => { 7 | let liveApi; 8 | 9 | beforeAll(() => { 10 | liveApi = new LiveApi({ websocket, appId: 1089 }); 11 | }); 12 | 13 | it('should be able to get account limit', async () => { 14 | await liveApi.authorize(token); 15 | const response = await liveApi.getAccountLimits(); 16 | expect(response.get_limits).toBeTruthy(); 17 | }); 18 | 19 | it('should be able return account limits in a server response', async () => { 20 | await liveApi.authorize(token); 21 | const response = await liveApi.getAccountLimits(); 22 | expect(response.get_limits.account_balance).toEqual('100000.00'); 23 | }); 24 | 25 | it('should be able to call the function getAccountSettings', () => 26 | expect(() => liveApi.getAccountSettings()).not.toThrow()); 27 | 28 | it('should be able to return account settings from a api response', async () => { 29 | await liveApi.authorize(token); 30 | const response = await liveApi.getAccountSettings(); 31 | expect(response.get_settings.country).toEqual('Indonesia'); 32 | }); 33 | 34 | it('should be able to call getAccountStatus without an error', () => { 35 | expect(() => liveApi.getAccountStatus()).not.toThrow(); 36 | }); 37 | 38 | it('should be able to get account status in a response from a server', async () => { 39 | await liveApi.authorize(token); 40 | const response = await liveApi.getAccountStatus(); 41 | expect(response.get_account_status.status).toBeTruthy(); 42 | }); 43 | 44 | it('should be able to call getSelfExclusion without an issue', () => { 45 | expect(() => liveApi.getSelfExclusion()).not.toThrow(); 46 | }); 47 | 48 | it('should be able to getSelfExclusion in a response from a server', async () => { 49 | await liveApi.authorize(token); 50 | const response = await liveApi.getSelfExclusion(); 51 | expect(response.get_self_exclusion.max_balance).toEqual('100000'); 52 | }); 53 | 54 | it('should be able to call logout function', () => { 55 | expect(() => liveApi.logOut()).not.toThrow(); 56 | }); 57 | 58 | it('should be able to sign user out of his account', async () => { 59 | await liveApi.authorize(token); 60 | const response = await liveApi.logOut(); 61 | expect(response.error).toBeFalsy(); 62 | }); 63 | 64 | it('it should be able to call getStatement function without an issue', () => 65 | expect(() => 66 | liveApi.getStatement({ 67 | statement : 1, 68 | description: 1, 69 | limit : 100, 70 | }) 71 | ).not.toThrow()); 72 | 73 | it('it should be able to get portfolio', () => expect(() => liveApi.getPortfolio()).not.toThrow()); 74 | 75 | it('should be able to get a statement if logged in', async () => { 76 | await liveApi.authorize(token); 77 | const response = await liveApi.getStatement(); 78 | expect(response.statement).toBeTruthy(); 79 | }); 80 | 81 | it('should be able to call getProfitTable without an error', () => { 82 | expect(() => liveApi.getProfitTable()).not.toThrow(); 83 | }); 84 | 85 | it('can get profitTable from the server', async () => { 86 | await liveApi.authorize(token); 87 | const response = await liveApi.getProfitTable(); 88 | expect(response.profit_table).toBeTruthy(); 89 | }); 90 | 91 | it('should be able to call getRealityCheckSummary without an error', () => { 92 | expect(() => liveApi.getRealityCheckSummary()).not.toThrow(); 93 | }); 94 | 95 | it('should be able to subscribe to balance updates', () => { 96 | expect(() => liveApi.subscribeToBalance()).not.toThrow(); 97 | }); 98 | 99 | it('should be able to unsubscribe from balance updates', () => { 100 | expect(() => liveApi.unsubscribeFromBalance()).not.toThrow(); 101 | }); 102 | 103 | it('should be able to subscribe to open contract updates', () => { 104 | expect(() => liveApi.subscribeToAllOpenContracts()).not.toThrow(); 105 | }); 106 | 107 | it('should be able to unsubscribe from open contract updates', () => { 108 | expect(() => liveApi.unsubscribeFromAllOpenContracts()).not.toThrow(); 109 | }); 110 | 111 | it('should be able to subscribe to transaction updates', () => { 112 | expect(() => liveApi.subscribeToTransactions()).not.toThrow(); 113 | }); 114 | 115 | it('should be able to unsubscribe from transaction updates', () => { 116 | expect(() => liveApi.unsubscribeFromTransactions()).not.toThrow(); 117 | }); 118 | 119 | it('should be able to call subscribeToOpenContract without an issue', () => { 120 | expect(() => liveApi.subscribeToOpenContract()).not.toThrow(); 121 | }); 122 | 123 | it('should subscribeToOpenContract and return a server response', async () => { 124 | await liveApi.authorize(token); 125 | const response = await liveApi.subscribeToOpenContract(); 126 | expect(response.echo_req.subscribe).toBeTruthy(); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /src/__tests__/resubscribe-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | function sleep(ms = 0) { 5 | return new Promise(r => setTimeout(r, ms)); 6 | } 7 | 8 | describe('resubscribe', () => { 9 | it('should reconnect when disconnected', async () => { 10 | const api = new LiveApi({ websocket, appId: 1089 }); 11 | 12 | await api.ping(); 13 | 14 | try { 15 | api.socket.close(); 16 | } catch (e) { 17 | // ignore error 18 | } 19 | 20 | await sleep(2000); 21 | 22 | const response = await api.ping(); 23 | expect(response.ping).toBeTruthy(); 24 | }); 25 | 26 | it.skip('should resubscribe all subscription after reconnect', async () => { 27 | const spy = jest.fn(); 28 | const api = new LiveApi({ websocket, appId: 1089 }); 29 | 30 | await api.ping(); 31 | 32 | api.events.on('tick', spy); 33 | 34 | const ticks = ['R_100']; 35 | api.subscribeToTicks(ticks); 36 | 37 | try { 38 | api.socket.close(); 39 | } catch (e) { 40 | // ignore error 41 | } 42 | 43 | await sleep(2000); 44 | 45 | expect(api.apiState.getState().ticks.has('R_100')).toEqual(true); 46 | expect(spy).toHaveBeenCalled(); 47 | }); 48 | 49 | // check if empty state, and no resubsription when new 50 | // check for specific resubsriptions 51 | 52 | it('should reject promise with DisconnectError when socket disconnected before response received', async () => { 53 | const api = new LiveApi({ websocket, appId: 1089 }); 54 | 55 | await sleep(2000); 56 | 57 | const promise = api.ping(); 58 | 59 | try { 60 | api.socket.close(); 61 | } catch (e) { 62 | // ignore error 63 | } 64 | 65 | return promise.catch(err => expect(err.name).toEqual('DisconnectError')); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /src/__tests__/stateful-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('stateful', () => { 5 | let liveApi; 6 | 7 | beforeAll(async () => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | await liveApi.ping(); 10 | }); 11 | 12 | beforeEach(() => { 13 | liveApi.apiState.resetState(); 14 | }); 15 | 16 | it('initial state is empty', () => { 17 | const state = liveApi.apiState.getState(); 18 | 19 | expect(state.token).toBeFalsy(); 20 | expect(state.balance).toBeFalsy(); 21 | expect(state.allContract).toBeFalsy(); 22 | expect(state.transactions).toBe(false); 23 | expect(state.ticks.size).toBeFalsy(); 24 | expect(state.proposals.size).toBeFalsy(); 25 | }); 26 | 27 | it('after authorization token is retained', () => { 28 | liveApi.authorize('some token'); 29 | const stateAfter = liveApi.apiState.getState(); 30 | expect(stateAfter.token).toEqual('some token'); 31 | }); 32 | 33 | it('subscribing to balance updates is remembered', () => { 34 | liveApi.subscribeToBalance(); 35 | const stateAfter = liveApi.apiState.getState(); 36 | expect(stateAfter.balance).toBeTruthy(); 37 | }); 38 | 39 | it('subscribing to balance updates is remembered', () => { 40 | liveApi.subscribeToAllOpenContracts(); 41 | const stateAfter = liveApi.apiState.getState(); 42 | expect(stateAfter.allContract).toBeTruthy(); 43 | }); 44 | 45 | it('subscribing to transactions updates is remembered', () => { 46 | liveApi.subscribeToTransactions(); 47 | const stateAfter = liveApi.apiState.getState(); 48 | expect(stateAfter.transactions).toBeTruthy(); 49 | }); 50 | 51 | it('subscribing to a single tick updates is remembered', () => { 52 | liveApi.subscribeToTick('R_50'); 53 | const stateAfter = liveApi.apiState.getState(); 54 | expect(stateAfter.ticks.size).toEqual(1); 55 | }); 56 | 57 | // unsubscribeFromTick is not really working, we should consider remove it 58 | it.skip('unsubsribing from a tick is remembered', () => { 59 | liveApi.subscribeToTick('R_50'); 60 | liveApi.unsubscribeFromTick('R_50'); 61 | const stateAfter = liveApi.apiState.getState(); 62 | expect(stateAfter.ticks.size).toEqual(0); 63 | }); 64 | 65 | it('subscribing to multiple tick updates is remembered', () => { 66 | liveApi.subscribeToTicks(['R_25', 'R_50', 'R_100']); 67 | const stateAfter = liveApi.apiState.getState(); 68 | expect(stateAfter.ticks.has('R_25')).toBeTruthy(); 69 | expect(stateAfter.ticks.has('R_50')).toBeTruthy(); 70 | expect(stateAfter.ticks.has('R_100')).toBeTruthy(); 71 | }); 72 | 73 | // unsubscribeFromTicks is not really working, we should consider remove it 74 | it.skip('unsubscribing from multiple tick updates is remembered', () => { 75 | liveApi.subscribeToTicks(['R_25', 'R_50', 'R_100']); 76 | liveApi.unsubscribeFromTicks(['R_50', 'R_100']); 77 | const stateAfter = liveApi.apiState.getState(); 78 | expect(stateAfter.ticks.has('R_25')).toBeTruthy(); 79 | expect(stateAfter.ticks.has('R_50')).toBeFalsy(); 80 | expect(stateAfter.ticks.has('R_100')).toBeFalsy(); 81 | }); 82 | 83 | it('subscribe ticks thru tickhistory should be remembered', () => { 84 | liveApi.getTickHistory('R_100', { subscribe: 1 }); 85 | const stateAfter = liveApi.apiState.getState(); 86 | 87 | expect(stateAfter.ticksHistory.has('R_100')).toBeTruthy(); 88 | expect(stateAfter.candlesHistory.has('R_100')).toBeFalsy(); 89 | }); 90 | 91 | it('subscribe candles thru tickhistory should be remembered', () => { 92 | liveApi.getTickHistory('R_100', { subscribe: 1, style: 'candles' }); 93 | const stateAfter = liveApi.apiState.getState(); 94 | 95 | expect(stateAfter.ticksHistory.has('R_100')).toBeFalsy(); 96 | expect(stateAfter.candlesHistory.has('R_100')).toBeTruthy(); 97 | }); 98 | 99 | // skipped as it only works for live contract, and there aint so many forever live contract for testing 100 | it.skip('subscribe to single contract is remembered only if contract id is valid', async () => { 101 | await liveApi.authorize('qdJ86Avvrsh0Le4'); 102 | await liveApi.subscribeToOpenContract('9939813188'); 103 | const stateAfter = liveApi.apiState.getState(); 104 | 105 | expect(stateAfter.contracts.size).toEqual(1); 106 | }); 107 | 108 | it('unsubscribeById should remove corresponding id', async () => { 109 | await liveApi 110 | .subscribeToPriceForContractProposal({ 111 | amount : 100, 112 | basis : 'payout', 113 | contract_type: 'CALL', 114 | currency : 'USD', 115 | duration : 60, 116 | duration_unit: 's', 117 | symbol : 'R_100', 118 | }) 119 | .then(r => { 120 | const id = r.proposal.id; 121 | 122 | const stateBefore = liveApi.apiState.getState(); 123 | expect(stateBefore.proposals.size).toEqual(1); 124 | liveApi.unsubscribeByID(id); 125 | 126 | const stateAfter = liveApi.apiState.getState(); 127 | expect(stateAfter.proposals.size).toEqual(0); 128 | }); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /src/__tests__/trade-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('trade', () => { 5 | let liveApi; 6 | 7 | beforeEach(() => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | }); 10 | 11 | it('can buy contract', () => { 12 | expect(() => liveApi.buyContract('someid', 100)).not.toThrow(); 13 | }); 14 | 15 | it('can buy contract with parameters', () => { 16 | const parameters = { 17 | amount : 100, 18 | basis : 'payout', // or 'stake' 19 | contract_type: 'PUT', // or 'CALL' 20 | currency : 'USD', 21 | duration : 5, 22 | duration_unit: 't', 23 | symbol : 'frxEURUSD', 24 | }; 25 | expect(() => liveApi.buyContract(parameters, 100)).not.toThrow(); 26 | }); 27 | 28 | it('can sell contract', () => { 29 | expect(() => liveApi.sellContract('someid', 100)).not.toThrow(); 30 | }); 31 | 32 | it('can sell expired contracts', () => { 33 | expect(() => liveApi.sellExpiredContracts()).not.toThrow(); 34 | }); 35 | 36 | it('can topup virtual account', () => { 37 | expect(() => liveApi.topUpVirtualAccount()).not.toThrow(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/__tests__/unauthenticated-test.js: -------------------------------------------------------------------------------- 1 | import websocket from 'ws'; 2 | import LiveApi from '../LiveApi'; 3 | 4 | describe('unauthenticated', () => { 5 | let liveApi; 6 | 7 | beforeAll(() => { 8 | liveApi = new LiveApi({ websocket, appId: 1089 }); 9 | }); 10 | 11 | it('can ping server', async () => expect((await liveApi.ping()).ping).toBeTruthy()); 12 | 13 | it('can call authorize', () => expect(liveApi.authorize).toBeTruthy()); 14 | 15 | it('can get available contracts for symbol', async () => 16 | expect((await liveApi.getContractsForSymbol('R_100')).contracts_for).toBeTruthy()); 17 | 18 | it('can get brief active symbols', async () => 19 | expect((await liveApi.getActiveSymbolsBrief()).active_symbols).toBeTruthy()); 20 | 21 | it('can get full active symbols', async () => 22 | expect((await liveApi.getActiveSymbolsFull()).active_symbols).toBeTruthy()); 23 | 24 | it('can get asset index', async () => expect((await liveApi.getAssetIndex()).asset_index).toBeTruthy()); 25 | 26 | it('must provide a parameter to getTradingTimes', async () => 27 | expect((await liveApi.getTradingTimes(new Date())).trading_times).toBeTruthy()); 28 | 29 | it('can get trading times', async () => 30 | expect((await liveApi.getTradingTimes(new Date())).trading_times).toBeTruthy()); 31 | 32 | it('can get residences', async () => expect((await liveApi.getResidences()).residence_list).toBeTruthy()); 33 | 34 | it('can get states for a country', async () => 35 | expect((await liveApi.getStatesForCountry('de')).states_list).toBeTruthy()); 36 | 37 | it('can subscribe to tick updates', async () => expect((await liveApi.subscribeToTick('R_100')).tick).toBeTruthy()); 38 | 39 | it('can subscribe to multiple ticks updates', async () => 40 | expect(() => liveApi.subscribeToTicks(['R_25', 'R_50', 'R_100'])).not.toThrow()); 41 | 42 | it('can unsubscribe from all tick updates', async () => 43 | expect(() => liveApi.unsubscribeFromAllTicks()).not.toThrow()); 44 | 45 | it('can get tick history with no parameters', async () => 46 | expect(() => liveApi.getTickHistory('R_100').history).not.toThrow()); 47 | 48 | it('can get tick history with custom intervals', async () => 49 | expect( 50 | async () => 51 | (await liveApi.getTickHistory('R_100', { 52 | end : 'latest', 53 | count: 10, 54 | })).history 55 | ).not.toThrow()); 56 | 57 | it('can get the landing company for a country', async () => 58 | expect((await liveApi.getLandingCompany('de')).landing_company).toBeTruthy()); 59 | 60 | it('can get details about a landing company', async () => 61 | expect((await liveApi.getLandingCompanyDetails('costarica')).landing_company_details).toBeTruthy()); 62 | 63 | it('can get payment agents for a country', async () => 64 | expect((await liveApi.getPaymentAgentsForCountry('id')).paymentagent_list).toBeTruthy()); 65 | 66 | it('can get payout currencies', async () => 67 | expect((await liveApi.getPayoutCurrencies()).payout_currencies).toBeTruthy()); 68 | 69 | it('can get price proposal for contract', async () => 70 | expect( 71 | (await liveApi.getPriceProposalForContract({ 72 | amount : 100, 73 | basis : 'payout', 74 | contract_type: 'CALL', 75 | currency : 'USD', 76 | duration : 60, 77 | duration_unit: 's', 78 | symbol : 'R_100', 79 | })).proposal 80 | ).toBeTruthy()); 81 | 82 | it('can subscribe to price proposal updates for contract', async () => 83 | expect( 84 | (await liveApi.subscribeToPriceForContractProposal({ 85 | amount : 100, 86 | basis : 'payout', 87 | contract_type: 'CALL', 88 | currency : 'USD', 89 | duration : 60, 90 | duration_unit: 's', 91 | symbol : 'R_100', 92 | })).proposal 93 | ).toBeTruthy()); 94 | 95 | it('can unsubscribe from all price proposal updates', async () => 96 | expect(() => liveApi.unsubscribeFromAllProposals()).not.toThrow()); 97 | 98 | it('can get candles for a symbol', async () => expect((await liveApi.getCandles('R_50')).candles).toBeTruthy()); 99 | 100 | it('can get candles for last N days', async () => 101 | expect((await liveApi.getCandlesForLastNDays('R_50', 40)).candles).toBeTruthy()); 102 | 103 | it('can get server time', async () => expect((await liveApi.getServerTime()).time).toBeTruthy()); 104 | 105 | it('can verify email', async () => 106 | expect((await liveApi.verifyEmail('address@example.com', 'account_opening')).verify_email).toBeTruthy()); 107 | 108 | it('can get website status', async () => expect((await liveApi.getWebsiteStatus()).website_status).toBeTruthy()); 109 | }); 110 | -------------------------------------------------------------------------------- /src/__tests__/useRx-test.js: -------------------------------------------------------------------------------- 1 | // import { Observable } from 'rx-lite'; 2 | import 'babel-polyfill'; 3 | import websocket from 'ws'; 4 | import LiveApi from '../LiveApi'; 5 | 6 | describe('use rx', () => { 7 | const apiWithRX = new LiveApi({ websocket, useRx: true, appId: 1089 }); 8 | 9 | it('should return observable for any call', callback => { 10 | const obs = apiWithRX.ping(); 11 | 12 | obs.subscribe( 13 | next => { 14 | expect(next.msg_type).toEqual('ping'); 15 | }, 16 | err => console.log(err), // eslint-disable-line no-console 17 | callback 18 | ); 19 | obs.connect(); 20 | }); 21 | 22 | // simple example 23 | it('should make stream handling easier', callback => { 24 | const stream = apiWithRX.subscribeToTick('R_100'); 25 | 26 | const avgPerTick = stream.scan((avg, json, idx) => { 27 | const currentVal = +json.tick.quote; 28 | const newAvg = (avg * idx + currentVal) / (idx + 1); 29 | return newAvg; 30 | }, 0); 31 | 32 | avgPerTick.take(2).subscribe( 33 | avg => { 34 | expect(typeof avg).toBe('number'); 35 | }, 36 | err => console.log(err), // eslint-disable-line no-console 37 | callback 38 | ); 39 | stream.connect(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/calls/admin.js: -------------------------------------------------------------------------------- 1 | export const deleteApiToken = (token: string) => ({ 2 | api_token : 1, 3 | delete_token: token, 4 | }); 5 | 6 | export const getApiTokens = () => ({ 7 | api_token: 1, 8 | }); 9 | 10 | export const createApiToken = (token: string, scopes: string[]) => ({ 11 | api_token : 1, 12 | new_token : token, 13 | new_token_scopes: scopes, 14 | }); 15 | 16 | export const changePassword = (oldPassword: string, newPassword: string) => ({ 17 | change_password: 1, 18 | old_password : oldPassword, 19 | new_password : newPassword, 20 | }); 21 | 22 | export const registerApplication = (options: Object) => ({ 23 | app_register: 1, 24 | ...options, 25 | }); 26 | 27 | export const getAllAppList = () => ({ 28 | app_list: 1, 29 | }); 30 | 31 | export const getAppslistById = (appid: number) => ({ 32 | app_get: appid, 33 | }); 34 | 35 | export const deleteApplication = (appid: number) => ({ 36 | app_delete: appid, 37 | }); 38 | 39 | export const createRealAccountMaltaInvest = (options: Object) => ({ 40 | new_account_maltainvest: 1, 41 | ...options, 42 | }); 43 | 44 | export const createRealAccount = (options: Object) => ({ 45 | new_account_real: 1, 46 | ...options, 47 | }); 48 | 49 | export const setAccountCurrency = (currency: string) => ({ 50 | set_account_currency: currency, 51 | }); 52 | 53 | export const setSelfExclusion = (options: Object) => ({ 54 | set_self_exclusion: 1, 55 | ...options, 56 | }); 57 | 58 | export const setAccountSettings = (options: Object) => ({ 59 | set_settings: 1, 60 | ...options, 61 | }); 62 | 63 | export const setTnCApproval = () => ({ 64 | tnc_approval: 1, 65 | }); 66 | -------------------------------------------------------------------------------- /src/calls/index.js: -------------------------------------------------------------------------------- 1 | export * from './unauthenticated'; 2 | export * from './read'; 3 | export * from './trade'; 4 | export * from './payments'; 5 | export * from './admin'; 6 | -------------------------------------------------------------------------------- /src/calls/payments.js: -------------------------------------------------------------------------------- 1 | export const getCashierLockStatus = () => ({ 2 | cashier_password: 1, 3 | }); 4 | 5 | export const setCashierLock = (options: Object) => ({ 6 | cashier_password: 1, 7 | ...options, 8 | }); 9 | 10 | export const withdrawToPaymentAgent = (options: Object) => ({ 11 | paymentagent_withdraw: 1, 12 | ...options, 13 | }); 14 | 15 | export const paymentAgentTransfer = (options: Object) => ({ 16 | paymentagent_transfer: 1, 17 | ...options, 18 | }); 19 | 20 | export const transferBetweenAccounts = (options: Object) => ({ 21 | transfer_between_accounts: 1, 22 | ...options, 23 | }); 24 | -------------------------------------------------------------------------------- /src/calls/read.js: -------------------------------------------------------------------------------- 1 | export const getAccountLimits = () => ({ 2 | get_limits: 1, 3 | }); 4 | 5 | export const getAccountSettings = () => ({ 6 | get_settings: 1, 7 | }); 8 | 9 | export const getAccountStatus = () => ({ 10 | get_account_status: 1, 11 | }); 12 | 13 | export const getSelfExclusion = () => ({ 14 | get_self_exclusion: 1, 15 | }); 16 | 17 | export const logOut = () => ({ 18 | logout: 1, 19 | }); 20 | 21 | export const getStatement = (options: Object) => ({ 22 | statement: 1, 23 | ...options, 24 | }); 25 | 26 | export const getPortfolio = () => ({ 27 | portfolio: 1, 28 | }); 29 | 30 | export const getProfitTable = (options: Object) => ({ 31 | profit_table: 1, 32 | ...options, 33 | }); 34 | 35 | export const getRealityCheckSummary = () => ({ 36 | reality_check: 1, 37 | }); 38 | 39 | export const subscribeToBalance = () => ({ 40 | balance : 1, 41 | subscribe: 1, 42 | }); 43 | 44 | export const unsubscribeFromBalance = () => ({ 45 | forget_all: 'balance', 46 | }); 47 | 48 | export const subscribeToOpenContract = (contractId: number) => ({ 49 | proposal_open_contract: 1, 50 | subscribe : 1, 51 | contract_id : contractId, 52 | }); 53 | 54 | export const getContractInfo = (contractId: number) => ({ 55 | proposal_open_contract: 1, 56 | contract_id : contractId, 57 | }); 58 | 59 | export const subscribeToAllOpenContracts = () => ({ 60 | proposal_open_contract: 1, 61 | subscribe : 1, 62 | }); 63 | 64 | export const unsubscribeFromAllOpenContracts = () => ({ 65 | forget_all: 'proposal_open_contract', 66 | }); 67 | 68 | export const subscribeToTransactions = () => ({ 69 | transaction: 1, 70 | subscribe : 1, 71 | }); 72 | 73 | export const unsubscribeFromTransactions = () => ({ 74 | forget_all: 'transaction', 75 | }); 76 | -------------------------------------------------------------------------------- /src/calls/trade.js: -------------------------------------------------------------------------------- 1 | export const buyContract = (contractId: number, price: number) => ({ 2 | buy: contractId, 3 | price, 4 | }); 5 | 6 | export const buyContractParams = (params: Object, price: number) => ({ 7 | buy : 1, 8 | price, 9 | parameters: params, 10 | }); 11 | 12 | export const sellContract = (contractId: number, price: number) => ({ 13 | sell: contractId, 14 | price, 15 | }); 16 | 17 | export const sellExpiredContracts = () => ({ 18 | sell_expired: 1, 19 | }); 20 | 21 | export const topUpVirtualAccount = () => ({ 22 | topup_virtual: 1, 23 | }); 24 | -------------------------------------------------------------------------------- /src/calls/unauthenticated.js: -------------------------------------------------------------------------------- 1 | export const getActiveSymbolsBrief = () => ({ 2 | active_symbols: 'brief', 3 | }); 4 | 5 | export const getActiveSymbolsFull = () => ({ 6 | active_symbols: 'full', 7 | }); 8 | 9 | export const getAssetIndex = () => ({ 10 | asset_index: 1, 11 | }); 12 | 13 | export const authorize = (token: string) => ({ 14 | authorize: token, 15 | }); 16 | 17 | export const getContractsForSymbol = (symbol: string) => ({ 18 | contracts_for: symbol, 19 | }); 20 | 21 | export const unsubscribeFromTick = (symbol: string) => ({ 22 | forget: symbol, 23 | }); 24 | 25 | export const unsubscribeFromTicks = (symbols: string[]) => ({ 26 | forget: symbols, 27 | }); 28 | 29 | export const unsubscribeByID = (id: number) => ({ 30 | forget: id, 31 | }); 32 | 33 | export const unsubscribeFromAllTicks = () => ({ 34 | forget_all: 'ticks', 35 | }); 36 | 37 | export const unsubscribeFromAllCandles = () => ({ 38 | forget_all: 'candles', 39 | }); 40 | 41 | export const unsubscribeFromAllProposals = () => ({ 42 | forget_all: 'proposal', 43 | }); 44 | 45 | export const unsubscribeFromAllPortfolios = () => ({ 46 | forget_all: 'portfolio', 47 | }); 48 | 49 | export const getLandingCompany = (landingCompany: string) => ({ 50 | landing_company: landingCompany, 51 | }); 52 | 53 | export const getLandingCompanyDetails = (landingCompany: string) => ({ 54 | landing_company_details: landingCompany, 55 | }); 56 | 57 | export const createVirtualAccount = (options: Object) => ({ 58 | new_account_virtual: 1, 59 | ...options, 60 | }); 61 | 62 | export const ping = () => ({ 63 | ping: 1, 64 | }); 65 | 66 | export const getPaymentAgentsForCountry = (countryCode: string) => ({ 67 | paymentagent_list: countryCode, 68 | }); 69 | 70 | export const getPayoutCurrencies = () => ({ 71 | payout_currencies: 1, 72 | }); 73 | 74 | export const getPriceProposalForContract = (options: Object) => ({ 75 | proposal: 1, 76 | ...options, 77 | }); 78 | 79 | export const subscribeToPriceForContractProposal = (options: Object) => ({ 80 | proposal : 1, 81 | subscribe: 1, 82 | ...options, 83 | }); 84 | 85 | export const getResidences = () => ({ 86 | residence_list: 1, 87 | }); 88 | 89 | export const getStatesForCountry = (countryCode: string) => ({ 90 | states_list: countryCode, 91 | }); 92 | 93 | export const subscribeToTick = (symbol: string) => ({ 94 | ticks: symbol, 95 | }); 96 | 97 | export const subscribeToTicks = (symbols: string[]) => ({ 98 | ticks: symbols, 99 | }); 100 | 101 | export const getTickHistory = (symbol: string, options: Object) => ({ 102 | ticks_history: symbol, 103 | ...(options || { end: 'latest' }), 104 | }); 105 | 106 | export const getCandles = (symbol: string, options: Object) => ({ 107 | ticks_history: symbol, 108 | style : 'candles', 109 | ...(options || { end: 'latest' }), 110 | }); 111 | 112 | export const getCandlesForLastNDays = (symbol: string, ndays: number) => ({ 113 | ticks_history: symbol, 114 | style : 'candles', 115 | start : Math.floor(Date.now() / 1000) - (ndays - 1) * 60 * 60 * 24, 116 | end : 'latest', 117 | granularity : 60 * 60 * 24, 118 | count : 30, 119 | }); 120 | 121 | export const getServerTime = () => ({ 122 | time: 1, 123 | }); 124 | 125 | export const getTradingTimes = (date: Date) => ({ 126 | trading_times: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`, 127 | }); 128 | 129 | export const verifyEmail = (email: string, type: string) => ({ 130 | verify_email: email, 131 | type, 132 | }); 133 | 134 | export const getWebsiteStatus = () => ({ 135 | website_status: 1, 136 | }); 137 | -------------------------------------------------------------------------------- /src/custom.js: -------------------------------------------------------------------------------- 1 | import { nowAsEpoch, computeStartEndForContract } from 'binary-utils'; 2 | 3 | const responseSizeLimit = 700; 4 | 5 | const granularities: number[] = [60, 120, 180, 300, 600, 900, 1800, 3600, 7200, 14400, 28800, 86400]; 6 | 7 | const ohlcDataToTicks = (candles: Candle[]): Tick[] => candles.map(data => ({ quote: +data.open, epoch: +data.epoch })); 8 | 9 | export const autoAdjustGetData = ( 10 | api: LiveApi, 11 | symbol: string, 12 | start: Epoch, 13 | end: Epoch, 14 | style: string = 'ticks', 15 | subscribe: boolean, 16 | extra = {} 17 | ) => { 18 | const secs = end - start; 19 | const ticksCount = secs / 2; 20 | if (ticksCount >= responseSizeLimit || style === 'candles') { 21 | const idealGranularity = secs / responseSizeLimit; 22 | let finalGranularity = 60; 23 | granularities.forEach((g, i) => { 24 | if (idealGranularity > g && idealGranularity <= granularities[i + 1]) { 25 | finalGranularity = granularities[i + 1]; 26 | } 27 | }); 28 | finalGranularity = Math.min(86400, finalGranularity); 29 | 30 | return api 31 | .getTickHistory(symbol, { 32 | start, 33 | end, 34 | adjust_start_time: 1, 35 | count : responseSizeLimit, 36 | style : 'candles', 37 | granularity : finalGranularity, 38 | subscribe : subscribe ? 1 : undefined, 39 | }) 40 | .then(r => { 41 | if (style === 'ticks') { 42 | return { 43 | ...extra, 44 | ticks: ohlcDataToTicks(r.candles), 45 | symbol, 46 | }; 47 | } 48 | return { 49 | ...extra, 50 | candles: r.candles, 51 | symbol, 52 | }; 53 | }); 54 | } 55 | return api 56 | .getTickHistory(symbol, { 57 | start, 58 | end, 59 | adjust_start_time: 1, 60 | count : responseSizeLimit, 61 | style : 'ticks', 62 | subscribe : subscribe ? 1 : undefined, 63 | }) 64 | .then(r => { 65 | const ticks = r.history.times.map((t, idx) => { 66 | const quote = r.history.prices[idx]; 67 | return { epoch: +t, quote: +quote }; 68 | }); 69 | return { 70 | ...extra, 71 | ticks, 72 | symbol, 73 | }; 74 | }); 75 | }; 76 | 77 | export function getDataForSymbol( 78 | api: LiveApi, 79 | symbol: string, 80 | duration: Epoch = 600, 81 | style: string = 'ticks', 82 | subscribe: boolean 83 | ) { 84 | const end = nowAsEpoch(); 85 | const start = end - duration; 86 | return autoAdjustGetData(api, symbol, start, end, style, subscribe); 87 | } 88 | 89 | /** 90 | * get data of contract 91 | * @param api - will be injected by library 92 | * @param getContract - function that accept nothing and return a Promise containing contract 93 | * @param durationCount - number of duration 94 | * @param durationType - type of duration, check http://api.highcharts.com/highstock#rangeSelector.buttons 95 | * @param style - one of ['ticks', 'candles'], this will affect the return data shape, 96 | * internally library might not always use this param when requesting, eg. when data is too large, 97 | * library will use `candles` instead of `ticks`, this is handle by library so user do not need to worry 98 | * @param granularity - default to 60, check https://developers.binary.com/api/#ticks_history 99 | * @returns {*|Promise.} 100 | */ 101 | export function getDataForContract( 102 | api: LiveApi, 103 | getContract, 104 | duration?: Epoch, 105 | style: string = 'ticks', 106 | subscribe: boolean 107 | ) { 108 | const getAllData = () => 109 | getContract().then(contract => { 110 | const symbol = contract.underlying; 111 | const { start, end } = computeStartEndForContract(contract); 112 | return autoAdjustGetData(api, symbol, start, end, style, subscribe, { isSold: !!contract.sell_time }); 113 | }); 114 | 115 | if (!duration) { 116 | return getAllData(); 117 | } 118 | 119 | return getContract().then(contract => { 120 | const symbol = contract.underlying; 121 | const startTime = +contract.date_start; 122 | 123 | // handle Contract not started yet 124 | if (startTime > nowAsEpoch()) { 125 | return autoAdjustGetData(api, symbol, nowAsEpoch() - 600, nowAsEpoch(), style, subscribe, { 126 | isSold: !!contract.sell_time, 127 | }); 128 | } 129 | 130 | const sellT = contract.sell_time; 131 | const end = sellT || nowAsEpoch(); 132 | 133 | const buffer = (end - startTime) * 0.05; 134 | 135 | const start = Math.min(startTime - buffer, end - duration); 136 | return autoAdjustGetData(api, symbol, Math.round(start), Math.round(end), style, subscribe, { 137 | isSold: !!contract.sell_time, 138 | }); 139 | }); 140 | } 141 | 142 | export const helpers = { 143 | autoAdjustGetData, 144 | }; 145 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as LiveEvents } from './LiveEvents'; 2 | export { default as LiveApi } from './LiveApi'; 3 | export { helpers } from './custom'; 4 | export * as OAuth from './OAuth'; 5 | -------------------------------------------------------------------------------- /wallaby.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = (wallaby) => ({ 2 | files: [{ 3 | pattern: 'src/**/*.js*', 4 | load: false, 5 | }, { 6 | pattern: 'src/**/__tests__/*.js', 7 | ignore: true, 8 | }], 9 | tests: [ 10 | 'src/**/__tests__/*.js', 11 | ], 12 | env: { 13 | type: 'node', 14 | }, 15 | testFramework: 'jest', 16 | compilers: { 17 | '**/*.js': wallaby.compilers.babel({ 18 | presets: [ 19 | 'latests', 20 | ], 21 | }), 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | entry: ['./src/index.js'], 7 | plugins: [ 8 | new webpack.DefinePlugin({ 9 | 'process.env': { 10 | NODE_ENV: JSON.stringify('production'), 11 | }, 12 | }), 13 | ], 14 | module: { 15 | loaders: [ 16 | { 17 | test: /\.js$/, 18 | loader: 'babel-loader', 19 | include: path.join(__dirname, 'src'), 20 | }, 21 | { 22 | test: /\.js$/, 23 | loader: 'eslint-loader', 24 | include: path.join(__dirname, 'src'), 25 | }, 26 | ], 27 | }, 28 | output: { 29 | library: 'binary-live-api', 30 | libraryTarget: 'umd', 31 | path: path.resolve(__dirname, 'lib'), 32 | filename: 'binary-live-api.js', 33 | }, 34 | }; 35 | --------------------------------------------------------------------------------