Algo Trading using Machine Learning on Zerodha Kite Connect

Abhijit Mondal
12 min readSep 20, 2021

--

Source: quanta magazine

In this post I am going to explain how I (un)successfully tried my hands on Algo Trading on the Zerodha Kite Connect platform using Python and Machine Learning.

Before moving ahead I must warn my readers that I did not make any significant profits but it was really a cool experience seeing how I can leverage my coding and ML skills to make some money. Also overall I had invested INR 4000 to subscribe to the platform which I spend from my profits from selling Bitcoin and Ethereum. So if you are looking to replicate the below steps it will cost you some bucks.

Account Setup

Open an account on Zerodha and obtain a client id. I use my Zerodha account mostly for long term stock trading.

In order to do algo-trading on Zerodha Kite Connect platform, one needs to first create an app on the following page:

Choose an app name that sounds cool.

For the redirect URL, you can choose anything. I chose ‘http://localhost:8000

It is not necessary that the redirect URL must be a valid or functional URL if you are not building this app for other users but for your own fun. During login you will be 302 redirected to this URL on the browser along with the ‘request_token’ in the query parameter.

The Post-back URL field can remain blank.

After saving the details you will get an API Key and an API Secret. Store these somewhere safe as these 2 keys will be used to access the APIs in future.

For creating an app, it costs around INR 2000 per month. Additionally if you want to use the Historical API feature then you have to spend a extra INR 2000 per month. I had subscribed to the historical API because I wanted to use ML strategies for doing trading. But if your strategies are non-ML and do not require past data at all, you can skip the historical API.

For doing trading, every day after 7 AM in the morning, you need to login to Kite Connect website using your username and password. After login, paste the following URL:

‘https://kite.trade/connect/login?api_key=<YOUR API KEY>&v=3’

in the browser and you will be redirected to an URL given in your redirect URL of your app. In my case it looked something like:

‘http://localhost:8000?request_token=abcd1234’

Note that on redirect, if this URL is not a valid URL you will see an error page, but don't worry, we are only concerned with the ‘request_token’ in the query parameter of the URL. Copy the request_token.

Its all Python from here onwards

Install Python 3 if not present. I am using Python 3.9

Install the kiteconnect python package:

pip3 install --upgrade kiteconnect

It will print the login URL as shown above:

‘https://kite.trade/connect/login?api_key=<YOUR API KEY>&v=3’

Paste this URL on your browser and obtain the request token form the following redirect URL.

‘http://localhost:8000?request_token=abcd1234’

You will obtain an ‘access token’ which will be used to authenticate all the future requests to the APIs.

To obtain the list of all instruments being traded in the NSE and BSE stock exchanges combined, there is an API call:

The purpose of obtaining the instruments is to obtain the instrument tokens. For some of the APIs that I am going to use below (such as the ticker api) instead of the trading symbol, instrument tokens are used as these are considered more reliable.

Trading symbols has many variants for e.g. HDFC has HDFCBANK, HDFCLIFE etc. and multiple exchanges thus it can be confusing and can lead to errors while subscribing to these instruments.

To get the instrument token for a particular instrument e.g. RELIANCE:

{'instrument_token': 128083204, 'exchange_token': '500325', 'tradingsymbol': 'RELIANCE', 'name': 'RELIANCE INDUSTRIES.', 'last_price': 0.0, 'expiry': '', 'strike': 0.0, 'tick_size': 0.05, 'lot_size': 1, 'instrument_type': 'EQ', 'segment': 'BSE', 'exchange': 'BSE'}
{'instrument_token': 738561, 'exchange_token': '2885', 'tradingsymbol': 'RELIANCE', 'name': 'RELIANCE INDUSTRIES', 'last_price': 0.0, 'expiry': '', 'strike': 0.0, 'tick_size': 0.05, 'lot_size': 1, 'instrument_type': 'EQ', 'segment': 'NSE', 'exchange': 'NSE'}

Note that instruments from both exchanges (NSE and BSE) are listed.

Next we want to obtain the historical data for a particular instrument. Historical data can be retrieved based on different time intervals of 5minutes, 3minutes, 2minutes, 1minute and so on.

To build our strategy, we are going to use data for the last 7 days with interval of 1 minutes for backtesting and data older than 7 days ago to 30 days ago (with interval of 1 minutes) to build our model or strategy.

Then convert the train_test_records into Pandas dataframe.

Next we are going to build LSTM Time Series models to predict the future stock prices using historical prices.

For this I experimented with 2 different LSTM models.

