From b7a651fb66f3445834bd109414da7f44c8b5bd83 Mon Sep 17 00:00:00 2001 From: Tyler Floyd Date: Sat, 5 Jan 2019 21:18:58 -0700 Subject: [PATCH] initial commit --- .gitignore | 6 + README.md | 119 +++++++++++++ index.js | 5 + lib/accounts.js | 37 ++++ lib/authorization.js | 31 ++++ lib/balances.js | 29 ++++ lib/history.js | 41 +++++ lib/index.js | 36 ++++ lib/liveOrders.js | 31 ++++ lib/positions.js | 29 ++++ package-lock.json | 383 +++++++++++++++++++++++++++++++++++++++++ package.json | 32 ++++ sample/index.js | 47 +++++ util/defaultHeaders.js | 11 ++ util/endpoints.js | 11 ++ 15 files changed, 848 insertions(+) create mode 100644 README.md create mode 100644 index.js create mode 100644 lib/accounts.js create mode 100644 lib/authorization.js create mode 100644 lib/balances.js create mode 100644 lib/history.js create mode 100644 lib/index.js create mode 100644 lib/liveOrders.js create mode 100644 lib/positions.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 sample/index.js create mode 100644 util/defaultHeaders.js create mode 100644 util/endpoints.js diff --git a/.gitignore b/.gitignore index ad46b30..b57ad2d 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,9 @@ typings/ # next.js build output .next + +# IDE +*.vscode + +#OSX +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c7feb7 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +# Tastyworks Node API + +NodeJS Framework for basic account information, balances, positions and orders with [TastyWorks's](https://www.tastyworks.com/) private API. This API has been reversed engineered. The stability and accuracy of this API cannot be garunteed. + +- [Features](#features) +- [Installation](#installation) +- [Getting Started](#gettingStarted) +- [API](#API) + - [`User`](#user) + - [`Authorization`](#authorization) + - [`Accounts`](#accounts) + - [`Balance`](#balances) + - [`Positions`](#positions) + - [`LiveOrders`](#liveorders) + - [`History`](#history) +- [TODOs](#todos) + +## Features + +- Account Balances +- Account History +- Current Orders +- Current Positions + +## Installation + +```bash +$ npm install tastyworks --save +``` + +## Getting Started + +`/sample/index.js` has an example of each call configured. To run and see each response, replace `YOUR_USERNAME' and 'YOUR_PASSWORD' and 'ACCOUNT_ID' with your Tastyworks credentials. Then run`\$ cd sample && node index.js` in your Terminal. + +## API + +### `User` + +Set account variables after authentication is complete so the variables can be used later. The object has four keys, `username`, `password`, `authorization_token` and `accounts`. By deafult, you are only required to set the `username` and `password`. In order to make sure the proper credentials are set as well as, to view the associated account(s) with the user, the `getUser()` function is provided. + +> **NOTE:** you must set the user object's `username` and `password` before other endpoints will work + +```js +const TastyWorks = require('tastyworks'); +const credentials = { + username: 'username', + password: 'password' +}; + +TastyWorks.setAccount(credentials); +console.log(TastyWorks.getUser()); +``` + +### `Authorization()` + +The authorization endpoint will return a session token that needs to be set into headers that are used in subsequent calls. Also, this call assumes that you have previously set the `username` and `password` in the users object. + +```js +TastyWorks.authorization().then(token => { + // REQUIRED: Apply the new session token to the headers object passed into each call + TastyWorks.setAuthorizationToken(token); + + // OPTIONAL: Set the session token in the user object + TastyWorks.setUser({ + authorization_token: token + }); + return true; +}); +``` + +### `Accounts()` + +Get all accounts associated with the logged in user. The response is an array of account objects. While it is easy to add these to the user object to be referenced when needed. You can hardcode the account that you wish to reference and pass that into the other calls, too. + +```js +TastyWorks.accounts().then(accounts => + TastyWorks.setUser({ + accounts + }) +); +``` + +### `Balances(ACCOUNT_ID)` + +Fetch the balances for a single account + +```js +TastyWorks.balances('ACCOUNT_ID').then(balances => console.log(balances)); +``` + +### `Positions(ACCOUNT_ID)` + +Fetch the current positions for a single account + +```js +TastyWorks.positions('ACCOUNT_ID').then(positions => console.log(positions)); +``` + +### `LiveOrders(ACCOUNT_ID)` + +Fetch the current (non-complete) orders for a single account. This can include orders that have been partially fulfilled + +```js +TastyWorks.liveOrders('ACCOUNT_ID').then(liveOrders => console.log(liveOrders)); +``` + +### `History(ACCOUNT_ID, START_DATE, END_DATE)` + +Fetch the accounts order history for a given time period. Date format is `yyyy-mm-dd`. + +```js +TastyWorks.history('ACCOUNT_ID', '2018-01-01', '2018-01-05').then(history => console.log(history)); +``` + +## TODOs + +- Position groupings +- Fetch news for an equity +- Current option prices diff --git a/index.js b/index.js new file mode 100644 index 0000000..6a327c8 --- /dev/null +++ b/index.js @@ -0,0 +1,5 @@ +'use strict'; + +const index = require('./lib/index'); + +module.exports = index; diff --git a/lib/accounts.js b/lib/accounts.js new file mode 100644 index 0000000..ae3580a --- /dev/null +++ b/lib/accounts.js @@ -0,0 +1,37 @@ +'use strict'; + +/** + * Get Accounts + * @param {object} headers + * @return {object} array of accounts + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (headers) => { + const endpoint = endpoints['accounts'](); + return request + .get(`${endpoint}`) + .set(headers) + .then(res => { + const { + body: { + data: { + items + } + } + } = res; + + const accounts = items.map(data => { + const { + account + } = data; + + return account; + }); + + return accounts; + }) + .catch(err => err.message); +} diff --git a/lib/authorization.js b/lib/authorization.js new file mode 100644 index 0000000..b45dcf2 --- /dev/null +++ b/lib/authorization.js @@ -0,0 +1,31 @@ +'use strict'; + +/** + * Get Authorization Token + * @param {string} username + * @param {string} passworde + * @return {string} session token + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (username, password, headers) => { + const endpoint = endpoints['login'](); + return request + .post(`${endpoint}`) + .set(headers) + .send({ + login: username, + password: password + }) + .then(res => { + const { + body: { + data + } + } = res; + return data['session-token']; + }) + .catch(err => err.message); +} diff --git a/lib/balances.js b/lib/balances.js new file mode 100644 index 0000000..9c0ef81 --- /dev/null +++ b/lib/balances.js @@ -0,0 +1,29 @@ +'use strict'; + +/** + * Get Account Balances + * @param {object} headers + * @param {string} accountId + * @return {object} account balances + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (headers, account_id) => { + const endpoint = endpoints['balances'](account_id); + return request + .get(`${endpoint}`) + .set(headers) + .send() + .then(res => { + const { + body: { + data + } + } = res; + + return data; + }) + .catch(err => err.message); +} diff --git a/lib/history.js b/lib/history.js new file mode 100644 index 0000000..19550ec --- /dev/null +++ b/lib/history.js @@ -0,0 +1,41 @@ +'use strict'; + +/** + * Get Account History + * @param {object} headers + * @param {string} account_id + * @param {date} start_date (yyyy-mm-dd) + * @param {date} end_date (yyyy-mm-dd) + * @return {object} account history + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (headers, account_id, start_date, end_date) => { + const endpoint = endpoints['history'](account_id); + return request + .get(`${endpoint}`) + .query({ + 'start-date': `${start_date}T07:00:00.000Z` + }) + .query({ + 'end-date': `${end_date}T07:00:00.000Z` + }) + .set(headers) + .send() + .then(res => { + const { + body: { + data: { + items + } + } + } = res; + + return items; + }) + .catch(err => { + return err.message; + }); +} diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..d0d6683 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,36 @@ +'use strict'; + +const _authorization = require('./authorization'); +const _accounts = require('./accounts'); +const _balances = require('./balances'); +const _positions = require('./positions'); +const _liveOrders = require('./liveOrders'); +const _history = require('./history'); + +let _headers = require('../util/defaultHeaders'); + +let _user = { + username: null, + password: null, + authorization_token: null, + accounts: [] +}; + +module.exports = { + setUser: user => { + _user = { + ..._user, + ...user + }; + }, + getUser: () => _user, + setAuthorizationToken: (authorization_token) => _headers['Authorization'] = authorization_token, + getHeaders: () => _headers, + + authorization: () => _authorization(_user.username, _user.password, _headers), + accounts: () => _accounts(_headers), + balances: (account_id) => _balances(_headers, account_id), + positions: (account_id) => _positions(_headers, account_id), + liveOrders: (account_id) => _liveOrders(_headers, account_id), + history: (account_id, start_date, end_date) => _history(_headers, account_id, start_date, end_date) +} diff --git a/lib/liveOrders.js b/lib/liveOrders.js new file mode 100644 index 0000000..a4e46c8 --- /dev/null +++ b/lib/liveOrders.js @@ -0,0 +1,31 @@ +'use strict'; + +/** + * Get Account Orders + * @param {object} headers + * @param {string} accountId + * @return {object} orders + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (headers, account_id) => { + const endpoint = endpoints['liveOrders'](account_id); + return request + .get(`${endpoint}`) + .set(headers) + .send() + .then(res => { + const { + body: { + data: { + items + } + } + } = res; + + return items; + }) + .catch(err => err.message); +} diff --git a/lib/positions.js b/lib/positions.js new file mode 100644 index 0000000..7f5059f --- /dev/null +++ b/lib/positions.js @@ -0,0 +1,29 @@ +'use strict'; + +/** + * Get Account Positions + * @param {object} headers + * @param {string} accountId + * @return {object} account positions + */ + +const request = require('superagent'); +const endpoints = require('../util/endpoints'); + +module.exports = (headers, account_id) => { + const endpoint = endpoints['positions'](account_id); + return request + .get(`${endpoint}`) + .set(headers) + .send() + .then(res => { + const { + body: { + data + } + } = res; + + return data; + }) + .catch(err => err.message); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..072e953 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,383 @@ +{ + "name": "tastyworks", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "qs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.6.0.tgz", + "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==" + }, + "readable-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.0.tgz", + "integrity": "sha512-vpydAvIJvPODZNagCPuHG87O9JNPtvFEtjHHRVwNVsVVRBqemvPJkc2SYbxJsiZXawJdtZNmkmnsPuE3IgsG0A==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "superagent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-4.0.0.tgz", + "integrity": "sha512-qaGDf+QUYxgMYdJBWCezHnc3UjrCUwxm5bCfxBhTXI5BbCluVzmVNYzxvCw1jP9PXmwUZeOW2yPpGm9fLbhtFg==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.2", + "debug": "^4.0.0", + "form-data": "^2.3.2", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^2.0.3", + "qs": "^6.5.1", + "readable-stream": "^3.0.3" + }, + "dependencies": { + "debug": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..38c0dea --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "tastyworks", + "version": "0.1.0", + "description": "NodeJS API wrapper for Tastyworks API", + "main": "index.js", + "scripts": {}, + "repository": { + "type": "git", + "url": "https://github.com/tylerfloyd/TastyWorks.git" + }, + "keywords": [ + "tasty works", + "tasty trade", + "api", + "trading", + "stock", + "options", + "finance" + ], + "author": "Tyler Floyd", + "license": "MIT", + "bugs": { + "url": "https://github.com/tylerfloyd/TastyWorks/issues" + }, + "homepage": "https://github.com/tylerfloyd/TastyWorks", + "engines": { + "node": ">=6.0.0" + }, + "dependencies": { + "superagent": "^4.0.0" + } +} diff --git a/sample/index.js b/sample/index.js new file mode 100644 index 0000000..58fbc1c --- /dev/null +++ b/sample/index.js @@ -0,0 +1,47 @@ +'use strict'; + +const TastyWorks = require('../lib/index'); +const credentials = { + username: 'YOUR_USERNAME', + password: 'YOUR_PASSWORD' +}; + +// Set the username and password +TastyWorks.setUser(credentials); + +// Before making any calls, get the session-token via the authorization endpoint +TastyWorks.authorization() + .then(token => { + // Set the authorization in the headers + TastyWorks.setAuthorizationToken(token); + console.log('Session is active, continue with other calls.'); + return true; + }) + .then(() => TastyWorks.accounts()) + .then(accounts => TastyWorks.setUser({ + accounts + })) + .then(() => { + console.log('======= USER OBJECT ======='); + console.log(TastyWorks.getUser()); + }) + .then(() => TastyWorks.balances('ACCOUNT_ID')) + .then(balances => { + console.log('======= ACCOUNT BALANCES ======='); + console.log(balances) + }) + .then(() => TastyWorks.positions('ACCOUNT_ID')) + .then(positions => { + console.log('======= ACCOUNT POSITIONS ======='); + console.log(positions) + }) + .then(() => TastyWorks.liveOrders('ACCOUNT_ID')) + .then(liveOrders => { + console.log('======= ACCOUNT LIVEORDERS ======='); + console.log(liveOrders) + }) + .then(() => TastyWorks.history('ACCOUNT_ID', '01/01/2019', '01/05/2019')) + .then(history => { + console.log('======= ACCOUNT HISTORY ======='); + console.log(history) + }); diff --git a/util/defaultHeaders.js b/util/defaultHeaders.js new file mode 100644 index 0000000..613178d --- /dev/null +++ b/util/defaultHeaders.js @@ -0,0 +1,11 @@ +module.exports = { + 'Accept': 'application/json, text/javascript, */*; q=0.01', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'en-US,en;q=0.9,tr;q=0.8', + 'Accept-Version': 'v1', + 'Authorization': null, + 'Connection': 'keep-alive', + 'Host': 'api.tastyworks.com', + 'Origin': 'https://trade.tastyworks.com', + 'Referer': 'https://trade.tastyworks.com/tw' +} diff --git a/util/endpoints.js b/util/endpoints.js new file mode 100644 index 0000000..4cfbbbb --- /dev/null +++ b/util/endpoints.js @@ -0,0 +1,11 @@ +const baseURL = 'https://api.tastyworks.com'; + +module.exports = { + login: () => `${baseURL}/sessions`, + validateLogin: () => `${baseURL}/sessions/validate`, + accounts: () => `${baseURL}/customers/me/accounts`, + balances: (account_id) => `${baseURL}/accounts/${account_id}/balances`, + positions: (account_id) => `${baseURL}/accounts/${account_id}/positions`, + liveOrders: (account_id) => `${baseURL}/accounts/${account_id}/orders/live`, + history: (account_id) => `${baseURL}/accounts/${account_id}/transactions` +}