Since starting algotrading in 2012, I have setup the quants I wrote to place automated trades.
So far I have used
E-Trade (Java SDK)
Robinhood (Hidden python SDK)
Alpaca (Python SDK)
BTC-e before they got hacked
I left E-Trade because they changed their setup so that a user has to approve every order that is placed through the API. Obviously the only reason I use the API is so that my quant can trade while I am at work. Making a user approve every trade makes the E-Trade api unsuitable for my use case. The process for applying for an api key can be summarized as ‘red tape everywhere’.
Robinhood kept promising users that there would be an API. It never came, and the private api that everyone discovered and started using, they blocked that. Then devs found another private API and they blocked that. After the second block I realized I had to switch brokerages. Robinhood is unsuitable for my use case.
Alpaca is ok. They have a python SDK that allows you to place all the trades you need. The problem I have with them is they have strange fees that the techs said would be updated with better descriptions but for me that didn’t happen. Also the books they have make it very hard to audit the past from the user interface. Do not use their pricing API, I have tested it and it gave info where the bid was higher than the ask.
I also have a strong opinion that the use of static typed languages is more correct for trading. There are so many situations that are not documented in the brokerage’s api where a call to their API using their python SDK returns a None and you get to find it out when your trading bot is running.
Currently I have my quants written using Swift for Tensorflow, so because of that I wrote a bare bones Swift Library to interact with the TDAmeritrade API.
Now the TDAmeritrade API isn’t great, parts of it are nonsense. But I just need the basics, which they currently provide.
These instructions are going to be provided for Mac OS, since that is what I use.
Make sure you have a brokerage account on tdameritrade.
Go to https://developer.tdameritrade.com and make an account and “App.” For the Callback URL, specify http://Localhost:8080/, and copy your App’s Consumer Key.
Now lets setup the xcode project, make a command line tool project.
Set the options for your project that you want
Then lets add the TDAmeritrade library I wrote to the xcode project. Click the plus shown below, then “Add Other”> “Add Package Dependency”
Put https://github.com/rezahussain/TDAmeritradeForSwift as shown below.
Hit next(Use 1.0.0 for the version now, that 0.1.0 screenshot is old)
Click next
Now we can add the code
//THE SAMPLE CODE ON THIS PAGE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
//INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
//INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) SUSTAINED BY YOU | |
//OR A THIRD PARTY, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
//CONTRACT, STRICT LIABILITY, OR TORT ARISING IN ANY WAY OUT OF THE USE OF | |
//THIS SAMPLE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
// | |
// | |
// main.swift | |
// HelloworldTrader | |
// | |
// Created by admin on 2/7/21. | |
// | |
import Foundation | |
import TDAmeritradeForSwift | |
let accountNumber = 42069 | |
let tdameritradeRedirectURI = "http://Localhost:8080/" | |
let appConsumerKey = "69420ABC" | |
let chosenSymbol = "SNAP" | |
let tdAmeritradeConsumerKey = appConsumerKey | |
let (someAuthCode,someClientId) = try TDAmeritradeForSwift.obtainInitialAuthorizationCodeUsingLocalhostServer(tempLocalhostServerPort:8080,tdameritradeRedirectURI:tdameritradeRedirectURI,tdameritradeConsumerKey:tdAmeritradeConsumerKey,sslCertPath:"/Users/admin/Desktop/localhost-ssl/localhost.crt",sslKeyPath:"/Users/admin/Desktop/localhost-ssl/localhost.key") | |
print(someAuthCode!) | |
print(someClientId) | |
print(tdameritradeRedirectURI) | |
let (refreshToken,accessToken) = TDAmeritradeForSwift.grantRefreshTokenAndAccessTokenUsingAuthorizationCode(clientId: someClientId, | |
authCode: someAuthCode!, | |
tdameritradeRedirectURI:tdameritradeRedirectURI) | |
let accessToken2 = TDAmeritradeForSwift.grantAccessTokenUsingRefreshToken(clientId:someClientId,refreshToken:refreshToken!,tdameritradeRedirectURI:tdameritradeRedirectURI) | |
print("\(accessToken2!)") | |
let someAccount = TDAmeritradeForSwift.getAccount(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!) | |
print(someAccount!) | |
//-----------------------| see existing position | |
//this returns the position if one exists in your account for the specified symbol | |
//let somePosition = TDAmeritradeForSwift.getPositionForSymbol(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!, chosenSymbol: "AAPL") | |
//print(somePosition) | |
//-----------------------| buy and cancel | |
/* | |
var someQuote = TDAmeritradeForSwift.getQuoteForSingleSymbol(symbol:chosenSymbol,accessTokenToUse:accessToken2!) | |
print(someQuote!) | |
let chosenPrice = someQuote!.bidPrice | |
var someOrder = TDAmeritradeForSwift.doOrder(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:chosenPrice,orderType: .buy) | |
print(someOrder!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
if someOrder!.cancelable! == true | |
{ | |
someOrder!.cancel(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
print(someOrder!) | |
print(someOrder!.status!) | |
} | |
*/ | |
//-----------------------| buy and sell | |
/* | |
let someQuote = TDAmeritradeForSwift.getQuoteForSingleSymbol(symbol:chosenSymbol,accessTokenToUse:accessToken2!) | |
print(someQuote!) | |
var someOrder = TDAmeritradeForSwift.doOrder(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:someQuote!.askPrice,orderType: .buy) | |
print(someOrder!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
while someOrder!.status!.compare("FILLED") != .orderedSame | |
{ | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
print(someOrder!) | |
print("waiting for BUY fill") | |
} | |
print(someOrder!.status!) | |
someOrder = TDAmeritradeForSwift.doOrder(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:someQuote!.bidPrice,orderType: .sell) | |
print(someOrder!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
while someOrder!.status!.compare("FILLED") != .orderedSame | |
{ | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
print(someOrder!) | |
print("waiting for SELL fill") | |
} | |
print(someOrder!.status!) | |
*/ | |
//-----------------------| sell short and cover | |
/* | |
let someQuote = TDAmeritradeForSwift.getQuoteForSingleSymbol(symbol:chosenSymbol,accessTokenToUse:accessToken2!) | |
print(someQuote!) | |
var someOrder = TDAmeritradeForSwift.doOrder(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:someQuote!.askPrice,orderType: .sellShort) | |
print(someOrder!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
while someOrder!.status!.compare("FILLED") != .orderedSame | |
{ | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
print(someOrder!) | |
print("waiting for SELL SHORT fill") | |
} | |
print(someOrder!.status!) | |
someOrder = TDAmeritradeForSwift.doOrder(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:someQuote!.bidPrice,orderType: .buyToCover) | |
print(someOrder!) | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
while someOrder!.status!.compare("FILLED") != .orderedSame | |
{ | |
someOrder!.refresh(tdAmeritradeAccountNumber: accountNumber, accessTokenToUse: accessToken2!) | |
print(someOrder!) | |
print("waiting for BUY TO COVER fill") | |
} | |
print(someOrder!.status!) | |
*/ | |
//-----------------------| | |
var someQuote = TDAmeritradeForSwift.getQuoteForSingleSymbol(symbol:chosenSymbol,accessTokenToUse:accessToken2!) | |
print(someQuote!) | |
let chosenPrice = someQuote!.askPrice | |
var someOrder = TDAmeritradeForSwift.doOrderFillOrKillImitation(tdAmeritradeAccountNumber:accountNumber,accessTokenToUse:accessToken2!,quantity:1,symbol:chosenSymbol,limitPrice:chosenPrice,timeLimitSecondsForFill:5,orderType: .buy) | |
print(someOrder!) |
Use the account number of your TDAmeritrade brokerage account, and your app’s consumer key from when you setup your app to run this code.
We need an access token to make authenticated requests to the TDAmeritrade API. Getting the access token from TDAmeritrade is overly complicated so I will explain.
The flow of events is as follows:
Pop up an authorization dialog where the user logs in and clicks ok, this is to grant your app an authorization code.
Once the user clicks ok, TDAmeritrade forwards to your callback url, with the authorization code attached.
Since we specified Localhost:8080 when we setup the App on the TDAmeritrade website, we need to have a server running on localhost:8080 to catch that callback. Don’t worry, the first function above stands up a localhost server temporarily to get that authorization code. But, on your computer you will need to generate SSL Certificates, because everything uses https now, and when tdameritrade forwards to localhost:8080, Safari is gonna change it to HTTPS://localhost:8080. To generate these ssl files, follow these instructions
// https://medium.com/@jonsamp/how-to-set-up-https-on-localhost-for-macos-b597bcf935ee // cd ~/ // mkdir .localhost-ssl // create a self signed key and certificate with next command // sudo openssl genrsa -out ~/.localhost-ssl/localhost.key 2048 // sudo openssl req -new -x509 -key ~/.localhost-ssl/localhost.key -out ~/.localhost-ssl/localhost.crt -days 3650 -subj /CN=localhost // now you have to drag and drop the crt file into your keychain app, so when the browser opens the callback https://localhost url, it doesnt // think that the self signed ssl cert for localhost that we just generated is a mitm attack // stuff is here: // ~/.localhost-ssl/localhost.crt // ~/.localhost-ssl/localhost.key // for some reason I had to move them to the desktop before the app got permissions to read them :\ // "/Users/admin/Desktop/localhost-ssl/localhost.crt" Once we get that code, we can request an access token along with a refresh token. An access token allows you to make authenticated requests to the TDAmeritrade API for 30 minutes then it expires. We can use the refresh token to request a new access token when the access token expires.
That should be enough to help you understand how to automate your trades! Just so you know I do not give financial advice and am not a financial advisor. Do your own research when deciding to make trades in the market.
This code is only tested on my 2017 Macbook running Mac OS Catalina.
how do you find the performance gain from using swift over java or c++ ? is there enough libraries or open source code for swift to use it as a 'server sidelong for trading ?
Thanks !