Quickstart
In this section you'll learn how to get started with thetadata and make simple requests.
Installation
- Install Python 3.
- Install Java 11 or later. If you are using Windows, this will automatically be done for you. THIS IS REQUIRED TO USE THE API.
- Using pip, Python's package manager, open a terminal and run
pip install thetadata
orpython -m pip install thetadata
Free data
End of day stock data
With the API installed, you can now
import thetadata
in any Python script you'd like!
Here's how you may make your first free stock data request:
import pandas as pd
from datetime import date
from thetadata import ThetaClient, DateRange, StockReqType
def end_of_day() -> pd.DataFrame:
client = ThetaClient() # No credentials required for free access
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_hist_stock(
req=StockReqType.EOD, # End of day data
root="AAPL",
date_range=DateRange(date(2022, 11, 14), date(2022, 11, 18))
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = end_of_day()
print(data.to_string())
> python eod.py 1 ✘
DataType.OPEN DataType.HIGH DataType.LOW DataType.CLOSE DataType.VOLUME DataType.COUNT DataType.DATE
0 149.30 166.6500 135.3900 148.770 80889202 576303 2022-11-14
1 149.51 153.5900 140.9150 149.700 96309849 703679 2022-11-15
2 150.25 155.0800 138.9800 148.980 71463364 472255 2022-11-16
3 149.08 159.0588 140.2858 150.865 86759251 563732 2022-11-17
4 151.00 153.3400 134.8700 150.880 84146100 514111 2022-11-18
What am I looking at?
End of day option data
Here's how you may make your first free option data request:
import pandas as pd
from datetime import date
from thetadata import ThetaClient, OptionReqType, OptionRight, DateRange
def end_of_day() -> pd.DataFrame:
client = ThetaClient() # No credentials required for free access
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
# Make the request
out = client.get_hist_option(
req=OptionReqType.EOD, # End of day data
root="AAPL",
exp=date(2022, 12, 16),
strike=150,
right=OptionRight.CALL,
date_range=DateRange(date(2022, 11, 14), date(2022, 11, 18))
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = end_of_day()
print(data.to_string())
> python eod.py 1 ✘
DataType.OPEN DataType.HIGH DataType.LOW DataType.CLOSE DataType.VOLUME DataType.COUNT DataType.DATE
0 5.50 6.15 4.75 5.10 8454 1155 2022-11-14
1 6.40 8.10 5.45 5.95 14153 1476 2022-11-15
2 5.42 5.84 4.55 5.15 10848 957 2022-11-16
3 4.10 6.57 3.05 6.00 5820 1083 2022-11-17
4 6.95 6.95 5.57 6.11 5553 951 2022-11-18
What am I looking at?
List roots
A root can be defined as a unique identified for a stock / underlying asset. Common terms also include: stock symbol, ticker, and underlying.
Stock roots
This request returns a series of all roots (stock symbols) that are available.
import pandas as pd
from thetadata import ThetaClient, SecType
def get_roots() -> pd.Series:
client = ThetaClient() # No credentials required for free access
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_roots(sec=SecType.STOCK)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
roots = get_roots()
print(roots)
Roots with options
This request returns a series of all roots (stock symbols) that have traded options.
import pandas as pd
from thetadata import ThetaClient, SecType
def get_roots() -> pd.Series:
client = ThetaClient() # No credentials required for free access
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_roots(sec=SecType.OPTION)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
roots = get_roots()
print(roots)
Unlike historical data requests, get_roots
returns a Series. In the pandas package, a
Series
is a one-dimensional array, similar to a Python list.
Understanding the output
The return type of any historical data request is a DataFrame
, a core datatype
in the pandas package. To understand what the DataType
labels in the DataFrame
mean,
please refer to our request types page.
pandas is a fast, powerful, flexible and easy to use open source data analysis and manipulation tool, built on top of the Python programming language.
The official pandas user guide covers significantly more material than this tutorial. However, a key takeaway is that pandas operates on two data structures:
Series
DataFrame
A Series is a one-dimensional array, similar to a Python list. A DataFrame is a two-dimensional data structure with rows and columns, similar to a spreadsheet.
Manipulating a DataFrame
The above example returned a DataFrame
. Let's see how we can use it to process our data.
import pandas as pd
from thetadata import DataType
from .stocks.eod import end_of_day
def main():
# get the data from the previous EOD stock data example
data: pd.DataFrame = end_of_day()
# print all datatypes in the response
print(f"{data.columns}")
# get the first row in the DataFrame (our requested data)
row = data.iloc[0]
# print just the open price
open_price = row[DataType.OPEN]
print(f"{open_price=}")
if __name__ == "__main__":
main()
> python script.py
Index([ DataType.OPEN, DataType.HIGH, DataType.LOW, DataType.CLOSE,
DataType.VOLUME, DataType.COUNT, DataType.DATE],
dtype='object')
close_price=149.3
As you can see, the response contains 7 fields (DataFrame columns), and the close price of the first row of this data (which represents July 18, 2022) is $13.10.
Congratulations, you've processed your first request!
Manipulating a Series
The above stock root list example returned a Series
. Let's see how we can use it to
process our data.
import pandas as pd
from .stocks.list_roots import get_roots
def main():
# get the result from the previous example
roots: pd.Series = get_roots()
# Check if a symbol is in the Series
contains_apple = "AAPL" in roots.values
# Count symbols
count = len(roots)
# Filter symbols to those that start with A
filtered = [s for s in roots if s.startswith("A")]
for symbol in filtered:
# do something
pass
print(f"{contains_apple=}")
print(f"{count=}")
print(f"{len(filtered)=}")
if __name__ == "__main__":
main()
Snapshots
A snapshot is the last quote / trade we have on record. Snapshots are best used when executing a trade. This allows you to determine the most recent price.
15-minute delayed stock snapshots
Currently, all of our equities data is 15 minutes delayed. We have plans to upgrade to real-time equities feeds in the future. Below is an example of a stock snapshot:
import pandas as pd
from thetadata import ThetaClient, StockReqType
def get_last() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
# Request the most recent quote for AAPL stock.
out = client.get_last_stock(
req=StockReqType.QUOTE,
root="AAPL",
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
quote = get_last()
print(quote.to_string())
Real-time option snapshots
Let's try a low-latency snapshot request for the most recent option quote available:
import pandas as pd
from datetime import date
from thetadata import ThetaClient, OptionReqType, OptionRight
def get_last() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
# Request the most recent quote for an AAPL $130.00 CALL options expiring on 2022-12-30.
out = client.get_last_option(
req=OptionReqType.QUOTE,
root="AAPL",
exp=date(2022, 12, 30),
strike=130.00,
right=OptionRight.CALL,
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
quote = get_last()
print(quote.to_string())
Streaming
Preface
Definition
Streaming is defined as receiving continuous market data updates throughout the trading day. It is much more performant when compared to snapshots.
Responses
You must implement a callback method as shown in the first example below. Each time a
StreamMsg
is received, this method will get called. A StreamMsg
has a StreamMsgType
, which
can be used to determine the purpose of the message.
Limitations
The Pro tier has the ability to request a trade stream for every option contract in existence and is able to request up to 20K quote streams. The Standard tier can request 10K quote streams and 20K trade streams. Other tiers do not have access to streaming at this time.
Option Quotes
Below requests to receive continuous updates for quote for an AAPL option contract. Notice that these options are probably expired, so you may need to change the expiration date.
from datetime import date
from thetadata import ThetaClient, OptionRight, StreamMsg, StreamMsgType, StreamResponseType
def streaming():
# Credentials now required because streaming is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
# This contract is likely expired! Replace it with a contract that isn't expired
req_id = client.req_quote_stream_opt("AAPL", date(2023, 7, 21), 170, OptionRight.CALL)
# Verify that the request to stream was successful.
response = client.verify(req_id)
if response == StreamResponseType.SUBSCRIBED:
print('The request to stream option quotes was successful.')
elif response == StreamResponseType.INVALID_PERMS:
print('Invalid permissions to stream option quotes. Theta Data Options Standard or Pro account required.')
elif response == StreamResponseType.MAX_STREAMS_REACHED:
print('You have reached your limit for the amount of option contracts you can stream quotes for.')
else:
print('Unexpected stream response: ' + str(response))
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
msg.type = msg.type
if msg.type == StreamMsgType.QUOTE:
print('---------------------------------------------------------------------------')
print('con: ' + msg.contract.to_string())
print('quote: ' + msg.quote.to_string())
if __name__ == "__main__":
streaming()
> python trade_streaming.py
---------------------------------------------------------------------------
con: root: AAPL isOption: True exp: 2023-07-21 strike: 170.0 isCall: True
quote: ms_of_day: 57061764 bid_size: 202 bid_exchange: EDGX bid_price: 6.35 bid_condition: NATIONAL_BBO ask_size: 742 ask_exchange: C2OX ask_price: 6.45 ask_condition: NATIONAL_BBO date: 2023-04-21
---------------------------------------------------------------------------
con: root: AAPL isOption: True exp: 2023-07-21 strike: 170.0 isCall: True
quote: ms_of_day: 57061816 bid_size: 202 bid_exchange: EDGX bid_price: 6.35 bid_condition: NATIONAL_BBO ask_size: 742 ask_exchange: C2OX ask_price: 6.45 ask_condition: NATIONAL_BBO date: 2023-04-21
---------------------------------------------------------------------------
con: root: AAPL isOption: True exp: 2023-07-21 strike: 170.0 isCall: True
quote: ms_of_day: 57061974 bid_size: 202 bid_exchange: EDGX bid_price: 6.35 bid_condition: NATIONAL_BBO ask_size: 742 ask_exchange: C2OX ask_price: 6.45 ask_condition: NATIONAL_BBO date: 2023-04-21
---------------------------------------------------------------------------
con: root: AAPL isOption: True exp: 2023-07-21 strike: 170.0 isCall: True
quote: ms_of_day: 57061974 bid_size: 254 bid_exchange: C2OX bid_price: 6.35 bid_condition: NATIONAL_BBO ask_size: 742 ask_exchange: C2OX ask_price: 6.45 ask_condition: NATIONAL_BBO date: 2023-04-21
---------------------------------------------------------------------------
con: root: AAPL isOption: True exp: 2023-07-21 strike: 170.0 isCall: True
quote: ms_of_day: 57062025 bid_size: 322 bid_exchange: C2OX bid_price: 6.35 bid_condition: NATIONAL_BBO ask_size: 742 ask_exchange: C2OX ask_price: 6.45 ask_condition: NATIONAL_BBO date: 2023-04-21
---------------------------------------------------------------------------
Option Trades
Below requests to receive continuous updates for trades for a NVDA option contract. Notice that these options are probably expired, so you may need to change the expiration date.
from datetime import date
from thetadata import ThetaClient, OptionRight, StreamMsg, StreamMsgType, StreamResponseType
def streaming():
# Credentials now required because streaming is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
# This contract is likely expired! Replace it with a contract that isn't expired
req_id = client.req_trade_stream_opt("NVDA", date(2023, 1, 13), 150, OptionRight.CALL)
# Verify that the request to stream was successful.
response = client.verify(req_id)
if response == StreamResponseType.SUBSCRIBED:
print('The request to stream option trades was successful.')
elif response == StreamResponseType.INVALID_PERMS:
print('Invalid permissions to stream option trades. Theta Data Options Standard or Pro account required.')
elif response == StreamResponseType.MAX_STREAMS_REACHED:
print('You have reached your limit for the amount of option contracts you can stream trades for.')
else:
print('Unexpected stream response: ' + str(response))
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
msg.type = msg.type
if msg.type == StreamMsgType.TRADE:
print('---------------------------------------------------------------------------')
print('con: ' + msg.contract.to_string())
print('trade: ' + msg.trade.to_string())
print('last quote at time of trade: ' + msg.quote.to_string())
if __name__ == "__main__":
streaming()
> python trade_streaming.py
---------------------------------------------------------------------------
con: root: MSFT isOption: True exp: 2023-06-16 strike: 185.0 isCall: False
trade: ms_of_day: 54448612 sequence: 3464894509 size: 2 condition: AUTO_EXECUTION price: 4.95 exchange: date: 2022-12-27
last quote at time of trade: ms_of_day: 54448612 bid_size: 2 bid_exchange: bid_price: 4.95 bid_condition: NATIONAL_BBO ask_size: 27 ask_exchange: XBOS ask_price: 5.05 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: MSFT isOption: True exp: 2023-06-16 strike: 185.0 isCall: False
trade: ms_of_day: 54448614 sequence: 3464894620 size: 2 condition: INTERMARKET_SWEEP price: 4.95 exchange: date: 2022-12-27
last quote at time of trade: ms_of_day: 54448613 bid_size: 1178 bid_exchange: bid_price: 4.95 bid_condition: NATIONAL_BBO ask_size: 27 ask_exchange: XBOS ask_price: 5.05 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: UUP isOption: True exp: 2023-01-27 strike: 28.5 isCall: True
trade: ms_of_day: 46725445 sequence: 1876543798 size: 55 condition: AUTO_EXECUTION price: 0.23 exchange: date: 2022-12-27
last quote at time of trade: ms_of_day: 46725445 bid_size: 1 bid_exchange: XNMS bid_price: 0.23 bid_condition: NATIONAL_BBO ask_size: 55 ask_exchange: ask_price: 0.23 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: UNM isOption: True exp: 2023-03-17 strike: 42.5 isCall: True
trade: ms_of_day: 46725454 sequence: 1876544125 size: 1 condition: AUTO_EXECUTION price: 1.95 exchange: XNMS date: 2022-12-27
last quote at time of trade: ms_of_day: 46725454 bid_size: 33 bid_exchange: XBOS bid_price: 1.85 bid_condition: NATIONAL_BBO ask_size: 2 ask_exchange: XMIO ask_price: 2.0 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
Every Option Trade
Below requests to receive continuous updates for every option trade. This method is only available to PRO subscribers.
from thetadata import ThetaClient, StreamMsg, StreamMsgType, StreamResponseType
def streaming():
# Credentials now required because streaming is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
req_id = client.req_full_trade_stream_opt() # Requests every option trade (async).
# Verify that the request to stream was successful.
response = client.verify(req_id)
if response == StreamResponseType.SUBSCRIBED:
print('Request to stream full trades successful.')
elif response == StreamResponseType.INVALID_PERMS:
print('Invalid permissions to stream full trades. Theta Data Options Pro account required.')
else:
print('Unexpected stream response: ' + str(response))
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
msg.type = msg.type
if msg.type == StreamMsgType.TRADE:
print('---------------------------------------------------------------------------')
print('con: ' + msg.contract.to_string())
print('trade: ' + msg.trade.to_string())
print('last quote at time of trade: ' + msg.quote.to_string())
if __name__ == "__main__":
streaming()
> python trade_streaming_full.py
---------------------------------------------------------------------------
con: root: SPY isOption: True exp: 2023-01-03 strike: 387.0 isCall: True
trade: ms_of_day: 45506758 sequence: 2502262221 size: 23 condition: AUTO_EXECUTION price: 1.56 exchange: BATS date: 2022-12-27
last quote at time of trade: ms_of_day: 45506758 bid_size: 159 bid_exchange: C2OX bid_price: 1.55 bid_condition: NATIONAL_BBO ask_size: 422 ask_exchange: MCRY ask_price: 1.57 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: NEE isOption: True exp: 2023-03-17 strike: 82.5 isCall: False
trade: ms_of_day: 52558559 sequence: 3375727498 size: 1 condition: AUTO_EXECUTION price: 3.5 exchange: XCBO date: 2022-12-27
last quote at time of trade: ms_of_day: 52558559 bid_size: 1266 bid_exchange: XPHL bid_price: 3.4 bid_condition: NATIONAL_BBO ask_size: 270 ask_exchange: XPHL ask_price: 3.6 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: TSLA isOption: True exp: 2023-01-27 strike: 95.0 isCall: False
trade: ms_of_day: 46432515 sequence: 2064836636 size: 1 condition: AUTO_EXECUTION price: 5.15 exchange: ARCX date: 2022-12-27
last quote at time of trade: ms_of_day: 46432515 bid_size: 852 bid_exchange: EDGX bid_price: 5.1 bid_condition: NATIONAL_BBO ask_size: 1 ask_exchange: ARCX ask_price: 5.15 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
con: root: USO isOption: True exp: 2023-01-06 strike: 80.0 isCall: False
trade: ms_of_day: 46432592 sequence: 1863359037 size: 1 condition: SINGLE_LEG_AUCTION_NON_ISO price: 9.8 exchange: XMIO date: 2022-12-27
last quote at time of trade: ms_of_day: 46429598 bid_size: 39 bid_exchange: XBOS bid_price: 9.55 bid_condition: NATIONAL_BBO ask_size: 39 ask_exchange: XBOS ask_price: 10.05 ask_condition: NATIONAL_BBO date: 2022-12-27
---------------------------------------------------------------------------
Every Open Interest
Below requests to receive continuous updates for every option open interest. This method is only available to PRO subscribers. Open Interest is generally reported at 06:30 ET. It is generally not updated during the trading day.
from thetadata import ThetaClient, StreamMsg, StreamMsgType
def streaming():
# Credentials now required because get_last is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
client.req_full_open_interest_stream() # requests every option open interest update
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
msg.type = msg.type
if msg.type == StreamMsgType.OPEN_INTEREST:
print('con:' + msg.contract.to_string() + ' open_interest: ' + msg.open_interest.to_string())
if __name__ == "__main__":
streaming()
> python open_interest_streaming.py
con:root: ZROZ isOption: True exp: 2023-03-17 strike: 98.0 isCall: True open_interest: open_interest: 51 date: 2022-12-23
con:root: XOM isOption: True exp: 2025-01-17 strike: 97.5 isCall: False open_interest: open_interest: 130 date: 2022-12-23
con:root: XOP isOption: True exp: 2025-01-17 strike: 100.0 isCall: True open_interest: open_interest: 79 date: 2022-12-23
con:root: WSO isOption: True exp: 2023-05-19 strike: 370.0 isCall: False open_interest: open_interest: 0 date: 2022-12-23
con:root: XOM isOption: True exp: 2023-03-17 strike: 135.0 isCall: False open_interest: open_interest: 51 date: 2022-12-23
con:root: XOP isOption: True exp: 2025-01-17 strike: 210.0 isCall: True open_interest: open_interest: 15 date: 2022-12-23
con:root: XOP isOption: True exp: 2025-01-17 strike: 160.0 isCall: True open_interest: open_interest: 14 date: 2022-12-23
Cancelling Streams
You can cancel any stream you previously subscribed to by using the remove stream methods.
from datetime import date
from thetadata import ThetaClient, OptionRight, StreamMsg, StreamMsgType
def streaming():
# Credentials now required because get_last is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
# Subscribes to a stream
client.req_trade_stream_opt("NVDA", date(2023, 1, 13), 150, OptionRight.CALL)
# Unsubscribes from the stream
client.remove_trade_stream_opt("NVDA", date(2023, 1, 13), 150, OptionRight.CALL)
client.req_full_trade_stream_opt() # Subscribes to every option trade.
client.remove_full_trade_stream_opt() # Unsubscribes from the full option trade stream.
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
msg.type = msg.type
if msg.type == StreamMsgType.TRADE:
print('---------------------------------------------------------------------------')
print('con: ' + msg.contract.to_string())
print('trade: ' + msg.trade.to_string())
print('last quote at time of trade: ' + msg.quote.to_string())
if __name__ == "__main__":
streaming()
Handling a connection lost to server.
If the connection between the API and ThetaData servers is lost, there is a DISCONNECTED
callback.
Once the reconnection has been reestablished, there is a RECONNECTED
callback.
from datetime import date
from thetadata import ThetaClient, OptionRight, StreamMsg, StreamMsgType
def streaming():
# Credentials now required because get_last is only available to ThetaData Standard & Pro subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
client.connect_stream(callback) # You can stop streaming by calling client.close_stream
client.req_full_trade_stream_opt() # Subscribes to every option trade.
# User generated method that gets called each time a message from the stream arrives.
def callback(msg: StreamMsg):
if msg.type == StreamMsgType.DISCONNECTED:
print('Lost connection to Theta Data servers')
if msg.type == StreamMsgType.STREAM_DEAD:
print('Lost connection to Theta Terminal, which is likely due the terminal being forcibly closed')
if msg.type == StreamMsgType.RECONNECTED:
print('The terminal has reconnected to Theta Data. You need to resubscribe to all streams.')
if __name__ == "__main__":
streaming()
Historical NBBO Quotes
This tutorial will provide examples for requesting historical & intraday NBBO(National Best Bid & Offer) quotes.
Preface
The interval_size
parameter
We provide tick-level data for QUOTE
requests. This means that when a request is made,
every single quote inside thedate_range
provided will be returned. This can end up being
millions of quotes per trading day. Specifying theinterval_size
parameter aggregates these
quotes and provides the NBBO at every interval_size
milliseconds.
Be careful!
Not specifying the interval_size
will return tick-level data, which can be an obscene
amount of data! If you plan to work with tick-level quote data, we recommend limiting
your date_range
(per-request) to 2 weeks for options and 1 day for stocks to prevent
memory overflow errors. If you are using an interval_size
>= 60000
(1-min),
you shouldn't need to do this.
Why are there quotes with no bid or ask?
You'll notice that the first few quotes of the day may have empty bid and or ask and prices. These are test quotes usually sent by exchanges or SIPs. It's safe to ignore them. We maintain a policy of not filtering any data unless the user asks us to, which is why they are included in tick-level requests.
Stock Quotes
Tick-level NBBO quotes
The example below returns every NBBO quote that occurred on 2022-11-18
for AMD
stock.
As seen in the output, there are over 5.8 million quotes for this day alone, which is
why we recommend using a date_range
of 1 for these requests.
import pandas as pd
from datetime import date
from thetadata import ThetaClient, DateRange, StockReqType
def quote() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_hist_stock(
req=StockReqType.QUOTE,
root="AMD",
date_range=DateRange(date(2022, 11, 18), date(2022, 11, 18)),
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = quote()
print(data.to_string(max_rows=8, max_cols=16))
> python docs/stocks/quote_tick.py
DataType.MS_OF_DAY DataType.BID_SIZE DataType.BID_CONDITION DataType.BID DataType.BID_EXCHANGE DataType.ASK_SIZE DataType.ASK_CONDITION DataType.ASK DataType.ASK_EXCHANGE DataType.DATE
0 14340012 0 0 0.00 0 0 0 0.00 0 2022-11-18
1 14340126 0 0 0.00 0 0 0 0.00 0 2022-11-18
2 14400005 4 1 74.11 0 0 0 0.00 0 2022-11-18
3 14400005 4 1 74.11 0 4 1 74.68 0 2022-11-18
... ... ... ... ... ... ... ... ... ... ...
5894898 71998825 8 65 73.58 0 1 7 73.58 0 2022-11-18
5894899 71998826 8 65 73.58 0 6 7 73.60 0 2022-11-18
5894900 71998827 8 65 73.58 0 6 7 73.60 0 2022-11-18
5894901 71998831 8 65 73.59 0 6 7 73.60 0 2022-11-18
1-minute intervals NBBO quotes
The only difference from the example above is that we specify the interval_size
parameter, which allows us to get the NBBO quote every minute of the trading day.
This significantly reduces output and improves latency.
import pandas as pd
from datetime import date
from thetadata import ThetaClient, DateRange, StockReqType
def quote() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_hist_stock(
req=StockReqType.QUOTE,
root="AMD",
date_range=DateRange(date(2022, 11, 14), date(2022, 11, 18)),
interval_size=60_000 # 60_000ms = 1 minute
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = quote()
print(data.to_string(max_rows=8, max_cols=16))
> python docs/stocks/quote_1min.py
DataType.MS_OF_DAY DataType.BID_SIZE DataType.BID_CONDITION DataType.BID DataType.BID_EXCHANGE DataType.ASK_SIZE DataType.ASK_CONDITION DataType.ASK DataType.ASK_EXCHANGE DataType.DATE
0 34200000 2 65 75.23 0 138 1 75.24 0 2022-11-14
1 34260000 1 1 74.72 0 1 1 74.77 0 2022-11-14
2 34320000 1 65 74.58 0 3 7 74.60 0 2022-11-14
3 34380000 2 17 73.70 0 3 7 73.71 0 2022-11-14
... ... ... ... ... ... ... ... ... ... ...
1951 57420000 5 1 73.50 0 6 63 73.51 0 2022-11-18
1952 57480000 5 3 73.48 0 12 3 73.49 0 2022-11-18
1953 57540000 7 3 73.53 0 11 3 73.54 0 2022-11-18
1954 57600000 209 1 73.58 0 314 1 73.61 0 2022-11-18
Historical Option Quotes
Tick-level NBBO quotes
The example below returns every NBBO quote that occurred between 2022-11-14
and
2022-11-18
for the $60 AMD CALL
that expires on 2022-12-16
. As seen in the
output, there are almost 1 million quotes for this week alone, which is why we
recommend using a date_range
of 2 weeks or fewer for tick-level option requests.
import pandas as pd
from datetime import date
from thetadata import ThetaClient, OptionRight, DateRange, OptionReqType
def quote() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_hist_option(
req=OptionReqType.QUOTE,
root="AMD",
exp=date(2022, 12, 16),
strike=60,
right=OptionRight.CALL,
date_range=DateRange(date(2022, 11, 14), date(2022, 11, 18)),
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = quote()
print(data.to_string(max_rows=8, max_cols=16))
> python docs/stocks/quote_1min.py
DataType.MS_OF_DAY DataType.BID_SIZE DataType.BID_CONDITION DataType.BID DataType.BID_EXCHANGE DataType.ASK_SIZE DataType.ASK_CONDITION DataType.ASK DataType.ASK_EXCHANGE DataType.DATE
0 28800107 0 65 0.00 50 0 65 0.00 50 2022-11-14
1 28801023 0 5 0.00 50 0 5 0.00 50 2022-11-14
2 28806116 0 42 0.00 50 0 42 0.00 50 2022-11-14
3 28806324 0 60 0.00 50 0 60 0.00 50 2022-11-14
... ... ... ... ... ... ... ... ... ... ...
979528 57598756 1183 9 14.15 50 65 11 14.45 50 2022-11-18
979529 57599253 1132 9 14.15 50 65 11 14.45 50 2022-11-18
979530 57599264 1118 9 14.15 50 65 11 14.45 50 2022-11-18
979531 57599539 1112 9 14.15 50 65 11 14.45 50 2022-11-18
1-minute intervals NBBO quotes
The only difference from the example above is that we specify the interval_size
parameter, which allows us to get the NBBO quote every minute of the trading day.
This significantly reduces output and improves latency.
import pandas as pd
from datetime import date
from thetadata import ThetaClient, OptionRight, DateRange, OptionReqType
def quote() -> pd.DataFrame:
# Credentials now required because get_last is only available to ThetaData subscribers.
client = ThetaClient(username="MyThetaDataEmail", passwd="MyThetaDataPassword")
# Make any requests for data inside this block. Requests made outside this block won't run.
with client.connect():
out = client.get_hist_option(
req=OptionReqType.QUOTE,
root="AMD",
exp=date(2022, 12, 16),
strike=60,
right=OptionRight.CALL,
date_range=DateRange(date(2022, 11, 14), date(2022, 11, 18)),
interval_size=60_000 # 60_000ms = 1 minute
)
# We are out of the client.connect() block, so we can no longer make requests.
return out
if __name__ == "__main__":
data = quote()
print(data.to_string(max_rows=8, max_cols=16))
> python docs/options/quote_1min.py
DataType.MS_OF_DAY DataType.BID_SIZE DataType.BID_CONDITION DataType.BID DataType.BID_EXCHANGE DataType.ASK_SIZE DataType.ASK_CONDITION DataType.ASK DataType.ASK_EXCHANGE DataType.DATE
0 34200000 0 47 0.00 50 0 47 0.00 50 2022-11-14
1 34260000 1081 9 15.40 50 188 65 16.15 50 2022-11-14
2 34320000 151 6 15.35 50 78 11 15.85 50 2022-11-14
3 34380000 40 43 14.75 50 138 43 15.10 50 2022-11-14
... ... ... ... ... ... ... ... ... ... ...
1946 57360000 2021 9 14.15 50 77 43 14.35 50 2022-11-18
1947 57420000 2056 9 14.15 50 39 47 14.35 50 2022-11-18
1948 57480000 2123 9 14.15 50 63 11 14.35 50 2022-11-18
1949 57540000 1887 9 14.15 50 87 11 14.40 50 2022-11-18
What's next?
Nice! You are now familiar the Series
and DataFrame
objects, the return types of our data requests.
Using the skills learned in this tutorial, you're well on your way to mastering the entire thetadata
package.
Explore other requests at your disposal on the API Reference page!
For more details on the pandas
API, please see the official pandas user guide.