TSA Prediction Market: Part 5 - Automated bot
Today’s post
- Introduction
- Connecting to the Kalshi API
- Getting floor strike for upcoming event
- Generating “fair value” for each market
- Placing automated trades
- Conclusion
- Next steps
- The Series
Introduction
Last time, we showed how to productionize our model, so it would automatically run every morning and send market predictions to us every morning.
This enabled us to get a daily email with the predictions to use for manual trading. Now that it’s been a few weeks, and we have built up confidence in our model, we can automate the trading.
This is the return from my first trade using the model. It was only about $10 to test out the model, and the results of a single trade don’t mean anything, but it’s always nice to start out a new strategy with a win.
Connecting to the Kalshi API
Step 1: Create a Kalshi account
This section will detail how to connect to the Kalshi API to get current contract prices and create new orders.
If you use my referral link we both get $25 subject to their referral process
Note: Last I checked, their API does not support MFA, so you can not have MFA on your account if you want to do any of the following steps.
Step 2: Store your credentials in AWS Secrets Manager
AWS Secrets Manager enables you to store credentials in a secure location that is easy to access from you application code.
Step 3:
Use Kalshi’s official Python SDK to connect. I’m using my own code to connect to Kalshi, so the actual connection code might be a bit different. You should probably use whatever they recommend.
Getting floor strike for upcoming event
Familiarize yourself with Kalshi terminology
Market: A single binary market. This is a low level object which rarely will need to be exposed on its own to members. The usage of the term “market” here is consistent with how it’s used in the backend and API. In our case, the markets are the individual contracts at a given passenger level.
Event: An event is a collection of markets and the basic unit that members should interact with on Kalshi.
Series: A series is a collection of related events
Ticker: Each series have unique ticker names to that
Floor Strike: The threshold for a given market that determines the resolution of the market
This blog series has focused on TSA Passenger events. So, all of these TSA passenger markets belong to the TSA series. Each week there is a new event consisting of many markets. For example, for the event ending on 9/8/24, there were markets for passengers above 2.3M, 2.35M, etc.
These definitions were taken directly from the Kalshi API Docs.
Determine the event
The TSA passenger event always goes from Monday - Sunday. The event ticker always reflects the last day of the event, so it’s generally Sunday’s date.
For example, the event ticker for the event ending on 9/8/24 follows:
tsaw-24sep08
Where:
TSAW
is the series.24sep08
is the specific event
We need to algorithmically determine the next event ticker using this format.
Determine the date of the upcoming Sunday
The below code shows how we can programmatically determine the date of the upcoming Sunday. We:
- Calculate the number of days until Sunday
- Add that number of days to the current date
- Add in logic to account for the edge case for if today is Sunday
- Format the date into the format accepted by Kalshi’s API
Query the markets in this event
Now that we have the event ticker, we can use this to query all the markets in this event. This will return each market’s ticker along with the floor strikes
The below code fetches all markets associated with a given event. It will return a dataframe containing the market ticker, floor strike, yes and no prices.
Generating “fair value” for each market
Now we want to determine how much we should pay for each market. If our prediction for a given week is 2.44M passengers, how do we translate that into specific prices? Clearly, we think the market for “Above 2.4M passengers” will resolve to “yes”, but does that mean we should pay $0.99 for that market? That would imply a 99% probability of more than 2.4M passengers.
This section details how we can translate our prediction into actual prices for each market.
For each market, calculate the difference between the prediction and the floor strike
We want to start off by calculating how far off each market is from our predicted value. A market that is 1% off from our prediction should be much more likely than a market that is 10% away.
We can use historical deviations from our prediction value to determine the likelihood of a similar discrepancy. Consider the following example of using a predicted passenger volume of 2,440,000 to determine the price of a market that pays out if the actual number of passengers is above 2.4M:
prediction: 2.44M floor strike: 2.4M
The floor strike is about 1.6% below the prediction. So, this market will resolve to yes as long as the actual
passenger volume comes in at prediction*(1-0.016)
or greater. Inversely, this market will resolve to “no” if
the deviation from our prediction is less than 1.6%. If the actual passenger volume is 2% below our prediction,
we will have actual passenger volumes around 2.38M which is below the required threshold for this market to
pay out.
Check historical data for similar discrepancies
We can consult our historical data to determine how often our prediction was this far off from the actual value. So, in this case we find all instances of historical data where the following condition is met:
actual passengers/prediction-1 < -0.016
This gives us a rough estimate of the likelihood that the actual passengers will be low enough to cause this market to resolve to “no”. We can use this as our estimate of the true value of a given market.
We repeat this process for each market to get the true value of every market.
After running this process for each market in the event we are left with something like this:
{'TSAW-24SEP08-A2.10': {'floor_strike': 2100000, 'side': 'yes', 'true_value': 1.0}, 'TSAW-24SEP08-A2.15': {'floor_strike': 2150000, 'side': 'yes', 'true_value': 1.0}, 'TSAW-24SEP08-A2.20': {'floor_strike': 2200000, 'side': 'yes', 'true_value': 0.9987789987789988}, 'TSAW-24SEP08-A2.25': {'floor_strike': 2250000, 'side': 'yes', 'true_value': 0.9914529914529915}, 'TSAW-24SEP08-A2.30': {'floor_strike': 2300000, 'side': 'yes', 'true_value': 0.978021978021978}, 'TSAW-24SEP08-A2.35': {'floor_strike': 2350000, 'side': 'yes', 'true_value': 0.7973137973137974}, 'TSAW-24SEP08-A2.40': {'floor_strike': 2400000, 'side': 'no', 'true_value': 0.9010989010989011}, 'TSAW-24SEP08-A2.45': {'floor_strike': 2450000, 'side': 'no', 'true_value': 0.989010989010989}, 'TSAW-24SEP08-A2.50': {'floor_strike': 2500000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.55': {'floor_strike': 2550000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.60': {'floor_strike': 2600000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.65': {'floor_strike': 2650000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.70': {'floor_strike': 2700000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.75': {'floor_strike': 2750000, 'side': 'no', 'true_value': 1.0}, 'TSAW-24SEP08-A2.80': {'floor_strike': 2800000, 'side': 'no', 'true_value': 1.0}}
Placing automated trades
Now we can use these “true values” to put in limit orders for each market. Limit orders specify a specific price and will only be executed if another order is placed at your specified price or better. This ensures we can set the price we want for a given market and also allows us to bypass Kalshi’s fees since there are no fees for market makers.
In the following code we:
- Iterate through each market
- For each market, create a limit order for either the “no” or “yes” side to buy
- Set the limit price at a threshold below our “true value” (I used 75% of true value arbitrarily)
Conclusion
In today’s blog post we discussed the following:
- How to create a Kalshi account
- Figuring out the event ticker for the next TSA event
- Using historical data along with our predicted value to calculate an estimate of the “true” value for each contract
- How to use this “true value” to create orders on Kalshi
Some important notes:
- This is not risk-free. You will likely lose money placing trades on Kalshi
- Please test your code extensively before automating any trading
- Use Kalshi’s demo accounts to test
Next steps
We finally have an automated trading bot. This series lasted much longer than I anticipated, so I’m excited to move on to other ideas I have; however, there are a few things I want to follow up on here.
- I want to implement monitoring for my bot. Anyone can check my Kalshi account to see my progress, but I would like to make it more transparent
- Improving the model. We implemented a pretty basic model. I would like to try to improve it to see if we can increase our accuracy
- Add logic to account for trades in the middle of the week. Trading on a Monday when no days are factored into the weekly average is very different from trading on a Thursday when much of the week is already determined.
The Series
Now, that the introduction is out of the way, let’s get started. Below are the different blog posts that are part of this series.
Please reach out if you have any feedback or want to chat.
-
Part 1: Web scraping to get historical data from the TSA site
-
Part 2: Finding supplementary data to help build our model (Note: I ended up not using this data in the model)