Stock History data

Hi all, still figuring stuff out. I’m trying to get historical data on ‘SPY’, I just wanted to test it going back a minimal amount of time. Truthfully the API docs seem very incomplete, there wasn’t a clear example of how to do this…

Anyway, this is how I’m trying to do it:

x = datetime(2023, 1, 19)

client = StockHistoricalDataClient(apiid, apikey)

request_params = StockQuotesRequest(
    symbol_or_symbols=['SPY'],
    timeframe=TimeFrame.Day,
    start=x
    )

prices = client.get_stock_quotes(request_params)

I’ve also tried start="2023-01-19" but that failed completely. The above code block just runs forever.

Anyone know what I’m doing wrong?

@redcoatwright Agreed the SDKs could use better documentations and examples. But I can get you started. First, it seems since you are including a TimeFrame, you are wanting ‘bar’ data (ie open, close, high. low, volume) and not quotes? Therefore I’ll show both bars and quotes and start with bars.

The timeframes should really be in timezone aware datetimes. Sometimes the SDK complains but if not, it just makes it easier to always work in market, or America/New_York time. The biggest issue with working in UTC is daylight saving time. Sometimes market time is offset -4:00 and other times it’s -5:00. Always converting to market time avoids this issue. All data responses are in UTC even if fetched in market time. Therefore, after each fetch it’s best to convert again to market time.

I personally like using the pandas to_datetime method rather than the basic python datetime methods. It handles timezones a bit nicer. I also would suggest working with data as pandas dataframes. The SDK has a convenient method df which converts everything into an easy to use dataframe. This example therefore uses that approach.

!pip install alpaca-py

import pandas as pd

from alpaca.data import StockHistoricalDataClient, TimeFrame
from alpaca.data.requests import StockQuotesRequest, StockBarsRequest

ALPACA_API_KEY_ID = 'xxxxxxx'
ALPACA_API_SECRET_KEY = 'xxxxxxx'

# Instantiate a data client
data_client = StockHistoricalDataClient(ALPACA_API_KEY_ID, ALPACA_API_SECRET_KEY)

# Set the start time
start_time = pd.to_datetime("2023-01-19").tz_localize('America/New_York')

# It's generally best to explicitly provide an end time but will default to 'now' if not
request_params = StockBarsRequest(
    symbol_or_symbols=['SPY'],
    timeframe=TimeFrame.Day,
    start=start_time
    )

bars_df = data_client.get_stock_bars(request_params).df.tz_convert('America/New_York', level=1)

This results in the following dataframe

Getting quotes is very similar. There are generally A LOT of quotes so it’s best to explicitly specify the start and end times to a reasonable timeframe otherwise it can take several minutes to fetch. The code below assumes the same imports and data_client as above.

start_time = pd.to_datetime("2023-01-19 09:30:00").tz_localize('America/New_York')
end_time = pd.to_datetime("2023-01-19 09:31:00").tz_localize('America/New_York')

request_params = StockQuotesRequest(
    symbol_or_symbols='SPY',
    start=start_time,
    end=end_time,
    )

quote_df = data_client.get_stock_quotes(request_params).df.tz_convert('America/New_York', level=1)

This results in a dataframe of over 20,000 rows (for just a minutes worth of quotes). Below are the first few rows.

Hope that helps and is enough to get you started.

2 Likes

I would like to look at the OHLC values for the last one minute candle (during market or premarket or aftermarket) in addition to bid/ask to determine where I want to place a limit order. How can I get the last 1 minute historical data? I do not want to request for 20,000 rows.

Perhaps, there are better ways of picking a limit price. If so, please advise.

Thanks.
RC

@RiskCapital To get the latest minute bar you can use the latest bar endpoint. Check out the docs here. Using the python alpaca-trade-api SDK …

api.get_latest_bars([‘IBM’, ‘SPY’])

{
‘SPY’: BarV2({ ‘c’: 399.58,
‘h’: 399.63,
‘l’: 399.58,
‘n’: 20,
‘o’: 399.63,
‘t’: ‘2023-01-24T12:30:00Z’,
‘v’: 586,
‘vw’: 399.60942}),

‘IBM’: BarV2({ ‘c’: 141.9,
‘h’: 141.9,
‘l’: 141.9,
‘n’: 1,
‘o’: 141.9,
‘t’: ‘2023-01-24T11:34:00Z’,
‘v’: 100,
‘vw’: 141.9})
}

