Are there any existing examples on how to pull quote data?
What exactly do you mean by pull quote data? Like set up a socket to receive live quote or replay quote data?
Yes, I am using C# and the demo provided has been very helpful. However, I have used other platforms in testing and it was very obvious how to get quote data. I cannot seem to find it here. Also, if they cap it, as I have read, it is probably a waste of time since I pull 1 million+ quotes a day with other brokers and they have no limitations.
I am not very familiar with c# but here is simplified way of doing it in TypeScript. Also here is a short write up from the Tradovate docs on the parameters for getChart that start a tick stream. The Tradovate javascript tutorial shows a way to get tick data too. You can reference these FAQ responses on limits: 1. Limit Requests and Data and 2. Data Size Limits.
It does not sound like there are hard limits.
import WebSocket, {MessageEvent, Data } from 'ws'
// Type for data received by Tick subscription
export type TickPacket = {
id: number // Subscription ID, the same as historical/real-time subscription IDs from request response.
eoh?: boolean // End-of-history used in historical data loads indicates that historical ticks are loaded and further packets will contain real-time ticks
s: string // Source of packet data.
td: string // Trade date YYYYMMDD.
bp: number // Base price of the packet (integer number of contract tick sizes). Tick prices are calculated as relative from this one.
bt: number // Base timestamp of the packet. Tick timestamps are calculated as relative from this value.
ts: number // Tick size of the contract for which the tick chart is requested.
tks: TickRaw[]
}
export type TickRaw = {
id: number // Tick ID
t: number // Tick relative timestamp. Actual tick timestamp is packet.bt + tick.t
p: number // Tick relative price (in contract tick sizes). Actual tick price is packet.bp + tick.p
s: number // Tick size (seems more proper name should be tick volume). Please don't confuse with contract tick size (packet.ts).
b: number // Bid relative price (optional). Actual bid price is packet.bp + tick.b
a: number // Ask relative price (optional).Actual ask price is packet.bp + tick.a
bs: number // Bid size (optional).
as: number // Ask size (optional).
}
// Id counter be passed every socket request
let requestId = 0
// the id to pass to md/cancelchart when done with tick stream
let cancelId: number
// reference to heartbeat interval to cancel interval when done with code.
let heartbeatInterval: NodeJS.Timer
// Receive an auth token
const token = getAccessToken() // I assume you have this logic
//Helper function to check if marketData Socket is connected
const isConnected = () => {
return marketDataSocket &&
marketDataSocket.readyState !== 0 &&
marketDataSocket.readyState !== 2 &&
marketDataSocket.readyState !== 3 &&
marketDataSocket.readyState !== undefined
}
//Heartbeat logic to be passed in to setInterval
const sendHeartbeat = () => {
if (!isConnected()) {
clearInterval(heartbeatInterval)
return
}
marketDataSocket.send('[]')
}
// Helper function for parsing socket data
const prepareMessage = (raw: Data) => {
const T = raw.slice(0, 1)
let data = []
if ((raw as string).length > 1) {
data = JSON.parse((raw as string).slice(1))
}
return {T, data}
}
// Once marketDataSocket is connected it will emit an 'o' open frame.
// On open frame connect with auth token and start heartbeat.
const onConnect = async (msg: MessageEvent) => {
const {T} = prepareMessage(msg.data)
if (T === 'o') {
await marketDataSocket.send(`authorize\n${requestId}\n\n${token}`)
requestId++
console.log('[MarketData]: connected.')
marketDataSocket.removeEventListener('message', onConnect)
heartbeatInterval = setInterval(sendHeartbeat, 2500)
}
}
// Attach onConnect event listener to socket
marketDataSocket.addEventListener('message', onConnect)
// connect to market data web socket endpoint
const marketDataSocket = new WebSocket('wss://md.tradovateapi.com/v1/websocket')
// Params for tick stream on ESU2023
const tickChartParams = {
symbol: 'ESU3',
chartDescription: {
underlyingType: 'Tick',// Available values: Tick, DailyBar, MinuteBar, Custom, DOM
elementSize: 1,
elementSizeUnit: 'UnderlyingUnits', // Available values: Volume, Range, UnderlyingUnits, Renko, MomentumRange, PointAndFigure, OFARange
withHistogram: false,
},
timeRange: {
asMuchAsElements: 2
}
}
// Tick event handler
const onTickData = (msg: MessageEvent) => {
const {T, data} = prepareMessage(msg.data)
if (T === 'a' && data && data.length > 0) {
data.forEach((item: any) => {
// initial response from getChart Request
// type guard for chart data, normally you wouldn't hard code 1 as id
if(item.s === 200 && item.i === 1){
cancelId = item.d.realtimeId
}
if(item.d && item.d.charts) {
item.d.charts.forEach((chart: TickPacket) => {
// manipulate data
console.log(chart)
})
}
})
}
}
marketDataSocket.addEventListener('message', onTickData)
// request id updated to 1
marketDataSocket.send(`md/getchart\n${requestId}\n\n${tickChartParams}`)
requestId++
const cancelTickSubscription = () => {
const cancelBody = {subscriptionId: cancelId}
marketDataSocket.send(`md/cancelchart\n${requestId}\n\n${cancelBody}`)
clearInterval(heartbeatInterval)
}
// After 30 seconds cancel tick subscription and clear interval
setTimeout(()=>{
cancelTickSubscription()
}, 30*1000)
The main issue with the above code is how the requestId counter is handled.