However, our objective isn’t to collect OHLC data and volume for each day. We specifically require the exact Last Traded Price (LTP) of Reliance at 9:25 AM. How can we obtain this information?
Instead of focusing on daily data, we need to narrow our search to the minute timeframe. Nevertheless, scanning the entire minute timeframe database would impose substantial processing demands on both Zerodha’s servers and our own system.
So, what’s the optimal solution in this scenario? In any case, the output above provides dates when Reliance was traded. If you perform further actions on this data, such as –
zap = zap[["date"]]
zap
date
0 2019-01-01 00:00:00+05:30
1 2019-01-02 00:00:00+05:30
2 2019-01-03 00:00:00+05:30
3 2019-01-04 00:00:00+05:30
4 2019-01-07 00:00:00+05:30
... ...
1159 2023-09-04 00:00:00+05:30
1160 2023-09-05 00:00:00+05:30
1161 2023-09-06 00:00:00+05:30
1162 2023-09-07 00:00:00+05:30
1163 2023-09-08 00:00:00+05:30
1164 rows × 1 columns
From the word 1164 rows, It is easy to assume that there are 1164 trading days where Reliance has been traded. Now, the trick is We will convert this dates to datetime function and add 9:25:00 to it. Right now the time is set at 00:00:00 because there were no time given.
So basically We are asking for the data of that exact 1 minute.
It will be the most optimized server request. In the world of backtesting, the more optimization on the server request, the more better it is in terms of processing.
Now, lets iterate this theory on each day from each row in the zap
dataframe that we got from earlier –
for index, row in zap.iterrows():
# Extract the date from the 'date' column
date = row['date']
# Set the fixed time to 09:25am
time = datetime.time(9, 25)
# Combine the date and time
from_date = datetime.datetime.combine(date, time)
# Calculate the ending date as from_date + 1 minute
ending_date = from_date + datetime.timedelta(minutes=1)
# Format from_date and ending_date as strings in "yyyy-mm-dd HH:MM:SS" format
from_date_str = from_date.strftime("%Y-%m-%d %H:%M:%S")
data = kite.historical_data(token, from_date_str, from_date_str, "minute")
if len(data) == 1:
# Assign the values to new columns in the DataFrame
zap.at[index, 'cmp'] = data[0]['open']
zap
date cmp
0 2019-01-01 00:00:00+05:30 1056.60
1 2019-01-02 00:00:00+05:30 1058.40
2 2019-01-03 00:00:00+05:30 1041.30
3 2019-01-04 00:00:00+05:30 1028.90
4 2019-01-07 00:00:00+05:30 1049.05
... ... ...
1159 2023-09-04 00:00:00+05:30 2421.05
1160 2023-09-05 00:00:00+05:30 2417.25
1161 2023-09-06 00:00:00+05:30 2428.50
1162 2023-09-07 00:00:00+05:30 2419.00
1163 2023-09-08 00:00:00+05:30 2433.05
1164 rows × 2 columns
As a matter of convenience, We have used the open
price on the minute candle of 9:25:00 of each day as LTP.
In case, there were no trade in that minute candle, then it will return na
. Although it is Reliance and it is an extremely liquid candidate, the chance of it not being traded on that time Is next to impossible. But, still as a standard practice, scan for the na
values and drop those rows.
zap.dropna(inplace=True)
na
value immediately. Now, Lets define a quant size i.e. Let’s say the quant size is 500000 and Reliance is trading at 1000. It will take 500000/1000=500 quantity of Reliance.
Right now, as of the time of making this tutorial, SEBI is allowing 4x leverage for intraday trades for the equities. So, with a 10L fund, One can take trade worth of 40L. And with quant size of 5L, there can be simuntaneos 8 trades running without any margin constraint.
Now, Let’s get the levels from the calculate_gann_values()
function.
We will put the cmp
of each day and get the levels.
result = calculate_gann_values(cmp)
buy_above=result["buy_above"]
buy_target=result["buy_target"][0]
buy_sl=result["sell_below"]
sell_below=result["sell_below"]
sell_target=result["sell_target"][0]
sell_sl=result["buy_above"]
quant_size = 500000
quantity = math.floor(quant_size / cmp)
# Create JSON object for 'sell' strategy
sell_data = {
"strategy": "GannSq9",
"symbol": "RELIANCE",
"exchange": "NSE",
"quantity": math.floor(quant_size / cmp),
"entry_time": 0,
"strategy_type": "sell",
"entry_price": sell_below,
"target": sell_target,
"stoploss": sell_sl,
"strategy_start": date + " 09:25:00",
"strategy_end": date + " 15:10:00",
"results": {
}
}
# Create JSON object for 'buy' strategy
buy_data = {
"strategy": "GannSq9",
"symbol": "RELIANCE",
"exchange": "NSE",
"quantity": math.floor(quant_size / cmp),
"entry_time": 0,
"strategy_type": "buy",
"entry_price": buy_above,
"target": buy_target,
"stoploss": buy_sl,
"strategy_start": date + " 09:25:00",
"strategy_end": date + " 15:10:00",
"results": {
}
}
These two JSONs have all the data that is needed to backtest. We can call these JSONs as an input for our backtesting function. So, after we save our signals into a stream of JSON data like this, We can put each of those signals represented by each of the JSOn into backtesting function and get the results.
So, Let’s create the JSON dataset of the buy signals and sell signals by iterating this logic into each day i.e. each row of the dataframe.
import math
import datetime
quant_size = 500000
data_list = [] # Initialize an empty list to store the JSON objects
# Iterate through each row in the DataFrame
for index, row in zap.iterrows():
# Extract the date from the 'date' column
date = row['date'].strftime("%d-%m-%Y")
cmp = row['cmp']
result = calculate_gann_values(cmp)
buy_above=result["buy_above"]
buy_target=result["buy_target"][0]
buy_sl=result["sell_below"]
sell_below=result["sell_below"]
sell_target=result["sell_target"][0]
sell_sl=result["buy_above"]
# Create JSON object for 'sell' strategy
sell_data = {
"strategy": "GannSq9",
"symbol": "RELIANCE",
"exchange": "NSE",
"quantity": math.floor(quant_size / cmp),
"entry_time": 0,
"strategy_type": "sell",
"entry_price": sell_below,
"target": sell_target,
"stoploss": sell_sl,
"strategy_start": date + " 09:25:00",
"strategy_end": date + " 15:10:00",
"results": {
}
}
# Create JSON object for 'buy' strategy
buy_data = {
"strategy": "GannSq9",
"symbol": "RELIANCE",
"exchange": "NSE",
"quantity": math.floor(quant_size / cmp),
"entry_time": 0,
"strategy_type": "buy",
"entry_price": buy_above,
"target": buy_target,
"stoploss": buy_sl,
"strategy_start": date + " 09:25:00",
"strategy_end": date + " 15:10:00",
"results": {
}
}
# Append both JSON objects to the data_list
data_list.extend([sell_data, buy_data])
# The 'data_list' now contains JSON objects for each row with 'sell' and 'buy' strategies
data_list
[{'strategy': 'GannSq9',
'symbol': 'RELIANCE',
'exchange': 'NSE',
'quantity': 473,
'entry_time': 0,
'strategy_type': 'sell',
'entry_price': 1056.25,
'target': 1048.66,
'stoploss': 1064.39,
'strategy_start': '01-01-2019 09:25:00',
'strategy_end': '01-01-2019 15:10:00',
'results': {}},
{'strategy': 'GannSq9',
'symbol': 'RELIANCE',
'exchange': 'NSE',
'quantity': 473,
'entry_time': 0,
'strategy_type': 'buy',
'entry_price': 1064.39,
'target': 1072.02,
'stoploss': 1056.25,
'strategy_start': '01-01-2019 09:25:00',
'strategy_end': '01-01-2019 15:10:00',
'results': {}},
{'strategy': 'GannSq9',
'symbol': 'RELIANCE',
'exchange': 'NSE',
'quantity': 472,
'entry_time': 0,
....
....
Each of the 1164 rows representing 1164 days will create 2*1164=2328 signals I.e. 2328 JSON objects.
Now, We will construct priceaction_backtester()
function which will take these JSON data as input and output with with another JSON that will contain the results of backtesting.
So, Lets pick out one JSON to construct the function upon it.
{'strategy': 'GannSq9',
'symbol': 'RELIANCE',
'exchange': 'NSE',
'quantity': 473,
'entry_time': 0,
'strategy_type': 'sell',
'entry_price': 1056.25,
'target': 1048.66,
'stoploss': 1064.39,
'strategy_start': '01-01-2019 09:25:00',
'strategy_end': '01-01-2019 15:10:00',
'results': {}}