Updated Review of the TD Ameritrade API

Last week I posted something about the TD Ameritrade API for trading instruments. My impressions of their API were not great. Well, I tried to re-code what I was attempting to accomplish in ReactJS and well, this time it worked, kinda…

Previously I was running into issues with utilizing their streaming API which utilizes WebSockets to retrieve real-time data of stock quotes. I wasn’t able to even login to the platform and was receiving a “Bad formatting” error. I couldn’t determine what I was doing wrong here and two weeks later it was still bothering me so I decided to utilize the example that they provided at the top of their documentation.

I used parts of their example and finagled it into a quickly written ReactJS application and the streaming data part works pretty well. For starters, I created a method that encodes the data in JSON and calls the WebSocket client’s send method so that I could reuse it for sending w/e I want.

    async sendRequest(data) {
        try {
            this.props.client.send(JSON.stringify(data));
        }
        catch(error) {
            console.log(error);
        }
    }

Then I created a login method that utilizes data from the original user principals call to send a request to login before I can subscribe to a data stream. They have a server-side application that they call The Streamer that allows users to subscribe to an event emitter that sends out quote updates, etc. Note that I’m storing this data in a component that handles all interactions with the API and retrieving the credentials from the state.

        var request = {
            "requests": [
                    {
                        "service": "ADMIN",
                        "command": "LOGIN",
                        "requestid": 0,
                        "account": this.state.userPrincipalsResponse.accounts[0].accountId,
                        "source": this.state.userPrincipalsResponse.streamerInfo.appId,
                        "parameters": {
                            "credential": this.jsonToQueryString(this.state.credentials),
                            "token": this.state.userPrincipalsResponse.streamerInfo.token,
                            "version": "1.0",
                            "qoslevel": 0
                        }
                    }
            ]
        }
        this.sendRequest(request);

Once a connection to the WebSocket client is created, I make sure to immediately send the login request so I’m logged into the application. From here, after a user enters a symbol, I send a request to subscribe to a quote field and retrieve the last price.

    async subscribeToQuote(symbol) {
        var request = {
            "requests": [
                    {
                        "service": "QUOTE",
                        "command": "SUBS",
                        "requestid": Math.round(Math.random()*10000),
                        "account": this.state.userPrincipalsResponse.accounts[0].accountId,
                        "source": this.state.userPrincipalsResponse.streamerInfo.appId,
                        "parameters": {
                            "keys": this.props.symbol,
                            "fields": "0,3"
                        }
                    }
            ]
        }
        this.sendRequest(request);
    }

Note, the fields are numerical values that represent a quote field value, in this case, I’m requesting the symbol and the last price. To understand what all of the fields mean, see here. This information was sufficient for the application that was building. When creating your WebSocket client, you can bind to the onmessage event that will let you handle what happens when you receive data. In my case, I only need to know if the login worked successfully and when the server sends me a message that the quote last price updated. The following is how I handle any data that comes through. I need to know if the application logged in successfully and update the last price state throughout my application for use in calculations and presenting it to the user.

    connectToServer() {
        var client = new WebSocket("wss://" + this.state.userPrincipalsResponse.streamerInfo.streamerSocketUrl + "/ws");
        client.onopen = () => {
            toast.success("Connected to Streaming Server...");
        }
        client.onmessage = (event) =>  {
            console.log("received message");
            const message = JSON.parse(event.data);
            console.log(message);
            this.handleConnectionData(message);
        }
        client.onclose = () =>  {
            toast.info("Disconnected from Streaming Server...");
            this.connectToServer();
        }
        this.props.onCreateClient(client);
    }
    handleConnectionData(data) {
        if(data.response) {
            if(data.response.length > 0) {
                if(data.response[0].command==="LOGIN") {
                    if(data.response[0].content.code===0) {
                        toast.success("Logged In Succesfully");
                    }
                }
            }
        }
        if(data.data && data.data[0].service === "QUOTE" && data.data[0].content[0]["key"] === this.props.symbol) {

            this.props.setLastPrice(data.data[0].content[0]["3"]);
            this.props.calculateQuantity();
        }
    }

Once I run my application, and I connect to the server and enter symbol price, I’m able to see quote data update very frequently and see my calculations update accordingly.

While working on the portion of my application to actually place an order, I ran into an error “Conditional orders are not permitted for accounts in this segment.” Based on some googling it has something to do with my account but I don’t know what the issue is for sure but it renders my application mostly useless. I’ll update this post once I find out more information.


Posted

in

by

Comments

One response to “Updated Review of the TD Ameritrade API”

  1. Marc Avatar
    Marc

    “Conditional orders are not permitted for accounts in this segment.” is probably because you have a margin account because they don’t allow those to use conditional orders.

    Also, great reviews. 👍

Leave a Reply

Your email address will not be published. Required fields are marked *

Share via
Copy link