Visualizing Prediction Markets with Python and Kalshi¶
In this blog post we’ll explore Prediction Markets using Kalshi.
What is Kalshi?¶
Kalshi is a predictions market, regulated by the Commodity Futures Trading Commission (CFTC) where you can buy event contracts based on future outcomes for certain events, such as the number of COVID cases in the future, or the total number of delays and cancellations at a particular airport.
Don’t wait, download now and transform your career!Your FREE Guide to Become a Data Scientist
What are event contracts?¶
Event contracts are a new financial instrument that allow people to trade directly on the outcome of events. Contracts are priced from \$0.01 to \\$0.99 and represent the probability you think the event will occur. Ultimately, each contract pays out \$1 if the event happens, and $0 if not.
Why are event contracts useful?¶
While at first glance event contracts may seem like a strange extension of gambling, I would argue that they are actually great vehicles for hedging and insurance.
Often market participants have strong conviction about a future about the probabliity of a future event, for example, the probability that student debt will be forgiven in the United States. Previously it was very difficult to find a set of related trades to execute on that conviction, for example, should you short companies dealing with student debt refinancing if you think there will be student debt forgiveness? What if these companies end up being contractors for the US Government as part of student debt forgiveness? That could kill your trade, even if your original prediction was correct. Prediction markets like Kalshi let you trade on the direct outcome of a particular event, meaning you would no longer need to perform complex derivative trades hoping that they are correlated to your original thesis.
More importantly, prediction markets can serve as a form of insurance against a particular event. Take for example the current Weather markets on Kalshi. If you were a restaurant owner or events manager, you may be seeking insurance against inclement weather. It may be difficult to find an insurance provider who would directly insure against such a set of events. Instead you could create your own protection by utilizing prediction markets for weather, giving you a hedge against weather events that would otherwise be difficult to insure against or have expensive premiums and terms. There are even prediction markets for flight delays!
Using Python with Kalshi!¶
Let’s get started exploring some data with Kalshi using Python:
Using Kalshi’s API¶
Kalshi has an official API which can be found here along with some starter code that can be found here: https://kalshi-public-docs.s3.amazonaws.com/KalshiAPI.html
The starter code we will use can be downloaded from here: https://kalshi-public-docs.s3.amazonaws.com/KalshiAPIStarterCode.zip
Let’s start with some imports:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from KalshiClientsBase import ExchangeClient
Interested in using Python for Finance?¶
Check out our offerings for training using Python and Finance!
You can use your own config file for this, or just manually use inputs as we did below:
from getpass import getpass
email = getpass("Email: ")
password = getpass("Password: ")
Email: ········ Password: ········
exchange_api_base = "https://trading-api.kalshi.com"
Confirm it worked with the code below upon connection:
exchange_client = ExchangeClient(exchange_api_base, email, password)
print(exchange_client.user_id)
Public Markets Information¶
Take a look at the KalshiClientsBase object to understand the methods available to us.
help(exchange_client)
Help on ExchangeClient in module KalshiClientsBase object: class ExchangeClient(KalshiClient) | ExchangeClient(exchange_api_base: str, username: str, password: str) | | Method resolution order: | ExchangeClient | KalshiClient | builtins.object | | Methods defined here: | | __init__(self, exchange_api_base: str, username: str, password: str) | Initializes the client and logs in the specified user. | | Raises an HttpError if the user could not be authenticated. | | get_market(self, market_id: str) | | get_market_url(self, market_id: str) -> str | | get_orderbook(self, market_id: str) | | get_public_markets(self) | | ---------------------------------------------------------------------- | Methods inherited from KalshiClient: | | get(self, path: str, params: Dict[str, Any] = {}) -> Any | GETs from an authenticated Kalshi HTTP endpoint. | | Returns the response body. Raises an HttpError on non-2XX results. | | post(self, path: str, body: dict) -> Any | POSTs to an authenticated Kalshi HTTP endpoint. | | Returns the response body. Raises an HttpError on non-2XX results. | | raise_if_bad_response(self, response: requests.models.Response) -> None | | rate_limit(self) -> None | | request_headers(self) -> Dict[str, Any] | | ---------------------------------------------------------------------- | Data descriptors inherited from KalshiClient: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)
Examining the S&P 500 future value prediction market¶
Let’s explore all the markets concerning the closing price of the S&P 500 index. Note that there are a few factors to consider on the data:
- The closing data
- The multiple ranges
- The probability for each range
Let’s take a look at a prediction market for one date: https://kalshi.com/events/INXW-22AUG19/markets/INXW-22AUG19-B4275
Notice it comes with a unique starter ticker code, where the last portion is for the individual range outcome.
all_22Aug19_sp500_markets = []
for market in all_public_markets['markets']:
if 'INXW-22AUG19' in market['ticker_name']:
all_22Aug19_sp500_markets.append(market['id'])
all_22Aug19_sp500_markets
['d7008c8f-af6e-44b0-b3bd-e740900e8280', '1d79bfa5-22a4-47a8-ab87-7ff697741fcd', '7e2c19b0-7168-49c9-b242-0f3cd90766ba', '26bb1335-9c89-458d-9b76-ac1c86a2570e', 'ce2dfba7-390c-4264-96a0-7c417f6e7796', '0d503fd4-5349-4bcf-9bf3-6f73f6d64d1c', '37ea7474-4b8e-4255-a886-4153e169ef34', '709c9514-91dc-4fc5-9665-2ea4d1f6554a', 'fb846005-c64d-46cf-9b97-b8f379c30b35', '7f15e0f2-e75f-4322-90d9-af10344743d5', 'c79901ba-2169-434d-81e7-c05aa8364b58', '7efa34c3-49d8-4ab1-9c60-8321e9c7130f', 'af9a84e8-8a9f-49e0-a53a-d17be3a6c98c', '229d67b1-7ba9-4a4a-8419-3f1f023ddb49', '784c41af-7703-43dd-9fd1-9744df26aa69']
We see a market_id for each range outcome possibiliy. Let’s explore one of these ranges:
sp500_example = exchange_client.get_market('fb846005-c64d-46cf-9b97-b8f379c30b35')
sp500_example['market']['yes_bid']
25
Because Yes/No prices reflect a dollar return, we can directly treat these prices as a liklihood prediction of the outcome. Keep in mind, you only get a dollar return upon the closing of the underlying event, which means you can technically sell the contract at anytime (for some markets) in between now and the closing, this means that the Yes/No prices won’t add up to exactly 1 dollar (although they should typically be relevatively close). You can also view the order book for each event to explore it further.
# Create lists for future histplot of probable outcomes
bins = []
prob_data = []
# Grab data
for market_id in all_22Aug19_sp500_markets:
# Get Market Object
marketobj = exchange_client.get_market(market_id)
# Calculate Probability of Outcome based off current bids
yes_bid = marketobj['market']['yes_bid']
yes_ask = marketobj['market']['yes_ask']
est_prob = (yes_ask+yes_bid)/2
# Add as probability data point:
prob_data.append(est_prob)
# Grab Info needed to build bar plot range
bin_value = marketobj['market']['sub_title']
bins.append(bin_value)
bins
['<3950', '3950-3999.99', '4000-4049.99', '4050-4099.99', '4100-4149.99', '4150-4199.99', '4200-4249.99', '4250-4299.99', '4300-4349.99', '4350-4399.99', '4400-4449.99', '4450-4499.99', '4500-4549.99', '4550-4600', '>4600']
prob_data
[1.0, 1.0, 1.0, 1.0, 2.5, 6.0, 16.0, 26.5, 27.0, 13.5, 5.5, 1.5, 1.0, 1.0, 1.0]
Again, note how due to our estimation between the bid and ask price (spread) as our probability, the sum may not be exactly 100% (but we should expect it to be relatively close, the larger the difference, the more variance in the current prediction market).
Coloring by Probability¶
Since we’re just using a barplot due to the bins, let’s quickly use a function to get coloring correct: https://stackoverflow.com/questions/36271302/changing-color-scale-in-seaborn-bar-plot
def colors_from_values(values, palette_name):
# normalize the values to range [0, 1]
normalized = (values - min(values)) / (max(values) - min(values))
# convert to indices
indices = np.round(normalized * (len(values) - 1)).astype(np.int32)
# use the indices to get the colors
palette = sns.color_palette(palette_name, len(values))
return np.array(palette).take(indices, axis=0)
plt.figure(figsize=(10,4),dpi=150)
plt.xticks(rotation=90)
sns.barplot(x=bins,
y=prob_data,
palette=colors_from_values(np.array(prob_data), "RdYlGn"))
plt.title('Prediction Market likelihoods for S&P 500 at the end of August 19, 2022')
Great! We’ve successfully visualized the estimated likelihoods of the S&P 500 closing value for the week of August 19th. Try out the API for yourself for other markets. If you want to learn more about using Python for finance, check out our course: https://pieriantraining.com/learn/python-for-finance/