Historical End-of-Day Price Data
- a look at online data vendors – spoiler alert: we will use Tiingo
- explore the Tiingo API
- examine API responses and status codes
- define common data types for our application
- build a client to query the Tiingo API
Vendors
Historical Price Data Vendors
Google this: “end of day stock data”
eoddata.com
lots of markets, varying prices from free to $450 annual confusing for entry level programming designed to integrate with commercial platforms rather expensive for our purposes
eodhistoricaldata.com
free :: 20 api calls /day $20-$80 / month :: 100,000 api calls /day
api.tiingo.com
free :: 500 calls/hr, 20K /day, 5 GB /month $10 / month :: 20K calls/hr, 150K /day, 100 GB /month $50 / month :: 40K calls/hr, 400K /day, 400 GB /month
We will be working with Tiingo
– the free account provides generous calls for our development
– $10 / month handles daily updates when fully implemented
– the API is simple and well documented
Tiingo API
The Tiingo API
Preliminary:
Tiingo requires users to register an account
– register for free account @ api.tiingo.com
– provide Username, E-mail, Password
– receive authentication key (API token)
The key looks something like:
“b2f9c56d8554dea1e733c1df0af9f4e0519617e3”
(don’t try to use this one – it is bogus, for example only)
In this discussion, we will refer to this key as MyToken
After reviewing the documentation, we see we can define some variables
to help with our documentation and discussion
Name | Description | Value |
Tiingo | the common URL of the API calls | https://api.tiingo.com/tiingo/daily |
ticker | a stock symbol | AAPL, GM, SP, etc. |
startDate | first data date | yyyy-mm-dd |
endDate | last data date | yyyy-mm-dd (optional) |
format | returned data format | ‘JSON’ (default) | ‘csv’ |
resampleFreq | data frequency | ‘daily’ (default) | ‘weekly’ | ‘monthly’ |
token | access key | MyToken |
So, what data is available?
# Meta Data https://api.tiingo.com/tiingo/daily/ticker?token=MyToken Tiingo/ticker?token=MyToken # Latest Price https://api.tiingo.com/tiingo/daily/ticker/prices?token=MyToken Tiingo/ticker/prices?token=MyToken # Historical Prices https://api.tiingo.com/tiingo/daily/ticker/prices ?startDate=2021-07-01&endDate=2021-07-02&token=MyToken Tiingo/ticker/prices ?startDate=startDate&endDate=endDate&token=MyToken
Examples (using ticker ‘GM’ – General Motors Company)
# Meta Data
(JSON) Tiingo/GM?token=MyToken {"endDate": "2021-07-06", "exchangeCode": "NYSE", "description": "General Motors is ...", "ticker": "GM", "name": "General Motors Company", "startDate": "2010-11-18"}
(CSV) Tiingo/GM?format=csv&token=MyToken ticker,name,exchangeCode,description,startDate,endDate GM,General Motors Company,NYSE,"General ...",2010-11-18,2021-07-08
Note: description is not interesting (blah, blah, blah) we want: ticker - vendor's internal name name for the ticker startDate - first available date from vendor endDate - last available date from vendor name - descriptive name, not just the ticker symbol exchangeCode - maybe. Useful if you are filtering by exchange
# Latest Price
(JSON) Tiingo/GM/prices?token=MyToken [{"adjClose":56.06,"adjHigh":56.585,"adjLow":54.86,"adjOpen":55.3, "adjVolume":14087371,"close":56.06, "date":"2021-07-08T00:00:00+00:00","divCash":0.0,"high":56.585, "low":54.86,"open":55.3,"splitFactor":1.0,"volume":14087371} ]
(CSV) Tiingo/GM/prices?format=csv&token=MyToken ,adjClose,adjHigh,adjLow,adjOpen,adjVolume,close,date,divCash,high,low,open,splitFactor,volume 0,56.06,56.585,54.86,55.3,14087371,56.06,2021-07-08T00:00:00+00:00,0.0,56.585,54.86,55.3,1.0,14087371
# Historical Prices
# general form Tiingo/ticker/prices ?startDate=startDate &endDate=endDate (optional = latest available) &format=format (JSON (default) | csv) &resampleFreq=resampleFreq (daily (default) | weekly | monthly) &token=MyToken
# daily price data – July 1 and 2, 2021
(JSON) Tiingo/GM/prices ?startDate=2021-07-01&endDate=2021-07-02 &token=MyToken [{"date":"2021-07-01T00:00:00.000Z", "close":59.11,"high":59.73,"low":58.775,"open":59.44,"volume":11752117, "adjClose":59.11,"adjHigh":59.73,"adjLow":58.775,"adjOpen":59.44,"adjVolume":11752117, "divCash":0.0,"splitFactor":1.0}, {"date":"2021-07-02T00:00:00.000Z", "close":58.96,"high":59.32,"low":58.52,"open":59.11,"volume":9699563, "adjClose":58.96,"adjHigh":59.32,"adjLow":58.52,"adjOpen":59.11,"adjVolume":9699563, "divCash":0.0,"splitFactor":1.0} ]
(CSV) Tiingo/GM/prices ?startDate=2021-07-01&endDate=2021-07-02 &format=csv &token=MyToken date,close,high,low,open,volume,adjClose,adjHigh,adjLow,adjOpen,adjVolume,divCash,splitFactor 2021-07-01,59.11,59.73,58.775,59.44,11752117,59.11,59.73,58.775,59.44,11752117,0.0,1.0 2021-07-02,58.96,59.32,58.52,59.11,9699563,58.96,59.32,58.52,59.11,9699563,0.0,1.0
API Responses
Tiingo API query response and status codes
Meta Data Requests
Tiingo/ticker?token=MyToken { &format=csv } Success( ticker 'GM' ) ==================================================== JSON statusCode = '200', statusText = 'OK' response = {"endDate": "2021-07-16", " exchangeCode": "NYSE", "description": "General Motors ...", "ticker": "GM", "name": "General Motors Company", "startDate": "2010-11-18"} CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: ticker,name,exchangeCode,description,startDate,endDate GM,General Motors Company,NYSE,"General Motors ...",2010-11-18,2021-07-16 <blank line> :::::<end>::::: ========================================================= Failure( bad ticker 'GMxxxxx' ) ========================================================= JSON statusCode = '404', statusText = 'Not Found' response = {"detail":"Not found."} CSV statusCode = '404', statusText = 'Not Found' response :::::<begin>::::: None :::::<end>::::: =========================================================
Latest Price
Tiingo/ticker/prices?token=MyToken { &format=csv } Success( ticker 'GM' ) ========================================================= JSON statusCode = '200', statusText = 'OK' response = [{"adjClose":55.46,"adjHigh":57.48, "adjLow":55.37,"adjOpen":57.48, "adjVolume":13611414,"close":55.46, "date":"2021-07-16T00:00:00+00:00", "divCash":0.0,"high":57.48,"low":55.37, "open":57.48,"splitFactor":1.0, "volume":13611414}] CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: ,adjClose,adjHigh,adjLow,adjOpen,adjVolume,close,date,divCash,high,low,open,splitFactor,volume 0,55.46,57.48,55.37,57.48,13611414,55.46,2021-07-16T00:00:00+00:00,0.0,57.48,55.37,57.48,1.0,13611414 <blank line> :::::::::: ========================================================= Failure( bad ticker 'GMxxxxx' ) ========================================================= JSON statusCode = '404', statusText = 'Not Found' response = {"detail":"Error: Ticker 'GMXXXXX' not found"} CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: Error: Ticker 'GMXXXXX' not found :::::<end>::::: =========================================================
Historical Prices
Tiingo/ticker/prices ?startDate=startDate &endDate=endDate *optional &token=MyToken ( &format=csv ) Success( ticker 'GM' ) ========================================================= JSON statusCode = '200', statusText = 'OK' response = [{"date":"2021-07-01T00:00:00.000Z", "close":59.11,"high":59.73,"low":58.775, "open":59.44,"volume":11752117, "adjClose":59.11,"adjHigh":59.73, "adjLow":58.775,"adjOpen":59.44, "adjVolume":11752117,"divCash":0.0, "splitFactor":1.0}, {"date":"2021-07-02T00:00:00.000Z", "close":58.96,"high":59.32,"low":58.52, "open":59.11,"volume":9699563, "adjClose":58.96,"adjHigh":59.32, "adjLow":58.52,"adjOpen":59.11, "adjVolume":9699563,"divCash":0.0, "splitFactor":1.0}] CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: date,close,high,low,open,volume,adjClose,adjHigh,adjLow,adjOpen,adjVolume,divCash,splitFactor 2021-07-01,59.11,59.73,58.775,59.44,11752117,59.11,59.73,58.775,59.44,11752117,0.0,1.0 2021-07-02,58.96,59.32,58.52,59.11,9699563,58.96,59.32,58.52,59.11,9699563,0.0,1.0 <blank line> :::::<end>::::: ========================================================= Failure( bad ticker 'GMxxxxx' ) ========================================================= JSON statusCode = '404', statusText = 'Not Found' response = {"detail":"Error: Ticker 'GMXXXXX' not found"} CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: Error: Ticker 'GMXXXXX' not found :::::<end>::::: ========================================================= Failure( bad start date '2021-13-01' ) ========================================================= JSON statusCode = '400', statusText = 'Bad Request' response = ["Error: Start date format was not correct. Must be in YYYY-MM-DD format."] CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: Error: Start date format was not correct. Must be in YYYY-MM-DD format. :::::<end>::::: ========================================================= Failure( bad end date '2021-07-0x' ) ========================================================= JSON statusCode = '400', statusText = 'Bad Request' response = ["Error: End date format was not correct. Must be in YYYY-MM-DD format."] CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: Error: End date format was not correct. Must be in YYYY-MM-DD format. :::::<end>::::: ========================================================= Failure( start date is after end date ) ========================================================= JSON statusCode = '200', statusText = 'OK' response = [] CSV statusCode = '200', statusText = 'OK' response :::::<begin>::::: date,close,high,low,open,volume,adjClose,adjHigh,adjLow,adjOpen,adjVolume,divCash,splitFactor <blank line> :::::<end>::::: =========================================================
Data Types
MetaData, Daily Prices, and Intraday Prices
Tiingo Client
A client interface to the Tiingo API
Binary vs SQL
Binary vs SQL data storage
Binary Store
Binary Store
SQL Store
SQL Store