In the previous lesson, we generated a raw trade log from the Entropy Alpha scanner. Our next objective is to process this log to simulate the trades based on our strategy rules and calculate the final profit and loss (P/L) for each trade. This involves determining the exit point for each trade and then calculating the outcome in terms of points and net P/L in Rupees.
The first task is to simulate the outcome of each trade trigger. We will iterate through our trade log and determine the exit time and price for each entry. The exit is determined by one of two conditions: the price hitting the day’s high (our target) or an end-of-day (EOD) square-off at 2:50 PM if the target is not met.
We will add three new columns to our DataFrame:
square_off_time: The timestamp when the trade was exited. This is either the time the target was hit or 2:50 PM for an EOD exit.square_off_price: The price at which the trade was exited. If the target is hit, this is the day’s high. Otherwise, it is the closing price at 2:50 PM.is_target: A boolean flag (True/False) indicating whether the trade successfully hit its target.After simulation, the DataFrame will look like this:
Triggered at stocks Trigger Date Trigger Time price high square_off_time square_off_price is_target
59 2023-04-03 10:01:00 MARUTI 2023-04-03 10:01:00 8558.15 8634.85 2023-04-03 14:50:00+05:30 8554.20 False
57 2023-04-03 10:04:00 M&M 2023-04-03 10:04:00 1178.5 1187.15 2023-04-03 10:22:00+05:30 1187.15 True
54 2023-04-03 10:10:00 ASHOKLEY 2023-04-03 10:10:00 142.6 143.30 2023-04-03 14:50:00+05:30 141.60 False
53 2023-04-03 10:16:00 CHAMBLFERT 2023-04-03 10:16:00 270.65 273.90 2023-04-03 15:19:00+05:30 273.90 True
51 2023-04-03 10:17:00 GMRINFRA 2023-04-03 10:17:00 42.8 43.95 2023-04-03 14:04:00+05:30 43.95 True
... ... ... ... ... ...
4 2023-04-20 12:49:00 CUB 2023-04-20 12:49:00 132.7 134.35 2023-04-20 14:50:00+05:30 133.25 False
3 2023-04-20 14:16:00 BAJAJ-AUTO 2023-04-20 14:16:00 4321 4336.60 2023-04-20 14:50:00+05:30 4317.85 False
2 2023-04-20 15:21:00 TATACONSUM 2023-04-20 15:21:00 705.7 706.50 2023-04-20 14:50:00+05:30 705.00 False
1 2023-04-21 09:58:00 ASIANPAINT 2023-04-21 09:58:00 2854.75 2888.00 2023-04-21 14:23:00+05:30 2888.00 True
0 2023-04-21 10:07:00 APOLLOTYRE 2023-04-21 10:07:00 335.5 337.85 2023-04-21 14:50:00+05:30 334.05 False
60 rows × 9 columns
To calculate the final P/L in Rupees, we need the lot size for each futures contract. Lot sizes vary for each instrument in the F&O segment. We can retrieve this information from the master instrument list provided by the KiteConnect API.
The following function looks up the lot size for a given trading symbol. We assume the full instrument list is loaded into a DataFrame named `instrumentList`.
def get_lotsize(tradesymbol,exchange="NFO"):
if(exchange=="NSE"):
if(tradesymbol=="NIFTY"):tradesymbol="NIFTY 50"
if(tradesymbol=="BANKNIFTY"):tradesymbol="NIFTY BANK"
#print(tradesymbol)
#print(exchange)
dataToken = instrumentList[(instrumentList['tradingsymbol'] == tradesymbol)&(instrumentList['exchange']==exchange)]
return dataToken.lot_size.iloc[0]
df["lotsize"] = df["stocks"].apply(lambda x: get_lotsize(x+"23APRFUT"))
Next, a minor data cleaning step is required. The square_off_time column includes timezone information (e.g., “+05:30”), while Triggered at does not. We will remove the timezone to make the timestamp format uniform across the DataFrame.
import datetime
df['square_off_time'] = df['square_off_time'].apply(lambda x: datetime.datetime.strptime(str(x)[:-6], '%Y-%m-%d %H:%M:%S'))
23APRFUT). For a robust backtesting engine, this should be dynamically generated based on the trade date to ensure the correct contract is always used.With all the necessary data in place, we can now calculate the final P/L. This involves four steps:
Trigger Date and Trigger Time columns.price column to entry_price for better clarity.pl_points = square_off_price - entry_price.pl = pl_points * lotsize.The corresponding Python code is as follows:
# remove "Trigger Date" column
df = df.drop("Trigger Date", axis=1)
# remove "Trigger Time" column
df = df.drop("Trigger Time", axis=1)
# rename "price" column to "entry_price"
df = df.rename(columns={"price": "entry_price"})
df["pl_points"] = df["square_off_price"]-df["entry_price"]
df["pl"] = df["pl_points"]*df["lotsize"]
df
After executing these steps, we have our final trade log. This comprehensive table shows each trade’s entry and exit details, along with the resulting profit or loss.
Triggered at stocks entry_price high square_off_time square_off_price is_target lotsize pl_points pl
59 2023-04-03 10:01:00 MARUTI 8558.15 8634.85 2023-04-03 14:50:00 8554.20 False 100 -3.95 -395.0
57 2023-04-03 10:04:00 M&M 1178.5 1187.15 2023-04-03 10:22:00 1187.15 True 700 8.65 6055.0
54 2023-04-03 10:10:00 ASHOKLEY 142.6 143.30 2023-04-03 14:50:00 141.60 False 5000 -1.0 -5000.0
53 2023-04-03 10:16:00 CHAMBLFERT 270.65 273.90 2023-04-03 15:19:00 273.90 True 1500 3.25 4875.0
51 2023-04-03 10:17:00 GMRINFRA 42.8 43.95 2023-04-03 14:04:00 43.95 True 22500 1.15 25875.0
... ... ... ... ... ... ... ... ... ... ...
5 2023-04-20 10:02:00 ICICIBANK 897.05 901.00 2023-04-21 09:32:00 901.00 True 700 3.95 2765.0
4 2023-04-20 12:49:00 CUB 132.7 134.35 2023-04-20 14:50:00 133.25 False 5000 0.55 2750.0
3 2023-04-20 14:16:00 BAJAJ-AUTO 4321 4336.60 2023-04-20 14:50:00 4317.85 False 250 -3.15 -787.5
2 2023-04-20 15:21:00 TATACONSUM 705.7 706.50 2023-04-20 14:50:00 705.00 False 900 -0.7 -630.0
1 2023-04-21 09:58:00 ASIANPAINT 2854.75 2888.00 2023-04-21 14:23:00 2888.00 True 200 33.25 6650.0
0 2023-04-21 10:07:00 APOLLOTYRE 335.5 337.85 2023-04-21 14:50:00 334.05 False 3500 -1.45 -5075.0
The final DataFrame is the cornerstone for performance analysis.
is_target column quickly tells us if a trade was a winner (True) or if it was closed at EOD, which could be a smaller profit or a loss (False).pl_points column shows the gross profit or loss in terms of price movement, independent of position size.pl column provides the final net profit or loss in Rupees for a single lot trade, which is the most critical metric for evaluating the strategy’s real-world performance.