Note the latest bar may not be the current minute. As in the example above, the latest bar for SPY was from 12:30:00 while the latest bar for IBM was 11:34:00. Bars are only created if there are valid trades. No trades no bars.

You may want to look at using the snapshot endpoint too. That returns the latest minute bar (as above) but also includes the latest quote which can be helpful in setting your prices. See here.

2 Likes

Thank you. I tried what you suggested.

api.get_latest_bars([‘SOXL’])
Out[24]:
{‘SOXL’: BarV2({ ‘c’: 14.6,
‘h’: 14.6,
‘l’: 14.6,
‘n’: 1,
‘o’: 14.6,
‘t’: ‘2023-01-24T21:23:00Z’,
‘v’: 2000,
‘vw’: 14.6})}

SOXL closed today at 14.44 at the end of regular market hours. In the after hours, it last traded at 13.98. I was expecting the bars data to give me 13.98.


Next, I tried getting the latest bid/ask prices to see if that will get me to 13.98.

from alpaca.data.requests import StockLatestQuoteRequest
from alpaca.data.historical import StockHistoricalDataClient
client = StockHistoricalDataClient(alpkey, alpsec)

def get_latest_bid_ask_for_symbol(symbol):

# multi symbol request - single symbol is similar
multisymbol_request_params = StockLatestQuoteRequest(symbol_or_symbols=[symbol])

latest_multisymbol_quotes = client.get_stock_latest_quote(multisymbol_request_params)

latest_ask_price = latest_multisymbol_quotes[symbol].ask_price
latest_bid_price = latest_multisymbol_quotes[symbol].bid_price   

return [latest_bid_price,latest_ask_price]

symbol = ‘SOXL’
[bid, ask] = get_latest_bid_ask_for_symbol(symbol)

print(f’bid and ask prices of {symbol} are {bid, ask}')

From this code, I get:
bid and ask prices of SOXL are (14.6, 14.62)


Next, I tried the snapshots endpoint as you suggested.

import requests

url = “https://data.alpaca.markets/v2/stocks/snapshots
params = {“symbols”: symbol}
headers = {
“APCA-API-KEY-ID”: alpkey,
“APCA-API-SECRET-KEY”: alpsec
}

response = requests.get(url, params=params, headers=headers)

if response.status_code == 200:
data = response.json()
print(data)
else:
print(“Error:”, response.status_code)


I get this output:

{‘SOXL’: {‘latestTrade’: {‘t’: ‘2023-01-24T21:23:12.41730688Z’,
‘x’: ‘V’,
‘p’: 14.6,
‘s’: 2000,
‘c’: [’ ', ‘T’],
‘i’: 57459102012777,
‘z’: ‘B’},
‘latestQuote’: {‘t’: ‘2023-01-24T21:35:00.086067712Z’,
‘ax’: ‘V’,
‘ap’: 14.62,
‘as’: 3,
‘bx’: ‘V’,
‘bp’: 14.6,
‘bs’: 10,
‘c’: [‘R’],
‘z’: ‘B’},
‘minuteBar’: {‘t’: ‘2023-01-24T21:23:00Z’,
‘o’: 14.6,
‘h’: 14.6,
‘l’: 14.6,
‘c’: 14.6,
‘v’: 2000,
‘n’: 1,
‘vw’: 14.6},
‘dailyBar’: {‘t’: ‘2023-01-24T05:00:00Z’,
‘o’: 14.3,
‘h’: 14.845,
‘l’: 14.18,
‘c’: 14.42,
‘v’: 454438,
‘n’: 2439,
‘vw’: 14.402522},
‘prevDailyBar’: {‘t’: ‘2023-01-23T05:00:00Z’,
‘o’: 13.19,
‘h’: 14.9,
‘l’: 13.13,
‘c’: 14.77,
‘v’: 786856,
‘n’: 3252,
‘vw’: 14.109428}}}

once again, not what I expected, but consistent with previous results.

Thanks.

Thank you, this is very comprehensive!