LSTM model that takes as input the stock prices from last 20 timesteps and predicts the price for the next timestep.

LSTM encoder-decoder model that takes as input the stock prices from last 20 timesteps and predicts the prices for the next 20 timesteps.

Note that the number of timesteps used here i.e. 20 is arbitrary and can be tuned to the result of the backtesting as we will see below.

The training data is further split into 90% for training the model and 10% for validation of the model in each epoch.

The performance on the test data with the first LSTM model:

There are a few key observations here:

  1. The predicted stock prices is right shifted. The LSTM model learns to follow the trend. Whenever the trend flips i.e price drops after a period of increase or price increases after a period of drops, the LSTM model cannot predict this flip but once the flip happens and the model moves to the next window, it can now predict the flip because the trend has flipped in the last window and LSTM is now just following the trend. Thus LSTM model is good at following trends but bad at predicting flips.
  2. The predicted prices line are above the actual prices line. The relative vertical position of the orange line is dependent on the window size and the starting window. As mentioned, LSTM is good at following trends. Thus if the starting window is small and the mean price within that window is higher as compared to the true mean, the LSTM model starts predicting from a higher price and just follows the trend there onwards. Similarly if the mean price within the starting window was lower than the true mean, then the orange line would be below the blue line. One solution to this problem is to consider a larger window size so that the mean price within that window is much closer to the true mean price of the stock. But the drawback is that if the window size is too large then, then the fluctuations within that window would be greater.

The above plot is with a window size of 200. Note that the orange line is closer to the blue line as compared to window size of 20.

Instead of using the LSTM model that predicts price only for the next timestep, I went ahead with an LSTM model that predicts prices for the next 20 timesteps. I implemented this model using Encoder-Decoder networks.

There are few reasons why I chose to use the Encoder-Decoder model:

  1. Multiple price predictions will give a better idea about the trend in the stock price.
  2. Help identify if there are any profit opportunities in the near future. For example if I have already bought stocks at price X and current price is Y > X. Then if there one or multiple scenarios in the predictions where there are pairs (Ai, Bj) with Ai < Bj and i < j then we can sell the stocks now at profit Y-X and additional profits of Bj-Ai.

Note that we are not doing any short selling here i.e. sell stocks without owning them

Now we come to the part to actually backtest our strategy with the validation_records dataset and estimate profit we can make with the strategy.

Here is a simple strategy for backtesting written in Python:

The algorithm is explained below:

  1. Initially set the curr_state = ‘None’.
  2. There is an in-memory FIFO queue that enqueues the prices as they are seen. When the size of queue is less than the lag parameter, only enqueue the price, else remove the oldest price in the queue to keep the size equal to the lag parameter. The lag parameter is equal to the Encoder model’s number of timesteps.
  3. If the length of FIFO queue is equal to ‘lag’, then run the LSTM model on the prices in the queue and store the predictions in ‘pred’. These are the predictions for prices in the next 20 timesteps.
  4. If the curr_state is None or curr_state is ‘SELL’, i.e. we are not holding any stocks, then we can BUY stocks and set curr_state to ‘BUY’. If the predicted prices for the next 20 timesteps show an upward trend, then we buy the stocks.
  5. If the curr_state is ‘BUY’, i.e. we are holding any stocks to sell, then we can SELL stocks and set curr_state to ‘SELL’. If the predicted prices for the next 20 timesteps show a downward trend and there is a profitable opportunity in the next 20 timesteps, then we sell the stocks.

To identify an upward and downward trend, I am using a heuristic approach.

If there is an upward trend, then the local minimas of the prices is an increasing sub-sequence and similarly if there is a downward trend, then the local maximas of the prices is an decreasing sub-sequence. We count the length of the increasing and decreasing subsequences and take their ratio.

If the ratio of length of increasing subsequences to length of decreasing subsequences is > 1 then we assume it’s an upward trend else its a downward trend.

This can be efficiently computed using stacks.

A proper approach would be to use linear regression to determine the slope of the best fit line for all the predicted prices. But it would too much time taking to fit a linear regression line at each event, thus we abandon this approach in favour of the heuristic.

To check if there is a profitable opportunity in the predicted prices, we use the following method.

It returns the maximum profit for a list of stock prices assuming that we are allowed to trade unlimited number of times.

We can use the same function to compute the maximum possible profit we can make if we traded like an oracle.

Maximum profit for a window of 7 days with 1 stock is INR 970, while the profit obtained with the above strategy is only INR 101.

The points where we are buying and selling in the backtesting data.

