ICHack Optiver Challenge

We ran the file newtommy.py

Arbitrage

When both markets are open, our trading bot looks for arbitrage opportunities in the market, which is when the bid price for a stock is higher than the ask price for the same stock in the different market. We buy the stock at the lower ask price, and sell the stock at the higher bid price, and profit from the difference.

We always perform the trade on the illiquid market first, because we are using the liquid market as a hedge. This approach ensures that we secure positions in the market where it's more challenging to execute orders due to lower liquidity. By doing so, we can then more reliably manage our risk and secure our desired position in the liquid market, where orders are filled more easily and quickly.

Novel statistics-based volume scaling

We implemented a novel statistics-based technique to scale the volume of our trades, to increase the expected profitability of our trades and also reduce inventory risk

The position of our trading bot will always be hedged, because all our trades would buy a stock in one market and sell the same volume of the equivalent stock in the different market. However, the trading environment has a limit of 500 on the number of stock we are able to hold, which means that when we are maxed out holding 500 of a stock in market A and -500 of the same stock in market B, then we lose out on potentially very profitable opportunities that require us to buy more in market A and sell more in market B.

As such, we don’t want to be making too many low profitability trades that take us to a maxed out position then lose out on way more profitable opportunities in the same direction.

In addition, making large trades are risky due to the inventory risk, because one side could fail and we would be stuck with a position and would be vulnerable to market shifts until we close our position out. Thus, we don’t want to be making very large trades that are only mildly profitable.

We kept track of the profitability of the last 30 arbitrage opportunities we found, and for every new arbitrage opportunity, we checked its profitability against the other recent opportunities seen before and scaled our trading volume accordingly, with more profitable trades having a higher volume.

Zero-cost neutralization and its tradeoffs

We were considering whether to neutralize our positions when a zero-cost opportunity presents itself. This decision involves trade-offs. On one hand, neutralizing our position enables us to seize opportunities that might be missed if our position reaches its limit. On the other hand, we might miss out on potential larger volume arbitrage opportunities in the opposite direction if we act to neutralize too quickly.

Market Making

The general idea that we used to reduce the spread in the illiquid market was that we looked to the liquid market to find a fair price, then based on that place a bid order and an ask order with a lower spread.

The new bid price that we placed in the illiquid market would be higher than the bid price in the illiquid market but lower than the bid price of the liquid market. This way, if our order gets filled, we would immediately sell the stock in the liquid market at a higher price, making a profit.

We perform the same thing on the ask side, where we place an ask order lower than the one in the illiquid market but higher than that in the liquid market. This way, we reduce the spread in the illiquid market but still remain in a profitable position.

We were able to see evidence of this in the trading log in the visualizer, where trades occurred where we were not the aggressor.

Market opening hours

When one of the markets is closed, our bot does not perform any trades, as we are not able to hedge and doing anything would mean taking on a net position.

When the market just opens, the order book would be empty, and hence there are no arbitrage opportunities yet. However, the market is more volatile and we are the trendsetters, so we choose to take a more conservative approach with market making, and place a larger spread than we would normally.

Reducing latency from logging

Logging was very useful for us to get a sense of the trades that our bot was making and our positions during which those trades were made, which helped us check if our strategies were implemented correctly.

However, logging introduced latency between our bot retrieving information about a potentially profitable trade, and capitalizing on it by submitting trades. This presented opportunities for our competitors to make profitable trades before us and reduce the profitability of our bot, so it was important to try to minimize this latency.

We addressed this by having a buffer where we stored our logs, and we inserted the logs into a buffer whenever we performed an operation. Then at the end of every cycle, we logged all the events that happened to the console so that we were still able to see the information.

Built With

Share this project:

Updates