'"\"Not found: md/subscribeQuote\""'

Cannot sub to quote:

const main = async () => {
//…
//sub to quote
const unsubscribeQuote = await syncSocket.subscribe({
url: ‘md/subscribeQuote’,
//body: { symbol: [xId.sym] },
body: { symbol: ‘MESM2’ },
subscription: data => { console.log(data)}
})
}

console:
Uncaught (in promise)
FAILED:
operation ‘md/subscribeQuote’
query
body {
“symbol”: “MESM2”
}
reason ‘"“Not found: md/subscribeQuote”"’ (forward slash not found, the symbol does not show up on the page here, on the console it shows: " \ " Not found: md/subscribeQuote\ ")

I’m sure you have reasons, but you shouldn’t be using SyncSocket as you’re applying a synchronous approach to an asyncronous process. (events) You will be happier if you chose something else. I used the WebSocket js library

You also don’t have enough detail to describe the full process.
What library are you using? What does the ‘subscribe’ method do?

You need to

  • Post login info to a REST endpoint get an auth token
  • Use auth token to connect to a socket at a specific url
  • THEN pass that socket the md/subscribeQuote path with the payload

Thanks for the reply.
I am using the sample subscribequote code from tradovate:

const syncSocket = new TradovateSocket({debugLabel: ‘sync data’})

and

TradovateSocket.prototype.subscribe = async function({url, body, subscription}) {

const self = this

let removeListener = noop
let cancelUrl = ''
let cancelBody = {}
let contractId

console.log('url='+url)
let response = await this.send({url, body})


if(response.d['p-ticket']) {
    await waitForMs(response.d['p-time']*1000)
    let nextResponse = await self.send({url, body: {...body, 'p-ticket': response.d['p-ticket']}})
	console.log('p-ticket')
    response = nextResponse
}

const realtimeId = response?.d?.realtimeId || response?.d?.subscriptionId
if(body?.symbol && !body.symbol.startsWith('@')) {
    const contractRes = await tvGet('/contract/find', { name: body.symbol })
    contractId = contractRes?.id || null
    if(!contractId) {
        contractId = await tvGet('/contract/suggest', {name: body.symbol })[0].id
    }
}

if(!realtimeId && response.d && response.d.users) { //for user sync request's initial response
    subscription(response.d)
}

return new Promise((res, rej) => {
    //console.log('swtich='+url)
    switch(url.toLowerCase()) {
        case 'md/getchart': {
            cancelUrl = 'md/cancelChart'
            cancelBody = { subscriptionId: realtimeId }
            if(this.listeningURL !== MD_URL) rej('Cannot subscribe to Chart Data without using the Market Data URL.')
            removeListener = self.addListener(data => {
                if(data.d.charts) {
                    data.d.charts.forEach(chart => chart.id === realtimeId ? subscription(chart) : noop())
                }
            })
            break
        }
        case 'md/subscribedom': {
            cancelUrl = 'md/unsubscribedom'
            cancelBody = { symbol: body.symbol }
            if(this.listeningURL !== MD_URL) rej('Cannot subscribe to DOM Data without using the Market Data URL.')
            removeListener = self.addListener(data => {
                if(data.d.doms) {
                    data.d.doms.forEach(dom => dom.contractId === contractId ? subscription(dom) : noop())
                }
            })
            break
        }
        case 'md/subscribequote': {
			console.log('tvsocket/md/subquote reached')
            cancelUrl = 'md/unsubscribequote'
            cancelBody = { symbol: body.symbol }
            if(this.listeningURL !== MD_URL) rej('Cannot subscribe to Quote Data without using the Market Data URL.')
            removeListener = self.addListener(data => {
                if(data.d.quotes) {
                    data.d.quotes.forEach(quote => quote.contractId === contractId ? subscription(quote) : noop())
                } 
            })
            break
        }
        case 'md/subscribehistogram': {
            cancelUrl = 'md/unsubscribehistogram'
            cancelBody = { symbol: body.symbol }
            if(this.listeningURL !== MD_URL) rej('Cannot subscribe to Histogram Data without using the Market Data URL.')
            removeListener = self.addListener(data => {
                if(data.d.histograms) {
                    data.d.histograms.forEach(histogram => histogram.contractId === contractId ? subscription(histogram) : noop())
                } 
            })
            break
        }
        case 'user/syncrequest': {
			console.log('tvsocket/user/sync reached')
            if(this.listeningURL !== WS_DEMO_URL && url !== WS_LIVE_URL) rej('Cannot subscribe to User Data without using one of the Demo or Live URLs.')
            removeListener = self.addListener(data => {
                if(data?.d?.users || data?.e === 'props') {
                    subscription(data.d)
                }                         
            })
            break
        }
        default:
            rej('Incorrect URL parameters provided to subscribe.')
            break            
    }

    res(async () => {
        removeListener()
        if(cancelUrl && cancelUrl !== '') {
            await self.send({ url: cancelUrl, body: cancelBody })
        }
    })
})

}

and

TradovateSocket.prototype.send = async function({url, query, body, onResponse, onReject}) {
const self = this

return new Promise((res, rej) => {
	
    const id = this.increment()
	
    self.ws.addEventListener('message', function onEvent(msg) {
        const [_, data] = prepareMessage(msg.data)        
        data.forEach(item => {
            if(item.s === 200 && item.i === id) {  
                if(onResponse) {
                    onResponse(item)
                }
                self.ws.removeEventListener('message', onEvent)
                res(item)
            } else if(item.s && item.s !== 200 && item.i && item.i === id) {
                //console.log(item)
                self.ws.removeEventListener('message', onEvent)
                if(onReject) onReject()
                rej(`\nFAILED:\n\toperation '${url}'\n\tquery ${query ? JSON.stringify(query, null, 2) : ''}\n\tbody ${body ? JSON.stringify(body, null, 2) : ''}\n\treason '${JSON.stringify(item?.d, null, 2) || 'unknown'}'`)
            } 
        })
    })
	
	console.log(`${url}\n${id}\n${query || ''}\n${JSON.stringify(body)}`)
    
	this.ws.send(`${url}\n${id}\n${query || ''}\n${JSON.stringify(body)}`)
})

}

====
It seems the error occurred right after this step:

under TradovateSocket.prototype.send

console.log(${url}\n${id}\n${query || ''}\n${JSON.stringify(body)})
this.ws.send(${url}\n${id}\n${query || ''}\n${JSON.stringify(body)})

The console shows:

md/subscribequote
3

{“symbol”:[“MESM2”]}

index.js:9 Uncaught (in promise)
FAILED:
operation ‘md/subscribequote’
query
body {
“symbol”: [
“MESM2”
]
}
reason ‘" \ “Not found: md/subscribequote”"’

well… ok… looks like you’re connected fine… the message is just wrong. what stands out to me is that you’re passing an array in your ‘symbol’ field.

Here’s mine:


	sendQuoteRequest(contract, messageCount){
                log.verbose(`TradovateQuoteStream:sendQuoteRequest`)		
                this.send(`md/subscribequote\n${messageCount}\n\n{"symbol":${contract.id}}`)
	}

where ‘this’ refers to a websocket… but I think that’s also how your code works.

So basically I’d change the body to something like:

body = {"symbol" : 12345}

Where the symbol is the numeric Id obtained for the contract. I don’t think you can us the instrument symbol in the subscription ‘symbol’ field. It’s counter intuitive for sure. check the docs

I tried both symbol name, and ID, with and without array, the response is the same:

passing to ws.send:
md/subscribequote
3

{“symbol”:[2553027]}

response:
index.js:9 Uncaught (in promise)
FAILED:
operation ‘md/subscribequote’
query
body {
“symbol”: [
2553027
]
}
reason ‘" \ “Not found: md/subscribequote”"’

Passing to ws.send:
md/subscribequote
3

{“symbol”:2553027}

response:

index.js:9 Uncaught (in promise)
FAILED:
operation ‘md/subscribequote’
query
body {
“symbol”: 2553027
}
reason ‘" \ “Not found: md/subscribequote”"’

In tradovate’s own code:
under TradovateSocket.prototype.send

console.log( ${url}\n${id}\n${query || ''}\n${JSON.stringify(body)} )
this.ws.send( ${url}\n${id}\n${query || ''}\n${JSON.stringify(body)} )

And it gives above responses

I’m not familiar with the code base you’re using. I found issues with Tradovate’s code personally.

but you also need 2 new lines before the body. see my example. (it looks like you are passing 2 new lines from the console output but it’s hard to tell given the last couple of lines

if you want a working example you can look at the web console of a running browser after logging in. navigate to the websocket connection to view the data going back and forth. subscribe to new data in the website… and voila… you see the whole cycle. Their webapp uses the api except slightly different urls

I think I got it finally.

I need to connect to WS_DEMO_URL to subscribe to user/syncrequest
But to a different address, MD_URL, to sub to market data

I was only connected to the first url before, so it was not able to find md/subscribequote (but for reasons, it says " \ " not found. a pretty cryptic hint)

Now I created 2 socket objects, one connected to WS_DEMO_URL for account updates, one connected to MD_URL for quotes

It seems to be working OK so far. Thank you for helping me along. I am pretty new to Javascript.

YES!!

It’s actually a bit counter intuitive…

#LIVE
TRADOVATE_QUOTE_URL=wss://md.tradovateapi.com/v1/websocket
TRADOVATE_ACCOUNT_URL=wss://live.tradovateapi.com/v1/websocket
TRADOVATE_LOGIN_URL=https://live.tradovateapi.com/v1/

#DEMO API
TRADOVATE_QUOTE_URL=wss://md-demo.tradovateapi.com/v1/websocket
TRADOVATE_ACCOUNT_URL=wss://demo.tradovateapi.com/v1/websocket
TRADOVATE_LOGIN_URL=https://demo.tradovateapi.com/v1/

I conceptually split this into Account Functions & Quote Functions. I have a base class which does the https login… then two sub classes which handle the separate streaming subscriptions

1 Like

That is very helpful. Thank you.

To subscribe to quotes on 2 symbols at the same time, should I send in an array of symbols over 1 connection to TRADOVAE_QUOTE_URL, or declare 2 websockets and make 2 connections at the same time? I tried sending an array but it doesn’t seem to work.


I got it. I just got to run the subscribequote endpoint twice with the same connection.