At the time of market opening, the market remains too volatile because of liquidity. It disturbs the very fabric of this strategy which relies on the fact that the Market conforms to Log Normal Distribution. So, We start our trade at 9:20 AM. Similarly, We auto square off at 14:20 PM to avoid day end’s volatility!
Similarly, Apart from the initial conditions of entry, exit and trade management, Here are two set of micro conditions that govern the strategy for the purpose of better stability and ease of exeuction.
Changing the parameters of these conditions does not alter the core startegy that much –
first_trigger
only i.e. A stock can not trigger again within 15 minutes.second_trigger
will have minimum 15 minutes of gap between first_trigger.
We shall be using Zerodha’s Kite Connect API to back test the given dataset. We’re a Zerodha Partner and if you’ve signed up with Unofficed, then You’ll get access to the raw file used to make this tutorial.
Kite Connect offers REST-like HTTP APIs with a wide range of capabilities which will suffice our needs.
As You can see from the example of KiteConnect Python API from Zerodha’s GitHub, the Step 1 is to create the Kite object
import logging
from kiteconnect import KiteConnect
logging.basicConfig(level=logging.DEBUG)
kite = KiteConnect(api_key="your_api_key")
# Redirect the user to the login url obtained
# from kite.login_url(), and receive the request_token
# from the registered redirect url after the login flow.
# Once you have the request_token, obtain the access_token
# as follows.
data = kite.generate_session("request_token_here", api_secret="your_secret")
kite.set_access_token(data["access_token"])
"Triggered at"
into "Trigger Date"
and "Trigger Time"
. It is easier to imagine calculations when things are broken.16 Tue Apr 18 2023, 10:13 am WHIRLPOOL, GODREJPROP, PEL
, it has more than one stock name in the line. So, as we treat them as different trades, Lets make them separate rows.
#Initilization of Pandas Dataframe
df = pd.read_csv("entropy_data.csv")
#Split the Column "Triggered at" into "Trigger Date" and "Trigger Time"
df["Trigger Date"],df["Trigger Time"]=pd.to_datetime(df["Triggered at"]).dt.date, pd.to_datetime(df["Triggered at"]).dt.time
#Removing the commas in Stock of same timed entries
## create an empty dataframe to store the updated rows
new_df = pd.DataFrame(columns=df.columns)
## iterate over the rows in the original dataframe
for index, row in df.iterrows():
## check if the "Stocks (new stocks are highlighted)" column contains a comma
if "," in row["Stocks (new stocks are highlighted)"]:
## split the stock names by comma
stocks = row["Stocks (new stocks are highlighted)"].split(",")
## create a new row for each stock
for stock in stocks:
new_row = row.copy()
new_row["Stocks (new stocks are highlighted)"] = stock.strip() # remove any leading/trailing spaces
new_df = new_df.append(new_row, ignore_index=True)
else:
new_df = new_df.append(row, ignore_index=True)
## assign the updated dataframe to the original dataframe
df = new_df
df
The Output is neat –
Triggered at Count Stocks (new stocks are highlighted) Trigger Date Trigger Time
0 Fri Apr 21 2023, 10:07 am 1 APOLLOTYRE 2023-04-21 10:07:00
1 Fri Apr 21 2023, 9:58 am 1 ASIANPAINT 2023-04-21 09:58:00
2 Thu Apr 20 2023, 3:21 pm 1 TATACONSUM 2023-04-20 15:21:00
3 Thu Apr 20 2023, 2:16 pm 1 BAJAJ-AUTO 2023-04-20 14:16:00
4 Thu Apr 20 2023, 12:49 pm 1 CUB 2023-04-20 12:49:00
... ... ... ... ... ...
775 Tue Sep 13 2022, 10:03 am 3 DIXON 2022-09-13 10:03:00
776 Tue Sep 13 2022, 10:03 am 3 DRREDDY 2022-09-13 10:03:00
777 Tue Sep 13 2022, 10:03 am 3 HEROMOTOCO 2022-09-13 10:03:00
778 Tue Sep 13 2022, 10:01 am 2 DRREDDY 2022-09-13 10:01:00
779 Tue Sep 13 2022, 10:01 am 2 HEROMOTOCO 2022-09-13 10:01:00
780 rows × 5 columns
Trigger Date
column, We will scan for the Trade Specific Conditions for each day. Then we will scan for if there is second entry with a time difference of less than 15 minutes from the first entry. That’s it.
# Convert the "Triggered at" column to datetime format
df['Triggered at'] = pd.to_datetime(df['Triggered at'], format='%a %b %d %Y, %I:%M %p')
# Sort the dataframe by "Triggered at" column
df.sort_values('Triggered at', inplace=True)
# Create an empty list to store the duplicates
duplicates = []
# Iterate over each stock in the dataframe
for stock in df['Stocks (new stocks are highlighted)'].unique():
# Subset the dataframe for the current stock
stock_df = df[df['Stocks (new stocks are highlighted)'] == stock]
# Group the dataframe by "Trigger Date"
grouped = stock_df.groupby('Trigger Date')
# Iterate over the groups
for name, group in grouped:
# Keep only the first entry of the day
group = group[~group.duplicated(subset=['Trigger Date'], keep='first')]
# Get the index of the first entry
first_idx = group.index[0]
# Check if there is a second entry with a time difference of less than 15 minutes from the first entry
if len(group) > 1:
second_idx = group['Triggered at'].diff().dropna().idxmax()
time_diff = group.loc[second_idx, 'Triggered at'] - group.loc[first_idx, 'Triggered at']
if time_diff < timedelta(minutes=15):
# Append the first and second entries to the list of duplicates
duplicates.extend(group.loc[[first_idx, second_idx]].to_dict('records'))
else:
# Append only the first entry to the list of duplicates
duplicates.append(group.loc[first_idx].to_dict())
else:
# Append only the first entry to the list of duplicates
duplicates.append(group.loc[first_idx].to_dict())
# Create a new dataframe from the list of duplicates
duplicates_df = pd.DataFrame(duplicates)
# Drop the duplicate rows from the original dataframe
df.drop_duplicates(subset=['Trigger Date', 'Stocks (new stocks are highlighted)'], keep='first', inplace=True)
df
The Output is neat. It jumps down from 780 rows to 542 rows –
Triggered at Count Stocks (new stocks are highlighted) Trigger Date Trigger Time
779 2022-09-13 10:01:00 2 HEROMOTOCO 2022-09-13 10:01:00
778 2022-09-13 10:01:00 2 DRREDDY 2022-09-13 10:01:00
775 2022-09-13 10:03:00 3 DIXON 2022-09-13 10:03:00
770 2022-09-13 10:06:00 5 ITC 2022-09-13 10:06:00
771 2022-09-13 10:06:00 5 SBICARD 2022-09-13 10:06:00
... ... ... ... ... ...
4 2023-04-20 12:49:00 1 CUB 2023-04-20 12:49:00
3 2023-04-20 14:16:00 1 BAJAJ-AUTO 2023-04-20 14:16:00
2 2023-04-20 15:21:00 1 TATACONSUM 2023-04-20 15:21:00
1 2023-04-21 09:58:00 1 ASIANPAINT 2023-04-21 09:58:00
0 2023-04-21 10:07:00 1 APOLLOTYRE 2023-04-21 10:07:00
542 rows × 5 columns
The current limitation of Stock Broker APIs of Indian market is that the instrument token of a derivative keeps changing every month. So, We are taking the April Futures and hence, the trades of April only for this context –
# Filter the data to take the Apr
df_filtered = df[(df['Triggered at'].dt.time >= pd.Timestamp('09:30').time()) & (df['Triggered at'].dt.time <= pd.Timestamp('14:20').time())]
df_filtered = df.loc[df['Triggered at'].dt.strftime('%Y-%m') == '2023-04']
df= df_filtered
df