33 |
34 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-09a-Realtime-Market-Data-Pt2-Solution/src/renderPriceSize.js:
--------------------------------------------------------------------------------
1 | export const renderPriceSize = ({price, size}) => `
2 | ${price ? '
price: ' +price+ '' : ''}
3 | ${size ? '
size: ' +size+ '' : ''}
4 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-09a-Realtime-Market-Data-Pt2-Solution/src/renderQuote.js:
--------------------------------------------------------------------------------
1 |
2 | import { renderPriceSize } from './renderPriceSize'
3 |
4 | export const renderQuote = (symbol, {
5 | Bid,
6 | HighPrice,
7 | LowPrice,
8 | Offer,
9 | OpenInterest,
10 | OpeningPrice,
11 | SettlementPrice,
12 | TotalTradeVolume,
13 | Trade,
14 | }) => `
15 |
16 | ${symbol}
17 |
18 |
19 |
Bid
20 |
21 | ${renderPriceSize(Bid)}
22 |
23 |
24 |
25 |
HighPrice
26 |
27 | ${renderPriceSize(HighPrice)}
28 |
29 |
30 |
31 |
LowPrice
32 |
33 | ${renderPriceSize(LowPrice)}
34 |
35 |
36 |
37 |
Offer
38 |
39 | ${renderPriceSize(Offer)}
40 |
41 |
42 |
43 |
OpenInterest
44 |
45 | ${renderPriceSize(OpenInterest)}
46 |
47 |
48 |
49 |
OpeningPrice
50 |
51 | ${renderPriceSize(OpeningPrice)}
52 |
53 |
54 |
55 |
SettlementPrice
56 |
57 | ${renderPriceSize(SettlementPrice)}
58 |
59 |
60 |
61 |
TotalTradeVolume
62 |
63 | ${renderPriceSize(TotalTradeVolume)}
64 |
65 |
66 |
67 |
Trade
68 |
69 | ${renderPriceSize(Trade)}
70 |
71 |
72 |
73 |
74 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-09a-Realtime-Market-Data-Pt2-Solution/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-09a-Realtime-Market-Data-Pt2-Solution/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-09a-Realtime-Market-Data-Pt2-Solution/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/src/app.js:
--------------------------------------------------------------------------------
1 | import { connect } from './connect'
2 | import { credentials } from '../../../tutorialsCredentials'
3 | import { URLs } from '../../../tutorialsURLs'
4 | import { TradovateSocket } from '../../EX-07-Making-Requests/src/TradovateSocket'
5 | import { setAccessToken } from './storage'
6 |
7 | setAccessToken(null)
8 |
9 | const main = async () => {
10 |
11 | let unsubscribe //variable to hold our unsubscribe function
12 |
13 | const { accessToken } = await connect(credentials)
14 |
15 | //socket init
16 | const socket = new TradovateSocket()
17 | await socket.connect(URLs.MD_URL, accessToken)
18 |
19 | //HTML elements
20 | const $getChart = document.getElementById('get-chart-btn')
21 | const $statusInd = document.getElementById('status')
22 |
23 | const onStateChange = _ => {
24 | $statusInd.style.backgroundColor =
25 | socket.ws.readyState == 0 ? 'gold' //pending
26 | : socket.ws.readyState == 1 ? 'green' //OK
27 | : socket.ws.readyState == 2 ? 'orange' //closing
28 | : socket.ws.readyState == 3 ? 'red' //closed
29 | : /*else*/ 'silver' //unknown/default
30 | }
31 | socket.ws.addEventListener('message', onStateChange)
32 |
33 | }
34 |
35 | main()
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10-Chart-Data/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
10 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
49 |
50 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Chart
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/src/utils/counter.js:
--------------------------------------------------------------------------------
1 | export function Counter() {
2 | this.current = 0
3 | }
4 |
5 | Counter.prototype.increment = function() {
6 | return ++this.current
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-10a-Chart-Data-Solution/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
10 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
49 |
50 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Chart
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/src/app.js:
--------------------------------------------------------------------------------
1 | import { connect } from './connect'
2 | import { credentials } from '../../../tutorialsCredentials'
3 | import { URLs } from '../../../tutorialsURLs'
4 | import { TradovateSocket } from '../../EX-07-Making-Requests/src/TradovateSocket'
5 | import { setAccessToken } from './storage'
6 |
7 | setAccessToken(null)
8 |
9 | const main = async () => {
10 |
11 | let all_bars = []
12 | let unsubscribe
13 |
14 | const { accessToken } = await connect(credentials)
15 |
16 | //socket init
17 | const socket = new TradovateSocket()
18 |
19 | //HTML elements
20 | const $getChart = document.getElementById('get-chart-btn')
21 | const $statusInd = document.getElementById('status')
22 | const $symbol = document.getElementById('symbol')
23 | const $type = document.getElementById('type')
24 | const $nElements = document.getElementById('n-elements')
25 | const $elemSize = document.getElementById('elem-size')
26 |
27 |
28 | const onStateChange = _ => {
29 | $statusInd.style.backgroundColor =
30 | socket.ws.readyState == 0 ? 'gold' //pending
31 | : socket.ws.readyState == 1 ? 'green' //OK
32 | : socket.ws.readyState == 2 ? 'orange' //closing
33 | : socket.ws.readyState == 3 ? 'red' //closed
34 | : /*else*/ 'silver' //unknown/default
35 | }
36 |
37 | $getChart.addEventListener('click', async () => {
38 | all_bars = []
39 |
40 | if(unsubscribe) unsubscribe()
41 |
42 | unsubscribe = await socket.subscribe({
43 | url: 'md/getchart',
44 | body: {
45 | symbol: $symbol.value,
46 | chartDescription: {
47 | underlyingType: $type.value,
48 | elementSize: parseInt($elemSize.value),
49 | elementSizeUnit: 'UnderlyingUnits',
50 | withHistogram: false,
51 | },
52 | timeRange: {
53 | asMuchAsElements: parseInt($nElements.value)
54 | }
55 | },
56 | subscription: chart => {
57 | //we need to render our chart still!
58 | }
59 | })
60 | })
61 |
62 | await socket.connect(URLs.MD_URL, accessToken)
63 |
64 | socket.ws.addEventListener('message', onStateChange)
65 | }
66 |
67 | main()
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/src/utils/counter.js:
--------------------------------------------------------------------------------
1 | export function Counter() {
2 | this.current = 0
3 | }
4 |
5 | Counter.prototype.increment = function() {
6 | return ++this.current
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11-Tick-Charts/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
10 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
49 |
50 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Chart
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/renderDOM.js:
--------------------------------------------------------------------------------
1 |
2 | import { renderPriceSize } from './renderPriceSize'
3 |
4 | const renderBidOffer = bid => `
5 |
6 |
7 | ${renderPriceSize(bid)}
8 |
9 |
10 | `
11 |
12 | export const renderDOM = ({
13 | contractId,
14 | timestamp,
15 | bids,
16 | offers,
17 | }) => `
18 |
19 |
20 | ETHH1 - ${contractId}
21 |
22 |
23 |
24 |
25 |
Bids
26 | ${bids.map(renderBidOffer).join('')}
27 |
28 |
29 |
Offers
30 | ${offers.map(renderBidOffer).join('')}
31 |
32 |
33 |
34 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/renderPriceSize.js:
--------------------------------------------------------------------------------
1 | export const renderPriceSize = ({price, size}) => `
2 | ${price ? '
price: ' +price+ '' : ''}
3 | ${size ? '
size: ' +size+ '' : ''}
4 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/renderQuote.js:
--------------------------------------------------------------------------------
1 |
2 | import { renderPriceSize } from './renderPriceSize'
3 |
4 | export const renderQuote = ({
5 | Bid,
6 | HighPrice,
7 | LowPrice,
8 | Offer,
9 | OpenInterest,
10 | OpeningPrice,
11 | SettlementPrice,
12 | TotalTradeVolume,
13 | Trade,
14 | }) => `
15 |
16 | BTCH1
17 |
18 |
19 |
Bid
20 |
21 | ${renderPriceSize(Bid)}
22 |
23 |
24 |
25 |
HighPrice
26 |
27 | ${renderPriceSize(HighPrice)}
28 |
29 |
30 |
31 |
LowPrice
32 |
33 | ${renderPriceSize(LowPrice)}
34 |
35 |
36 |
37 |
Offer
38 |
39 | ${renderPriceSize(Offer)}
40 |
41 |
42 |
43 |
OpenInterest
44 |
45 | ${renderPriceSize(OpenInterest)}
46 |
47 |
48 |
49 |
OpeningPrice
50 |
51 | ${renderPriceSize(OpeningPrice)}
52 |
53 |
54 |
55 |
SettlementPrice
56 |
57 | ${renderPriceSize(SettlementPrice)}
58 |
59 |
60 |
61 |
TotalTradeVolume
62 |
63 | ${renderPriceSize(TotalTradeVolume)}
64 |
65 |
66 |
67 |
Trade
68 |
69 | ${renderPriceSize(Trade)}
70 |
71 |
72 |
73 |
74 | `
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/utils/counter.js:
--------------------------------------------------------------------------------
1 | export function Counter() {
2 | this.current = 0
3 | }
4 |
5 | Counter.prototype.increment = function() {
6 | return ++this.current
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-11a-Tick-Charts-Solution/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Open P/L
30 | --
31 |
32 |
33 |
Positions
34 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/app.js:
--------------------------------------------------------------------------------
1 | import { connect } from './connect'
2 | import { isMobile } from './utils/isMobile'
3 | import { DeviceUUID } from "device-uuid"
4 | import { tvPost } from './services'
5 | import { getDeviceId, setAccessToken, setDeviceId } from './storage'
6 | import { TradovateSocket } from './TradovateSocket'
7 | import { credentials } from '../../../tutorialsCredentials'
8 | import { URLs } from '../../../tutorialsURLs'
9 |
10 | setAccessToken(null)
11 |
12 | //MOBILE DEVICE DETECTION
13 | let DEVICE_ID
14 | if(!isMobile()) {
15 | const device = getDeviceId()
16 | DEVICE_ID = device || new DeviceUUID().get()
17 | setDeviceId(DEVICE_ID)
18 | } else {
19 | DEVICE_ID = new DeviceUUID().get()
20 | }
21 |
22 | //get relevant UI elements
23 | const $buyBtn = document.getElementById('buy-btn'),
24 | $sellBtn = document.getElementById('sell-btn'),
25 | $posList = document.getElementById('position-list'),
26 | $symbol = document.getElementById('symbol'),
27 | $openPL = document.getElementById('open-pl'),
28 | $qty = document.getElementById('qty')
29 |
30 |
31 | //Setup events for active UI elements.
32 | const setupUI = (socket) => {
33 | //We will hook up UI events here
34 | }
35 |
36 |
37 | //APPLICATION ENTRY POINT
38 | const main = async () => {
39 |
40 | //for caching our open positions
41 | const pls = []
42 |
43 | //Connect to the tradovate API by retrieving an access token
44 | await connect(credentials)
45 |
46 | //We will need a MarketDataSocket to get realtime price quotes to compare w/ our positions
47 | const socket = new TradovateSocket({debugLabel: 'Realtime API'})
48 | await socket.connect(URLs.WS_DEMO_URL)
49 |
50 | const mdsocket = new TradovateSocket({debugLabel: 'Market Data API'})
51 | await mdsocket.connect(URLs.MD_URL)
52 |
53 | //run the UI Setup
54 | setupUI(socket)
55 |
56 | //Calculate P&L! ...but how?
57 |
58 | }
59 |
60 | //START APP
61 | main()
62 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/renderPosition.js:
--------------------------------------------------------------------------------
1 | export const renderPos = (name, pl) => {
2 |
3 | return `
4 | ${name} : ($${pl.toFixed(2)})
5 | `
6 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/utils/isMobile.js:
--------------------------------------------------------------------------------
1 | export const isMobile = () => {
2 | let check = false;
3 | (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
4 | return check
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12-Calculating-Open-PL/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Tradovate API JS Example
8 |
9 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Open P/L
30 | --
31 |
32 |
33 |
Positions
34 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-api-js",
3 | "version": "1.0.0",
4 | "description": "Examples for using the Tradovate API with JavaScript.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/tradovate/example-api-js"
8 | },
9 | "main": "index.html",
10 | "scripts": {
11 | "build": "webpack build",
12 | "start": "webpack serve",
13 | "export": "webpack --watch"
14 | },
15 | "author": "Alexander Ross Lennert",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@babel/cli": "^7.13.14",
19 | "@babel/core": "^7.13.15",
20 | "@babel/node": "^7.13.13",
21 | "@babel/preset-env": "^7.13.15",
22 | "babel-loader": "^8.2.2",
23 | "del": "^2.2.0",
24 | "html-webpack-plugin": "^5.3.1",
25 | "path": "^0.12.7",
26 | "webpack": "^5.33.2",
27 | "webpack-cli": "^4.6.0",
28 | "webpack-dev-server": "^3.11.2"
29 | },
30 | "dependencies": {
31 | "@babel/polyfill": "^7.12.1",
32 | "device-uuid": "^1.0.4",
33 | "node-polyfill-webpack-plugin": "^1.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/src/connect.js:
--------------------------------------------------------------------------------
1 | import { setAccessToken, getAccessToken, tokenIsValid, setAvailableAccounts, setUserData, getUserData } from './storage'
2 | import { tvGet, tvPost } from './services'
3 | import { waitForMs } from './utils/waitForMs'
4 |
5 |
6 | const handleRetry = async (data, json) => {
7 | const ticket = json['p-ticket'],
8 | time = json['p-time'],
9 | captcha = json['p-captcha']
10 |
11 | if(captcha) {
12 | console.error('Captcha present, cannot retry auth request via third party application. Please try again in an hour.')
13 | return
14 | }
15 |
16 | console.log(`Time Penalty present. Retrying operation in ${time}s`)
17 |
18 | await waitForMs(time * 1000)
19 | await connect({ ...data, 'p-ticket': ticket })
20 | }
21 |
22 | export const connect = async (data) => {
23 | let { token, expiration } = getAccessToken()
24 |
25 | if(token && tokenIsValid(expiration)) {
26 | console.log('Already connected. Using valid token.')
27 | const accounts = await tvGet('/account/list')
28 | const { token, expiration } = getAccessToken()
29 | const { userId, name } = getUserData()
30 | setAvailableAccounts(accounts)
31 | return { accessToken: token, expirationTime: expiration, userId, name }
32 | }
33 |
34 | const authResponse = await tvPost('/auth/accesstokenrequest', data, false)
35 |
36 | if(authResponse['p-ticket']) {
37 | return await handleRetry(data, authResponse)
38 | } else {
39 | const { errorText, accessToken, userId, userStatus, name, expirationTime } = authResponse
40 |
41 | if(errorText) {
42 | console.error(errorText)
43 | return
44 | }
45 |
46 | setAccessToken(accessToken, expirationTime)
47 |
48 | setUserData({ userId, name })
49 |
50 | const accounts = await tvGet('/account/list')
51 |
52 | console.log(accounts)
53 |
54 | setAvailableAccounts(accounts)
55 |
56 | console.log(`Successfully stored access token ${accessToken} for user {name: ${name}, ID: ${userId}, status: ${userStatus}}.`)
57 |
58 | return authResponse
59 | }
60 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/src/renderPosition.js:
--------------------------------------------------------------------------------
1 | export const renderPos = (name, pl, netpos) => {
2 |
3 | return `
4 |
5 | ${name}${netpos > 0 ? '+' + netpos : '' + netpos}
:
(${pl.toFixed(2)} | USD)
6 |
7 | `
8 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/src/storage.js:
--------------------------------------------------------------------------------
1 | const STORAGE_KEY = 'tradovate-api-access-token'
2 | const EXPIRATION_KEY = 'tradovate-api-access-expiration'
3 | const DEVICE_ID_KEY = 'tradovate-device-id'
4 | const AVAIL_ACCTS_KEY = 'tradovate-api-available-accounts'
5 | const USER_DATA_KEY = 'tradovate-user-data'
6 |
7 | export const setDeviceId = (id) => {
8 | sessionStorage.setItem(DEVICE_ID_KEY, id)
9 | }
10 |
11 | export const getDeviceId = () => {
12 | return sessionStorage.getItem(DEVICE_ID_KEY)
13 | }
14 |
15 | export const setAvailableAccounts = accounts => {
16 | sessionStorage.setItem(AVAIL_ACCTS_KEY, JSON.stringify(accounts))
17 | }
18 |
19 | /**
20 | * Returns and array of available accounts or undefined.
21 | * @returns Account[]
22 | */
23 | export const getAvailableAccounts = () => {
24 | return JSON.parse(sessionStorage.getItem(AVAIL_ACCTS_KEY))
25 | }
26 |
27 | /**
28 | * Use a predicate function to find an account. May be undefined.
29 | */
30 | export const queryAvailableAccounts = predicate => {
31 | return JSON.parse(getAvailableAccounts()).find(predicate)
32 | }
33 |
34 | export const setAccessToken = (token, expiration) => {
35 | //if(!token || !expiration) throw new Error('attempted to set undefined token')
36 | sessionStorage.setItem(STORAGE_KEY, token)
37 | sessionStorage.setItem(EXPIRATION_KEY, expiration)
38 | }
39 |
40 | export const getAccessToken = () => {
41 | const token = sessionStorage.getItem(STORAGE_KEY)
42 | const expiration = sessionStorage.getItem(EXPIRATION_KEY)
43 | if(!token) {
44 | console.warn('No access token retrieved. Please request an access token.')
45 | }
46 | return { token, expiration }
47 | }
48 |
49 | export const tokenIsValid = expiration => new Date(expiration) - new Date() > 0
50 |
51 | export const setUserData = (data) => sessionStorage.setItem(USER_DATA_KEY, JSON.stringify(data))
52 | export const getUserData = () => JSON.parse(sessionStorage.getItem(USER_DATA_KEY))
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/src/utils/isMobile.js:
--------------------------------------------------------------------------------
1 | export const isMobile = () => {
2 | let check = false;
3 | (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
4 | return check
5 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/src/utils/waitForMs.js:
--------------------------------------------------------------------------------
1 | export const waitForMs = t => {
2 | return new Promise((res) => {
3 | setTimeout(() => {
4 | res()
5 | }, t)
6 | })
7 | }
--------------------------------------------------------------------------------
/tutorial/WebSockets/EX-12a-Calculating-Open-PL-Solution/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")
3 | const path = require("path");
4 |
5 | module.exports = {
6 |
7 | mode: "development",
8 |
9 | entry: ["@babel/polyfill", "./src/app.js"],
10 |
11 | output: {
12 | path: path.resolve(__dirname, "./dist"),
13 | filename: "bundle.js"
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.m?js$/,
20 | exclude: /node_modules/,
21 | use: {
22 | loader: "babel-loader",
23 | options: {
24 | presets: ['@babel/preset-env']
25 | }
26 | }
27 | }
28 | ]
29 | },
30 |
31 | plugins: [
32 | new HtmlWebpackPlugin({
33 | filename: "index.html",
34 | template: 'index.html'
35 | }),
36 | new NodePolyfillPlugin()
37 | ],
38 |
39 |
40 | devtool: "source-map",
41 | devServer: {
42 | contentBase: path.join(__dirname, "dist"),
43 | compress: true,
44 | port: 8080
45 | }
46 | };
--------------------------------------------------------------------------------
/tutorial/WebSockets/README.md:
--------------------------------------------------------------------------------
1 | # Connecting the Tradovate WebSocket Client
2 |
3 | In this section, we will focus on connecting the websocket client. We will discuss all of its features, including how to
4 | establish a connection, how to maintain a connection using *heartbeat frames*, how to send requests to the server, and
5 | what to expect back in response. For readers who are new to using the Tradovate API, connecting to websockets requires
6 | acquiring an access token. You can follow the lessons from [part one](https://github.com/tradovate/example-api-js/tree/main/tutorial/Access/EX-0-Access-Start) to learn how to acquire a token.
--------------------------------------------------------------------------------
/tutorial/tutorialsCredentials.js:
--------------------------------------------------------------------------------
1 | export const credentials = {
2 | name: "Your credentials here",
3 | password: "Your credentials here",
4 | appId: "Sample App",
5 | appVersion: "1.0",
6 | cid: 0,
7 | sec: "Your API secret here"
8 | }
--------------------------------------------------------------------------------
/tutorial/tutorialsURLs.js:
--------------------------------------------------------------------------------
1 | export const URLs = {
2 | DEMO_URL: "https://demo.tradovateapi.com/v1",
3 | LIVE_URL: 'https://live.tradovateapi.com/v1',
4 | MD_URL: 'wss://md.tradovateapi.com/v1/websocket',
5 | WS_DEMO_URL: 'wss://demo.tradovateapi.com/v1/websocket',
6 | WS_LIVE_URL: 'wss://live.tradovateapi.com/v1/websocket'
7 | }
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------