Excerpt
The changing face of market data providers
Over the last few years, a number of new market data providers have come online. They tend to have modern websites, broad coverage, and well-documented RESTful APIs. Their services are often priced very competitively – especially for personal use – and usually have generous free tiers.
One such newcomer is finnhub.io (https://robotwealth.com/finnhub-api/finnhub.io) Its offering includes stock, bond, crpto, and FX historical price data and real time trades and quotes. Their fundamental data offering is noticably broad and includes current values and point-in-time snapshots for numerous metrics. There are also some interesting alternative data sets including measures of social media sentiment, insider transactions and insider sentiment, senate lobbying, government spending, and others. In addition, there’s a real-time newsfeed delivered over websockets.
The free tier is quite generous and offers more than enough for doing proof of concept work and testing ideas. While you only get a year’s worth of historical data per API call on the free tier (more if you specify a lower resolution, like monthly), you can make up to 50 calls per minute.
In this post, we’ll explore the finnhub.io free tier via its REST API.
Get started
A nice python library for working with the finnhub.io is available: pip install finnhub-python
To access the API, you’ll need an API key. Get one here.
To get started, import the libraries we need and set up a finnhub.Client
with your API key:
import finnhub import os import time import datetime from zoneinfo import ZoneInfo import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt # Set up client # Note the FINNHUB_KEY environment variable stores my API key finnhub_client = finnhub.Client(api_key=os.environ['FINNHUB_KEY'])
OHLCV stock prices
The stock prices endpoint requires a symbol, a resolution (‘D’ for daily data), and a date range consisting of ‘from’ and ‘to’ values as UNIX timestamps.
We’ll get a week’s worth of AAPL data from February 2023.
# helper function for making UNIX timestamps def unix_timestamp_from_date(date, format='%Y-%m-%d'): '''Transform human readable date string to UNIX timestamp''' return int( datetime.datetime.strptime(date, format) .replace(tzinfo=ZoneInfo('US/Eastern')) .timestamp() ) # api query paramters symbol = 'AAPL' resolution = 'D' from_date = unix_timestamp_from_date('2023-02-06') to_date = unix_timestamp_from_date('2023-02-10') # make request and print res = finnhub_client.stock_candles( symbol, resolution, from_date, to_date ) display(res)
{'c': [151.73, 154.65, 151.92, 150.87, 151.01], 'h': [153.1, 155.23, 154.58, 154.33, 151.3401], 'l': [150.78, 150.64, 151.168, 150.42, 149.22], 'o': [152.575, 150.64, 153.88, 153.775, 149.46], 's': 'ok', 't': [1675641600, 1675728000, 1675814400, 1675900800, 1675987200], 'v': [69858306, 83322551, 64120079, 56007143, 57450708]}
The data comes down as a dictionary of lists. The docs state that prices are adjusted for splits. Spot checking some data against other sources suggests it’s also adjusted for dividends.
The dictionary keys consist of letters that represent:
c
: close pricesh
: high pricesl
: low priceso
: open pricess
: statust
: timestampsv
: volume traded
We’ll want to transform that response data into a pandas DataFrame
:
# Ditch the status code try: res.pop('s') except KeyError as e: print("Already ditched status code") # Create dataframe from remaining records df = pd.DataFrame(res) \ .rename(columns={ 'c': 'close', 'h': 'high', 'l': 'low', 'o': 'open', 't': 'timestamp', 'v': 'volume' }) \ .set_index(keys = 'timestamp') # I like things in a certain order df = df[['open', 'high', 'low', 'close', 'volume']] # Convert index to human-readable date format df.index = pd.to_datetime(df.index, unit='s') display(df)
open | high | low | close | volume | |
timestamp | |||||
2023-02-06 | 152.575 | 153.1000 | 150.780 | 151.73 | 69858306 |
2023-02-07 | 150.640 | 155.2300 | 150.640 | 154.65 | 83322551 |
2023-02-08 | 153.880 | 154.5800 | 151.168 | 151.92 | 64120079 |
2023-02-09 | 153.775 | 154.3300 | 150.420 | 150.87 | 56007143 |
2023-02-10 | 149.460 | 151.3401 | 149.220 | 151.01 | 57450708 |
Looks good!
Fundamental data
The basic company financials endpoint serves quite a lot of data. The response object is a dictionary of dictionaries that includes current values for various metrics (under the metric
outer key) as well as historical point-in-time snapshots (under the series
outer key).
# Get basic company financials financials = finnhub_client.company_basic_financials(symbol, 'all') # Outer keys of response object display(financials.keys())
dict_keys(['metric', 'metricType', 'series', 'symbol'])
Originally posted on Robot Wealth Blog. Visit Robot Wealth to read the full article.
Disclosure: Interactive Brokers
Information posted on IBKR Campus that is provided by third-parties does NOT constitute a recommendation that you should contract for the services of that third party. Third-party participants who contribute to IBKR Campus are independent of Interactive Brokers and Interactive Brokers does not make any representations or warranties concerning the services offered, their past or future performance, or the accuracy of the information provided by the third party. Past performance is no guarantee of future results.
This material is from Robot Wealth and is being posted with its permission. The views expressed in this material are solely those of the author and/or Robot Wealth and Interactive Brokers is not endorsing or recommending any investment or trading discussed in the material. This material is not and should not be construed as an offer to buy or sell any security. It should not be construed as research or investment advice or a recommendation to buy, sell or hold any security or commodity. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.
Disclosure: API Examples Discussed
Throughout the lesson, please keep in mind that the examples discussed are purely for technical demonstration purposes, and do not constitute trading advice. Also, it is important to remember that placing trades in a paper account is recommended before any live trading.
Disclosure: Forex
There is a substantial risk of loss in foreign exchange trading. The settlement date of foreign exchange trades can vary due to time zone differences and bank holidays. When trading across foreign exchange markets, this may necessitate borrowing funds to settle foreign exchange trades. The interest rate on borrowed funds must be considered when computing the cost of trades across multiple markets.
Disclosure: Bitcoin Futures
TRADING IN BITCOIN FUTURES IS ESPECIALLY RISKY AND IS ONLY FOR CLIENTS WITH A HIGH RISK TOLERANCE AND THE FINANCIAL ABILITY TO SUSTAIN LOSSES. More information about the risk of trading Bitcoin products can be found on the IBKR website. If you're new to bitcoin, or futures in general, see Introduction to Bitcoin Futures.