Note that the profit numbers are on backtesting data. We have not yet done actual trading on the Kite connect platform. For that we need to obtain stock prices ticker data from Zerodha. The following code is used to obtain continuous ticker data.

The on_connect method, connects to a WebSocket endpoint and then subscribes to a list of instrument tokens. The following call:

ws.subscribe([List of instrument Tokens])

does that.

The ‘on_ticks’ method obtains the ticks for all the instruments we have subscribed to at continuous intervals (about 2–3 events per second). Here we are just printing the current stock price retrieved from the ticker data.

What we need are 2 more things:

Update the strategy class to actually place trades at the stock market.

Update the ticker code to call the strategy class inside on_ticks method.

There are a few things to emphasize in both the codes:

  1. When trading on stock market on Zerodha Kite connect, for every trade placed there is a commission of 0.03% of the trade price or INR 20 whichever is lower. So we need to take that into account while placing our trades and computing the profits.
  2. Using a LSTM model for doing prediction at each event can be time consuming. It might happen that by the time we have reached the point of actually placing the trade after model prediction, getting the trends etc. the price of the stock has changed drastically and placing a sell trade will incur loss instead of a profit.
  3. To overcome this challenge, we use another FIFO queue but it is a shared queue across multiple processes. So in one process, we will insert the stock prices in this shared queue and in another process we will use to place the trades (and yet another process to trim the queue). In this way, when we are placing the actual sell order, we check in the shared queue if the latest price of the stock is at-least greater than the price used to place the sell order. If it is then we place the sell order else do not place sell order.
  4. During placing of the sell order, we only place the sell order if the difference between the current price (after commission adjustment) and the buy price is at-least some ‘delta’. This delta is computed as the 75th percentile value of the stock price differences at events T and T+1.

Although I earned very low profits with these strategies but I got to learn many things while doing algo-trading for the first time. Few of the key learnings are listed below:

  1. Although the placing of trades in the stock market is automated by the above scripts but it is good to monitor the trades done to get an overview of whether we are making profits or losses. Before implementation of the shared queue logic and the delta, I suddenly realised that I lost INR 89 over a period of time due to the latency between the model prediction, trend prediction and placing the actual trades.
  2. The latency of the strategy should be as low as possible. With the usage of deep learning LSTM models it is just the opposite. I would try to come up with faster approaches for the model prediction in the future or probably use C/C++ for the codes to cut down latencies.
  3. In the event that we realize that we have placed a BUY order at the wrong price, we might need to manually shut down the script and then SELL the stock immediately at a minimal loss. So it is important to monitor the trade orders even with automation.
  4. There might be network disconnection after placing a BUY order and you realize later that now the price has tanked and you cannot exit the position anymore. Probably store the last traded price and states for each stock in a DB and whenever back online retrieve the states from the DB. There could be another worker thread to process the DB reads and writes.
  5. In the training data, we had stock prices at a minimum interval of 1 minute while with real time data the interval between consecutive events is as low as 0.5 to 1 second. Thus with a model trained on 1 minute interval it might not work well on data with 1 second interval. One way to overcome this is to store data in the FIFO queue with 1 minute interval.
  6. Hyper-parameter tuning — Tune the hyper-parameters such as the lag duration for the model, number of future predictions from model, the delta etc. to maximize profit on the backtesting data.
  7. Alternative strategies — There are alternative ML strategies too as well as famous non-ML strategies such as Momentum and Mean-Reversion.

Momentum strategy says that if the stock price is increasing it will further increase and if its decreasing it will further decrease. But the rate of increase or decrease will change depending at which point we are.

Mean reversion strategy says that if the stock price has been increasing or decreasing for a while it should now flip. One heuristic we can use here is that if the current stock price is above the mean+2*sd of the stock over the last 3–4 days then stock price should decrease now and its right time to sell or if current stock price is below the mean-2*sd of the stock then stock price should increase now and its right time to buy.

It’s all about experimentation. Nobody can predict the stock market.

Happy Learning and Trading !!!

References:

  1. https://github.com/zerodha/pykiteconnect
  2. https://medium.com/geekculture/beating-the-market-with-a-momentum-trading-strategy-using-python-how-you-can-too-18195a9b23fd
  3. https://medium.com/raposa-technologies/how-to-build-your-first-mean-reversion-trading-strategy-in-python-8c9d4813ee40
  4. https://medium.com/auquan/mean-reversion-simple-trading-strategies-part-1-a18a87c1196a
  5. https://medium.com/auquan/momentum-simple-trading-strategies-part-2-188cf464ffcf

--

--