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 and not by Interactive Brokers does NOT constitute a recommendation by Interactive Brokers 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 permission from Robot Wealth. The views expressed in this material are solely those of the author and/or Robot Wealth and IBKR 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 sell or the solicitation of an offer to buy any security. To the extent that this material discusses general market activity, industry or sector trends or other broad based economic or political conditions, it should not be construed as research or investment advice. To the extent that it includes references to specific securities, commodities, currencies, or other instruments, those references do not constitute a recommendation to buy, sell or hold such security. 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.
In accordance with EU regulation: The statements in this document shall not be considered as an objective or independent explanation of the matters. Please note that this document (a) has not been prepared in accordance with legal requirements designed to promote the independence of investment research, and (b) is not subject to any prohibition on dealing ahead of the dissemination or publication of investment research.
Any trading symbols displayed are for illustrative purposes only and are not intended to portray recommendations.
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: Digital Assets
Trading in digital assets, including cryptocurrencies, is especially risky and is only for individuals with a high risk tolerance and the financial ability to sustain losses. Eligibility to trade in digital asset products may vary based on jurisdiction.