output_list=[]
for data in data_list:
result = priceaction_backtester(data)
output_list.append(result)
output_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_state': 'Squareoff',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': datetime.datetime(2019, 1, 1, 9, 54, tzinfo=tzoffset(None, 19800)),
'entry_price': 1056.25,
'exit_price': 1059.75,
'exit_time': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=tzoffset(None, 19800)),
'strategy_pl': -1655.45}},
{'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_state': 'No Entry',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': 0,
'entry_price': 1064.39,
'exit_price': 0,
'exit_time': 0,
'strategy_pl': 0}},
{'strategy': 'GannSq9',
'symbol': 'RELIANCE',
'exchange': 'NSE',
'quantity': 472,
.....
.....
.....
results
key of each JSON
'results': {'strategy_state': 'Squareoff',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': datetime.datetime(2019, 1, 1, 9, 54, tzinfo=tzoffset(None, 19800)),
'entry_price': 1056.25,
'exit_price': 1059.75,
'exit_time': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=tzoffset(None, 19800)),
'strategy_pl': -1655.45}
There can be four types of strategy states: “No Trigger,” “StopLoss,” “Target,” and “Squareoff.”
If a trade is triggered, then it will check if the trade has hit the stop loss. If the stop loss has not hit, then it will check if the target is hit. If neither the stop loss nor the target is hit, then it will get auto squared off at the end of the strategy time, which is 15:10.
In short, if the trade is triggered:
The “results” variable contains the results of each signal, indicating in which state the trade ended (No Trigger, StopLoss, Target, or Squareoff).
Like for the case of No Trigger, it is like –
{'strategy_state': 'No Entry',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': 0,
'entry_price': 1064.39,
'exit_price': 0,
'exit_time': 0,
'strategy_pl': 0}
Now, if we convert this list of JSON to rows of a new pandas dataframe it will become messy because what will happen to the values which are inside the results key. It goes to the second level right? So will be there another database under each such values?
Why not normalize 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': {'strategy_state': 'Squareoff',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': datetime.datetime(2019, 1, 1, 9, 54, tzinfo=tzoffset(None, 19800)),
'entry_price': 1056.25,
'exit_price': 1059.75,
'exit_time': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=tzoffset(None, 19800)),
'strategy_pl': -1655.45}}
The ["results"]["strategy_state"]
will become ["results_strategy_state"]
.
Now it will be all in first level. The above JSON will become –
{
"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_state": "Squareoff",
"results_strategy_start": "2019-01-01 09:25:00+05:30",
"results_strategy_end": "2019-01-01 15:10:00+05:30",
"results_entry_time": "2019-01-01 09:54:00+05:30",
"results_entry_price": 1056.25,
"results_exit_price": 1059.75,
"results_exit_time": "2019-01-01 15:10:00+05:30",
"results_strategy_pl": -1655.45
}
from pandas import json_normalize
df = pd.DataFrame(output_list)
# Normalize the 'results' column
df_normalized = json_normalize(df['results'])
df_normalized = df_normalized.add_prefix('results_')
# Drop the original 'results' column from the original DataFrame
df = df.drop('results', axis=1)
# Concatenate the original DataFrame with the normalized 'results' DataFrame
df = pd.concat([df, df_normalized], axis=1)
df
strategy symbol exchange quantity entry_time strategy_type entry_price target stoploss strategy_start strategy_end results_strategy_state results_strategy_start results_strategy_end results_entry_time results_entry_price results_exit_price results_exit_time results_strategy_pl
0 GannSq9 RELIANCE NSE 473 0 sell 1056.25 1048.66 1064.39 01-01-2019 09:25:00 01-01-2019 15:10:00 Squareoff 2019-01-01 09:25:00+05:30 2019-01-01 15:10:00+05:30 2019-01-01 09:54:00+05:30 1056.25 1059.75 2019-01-01 15:10:00+05:30 -1655.45
1 GannSq9 RELIANCE NSE 473 0 buy 1064.39 1072.02 1056.25 01-01-2019 09:25:00 01-01-2019 15:10:00 No Entry 2019-01-01 09:25:00+05:30 2019-01-01 15:10:00+05:30 0 1064.39 0.00 0 0.00
2 GannSq9 RELIANCE NSE 472 0 sell 1056.25 1048.66 1064.39 02-01-2019 09:25:00 02-01-2019 15:10:00 Target 2019-01-02 09:25:00+05:30 2019-01-02 15:10:00+05:30 2019-01-02 09:28:00+05:30 1056.25 1048.66 2019-01-02 12:46:00+05:30 3582.50
3 GannSq9 RELIANCE NSE 472 0 buy 1064.39 1072.02 1056.25 02-01-2019 09:25:00 02-01-2019 15:10:00 No Entry 2019-01-02 09:25:00+05:30 2019-01-02 15:10:00+05:30 0 1064.39 0.00 0 0.00
4 GannSq9 RELIANCE NSE 480 0 sell 1040.06 1032.54 1048.14 03-01-2019 09:25:00 03-01-2019 15:10:00 Target 2019-01-03 09:25:00+05:30 2019-01-03 15:10:00+05:30 2019-01-03 11:36:00+05:30 1040.06 1032.54 2019-01-03 14:29:00+05:30 3609.60
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2309 GannSq9 RELIANCE NSE 205 0 buy 2437.89 2449.02 2425.56 06-09-2023 09:25:00 06-09-2023 15:10:00 No Entry 2023-09-06 09:25:00+05:30 2023-09-06 15:10:00+05:30 0 2437.89 0.00 0 0.00
2310 GannSq9 RELIANCE NSE 206 0 sell 2413.27 2402.20 2425.56 07-09-2023 09:25:00 07-09-2023 15:10:00 StopLoss 2023-09-07 09:25:00+05:30 2023-09-07 15:10:00+05:30 2023-09-07 12:23:00+05:30 2413.27 2425.56 2023-09-07 14:19:00+05:30 -2531.70
2311 GannSq9 RELIANCE NSE 206 0 buy 2425.56 2436.67 2413.27 07-09-2023 09:25:00 07-09-2023 15:10:00 StopLoss 2023-09-07 09:25:00+05:30 2023-09-07 15:10:00+05:30 2023-09-07 10:15:00+05:30 2425.56 2413.27 2023-09-07 12:23:00+05:30 -2531.75
2312 GannSq9 RELIANCE NSE 205 0 sell 2425.56 2414.48 2437.89 08-09-2023 09:25:00 08-09-2023 15:10:00 StopLoss 2023-09-08 09:25:00+05:30 2023-09-08 15:10:00+05:30 2023-09-08 10:34:00+05:30 2425.56 2437.89 2023-09-08 14:19:00+05:30 -2527.60
2313 GannSq9 RELIANCE NSE 205 0 buy 2437.89 2449.02 2425.56 08-09-2023 09:25:00 08-09-2023 15:10:00 StopLoss 2023-09-08 09:25:00+05:30 2023-09-08 15:10:00+05:30 2023-09-08 09:36:00+05:30 2437.89 2425.56 2023-09-08 10:34:00+05:30 -2527.65
2314 rows × 19 columns
{'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_state': 'Squareoff',
'strategy_start': datetime.datetime(2019, 1, 1, 9, 25, tzinfo=),
'strategy_end': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=),
'entry_time': datetime.datetime(2019, 1, 1, 9, 54, tzinfo=tzoffset(None, 19800)),
'entry_price': 1056.25,
'exit_price': 1059.75,
'exit_time': datetime.datetime(2019, 1, 1, 15, 10, tzinfo=tzoffset(None, 19800)),
'strategy_pl': -1655.45}}
Now, let’s perform some column operations on the DataFrame:
'strategy'
column from the DataFrame.'symbol'
is renamed to 'Stocks'
.'quantity'
is renamed to 'Qty.'
.'results_strategy_state'
is renamed to 'is_stoploss'
.'results_entry_time'
is renamed to 'Entry Time'
.'results_exit_time'
is renamed to 'Exit Time'
.'results_entry_price'
is renamed to 'Entry Price'
.'results_exit_price'
is renamed to 'Exit Price'
.'target'
is renamed to 'Target'
.'strategy_type'
is renamed to 'Type'
.'results_strategy_pl'
is renamed to 'P&L'
.'exchange'
'entry_time'
'entry_price'
'stoploss'
'strategy_end'
'results_strategy_start'
'results_strategy_end'
Any columns not found in the DataFrame will be ignored during this process.
results_strategy_end
and strategy_end
have no role as they contain the value 15:10
only. Right?
Also, we know it is NFO!
df = df.drop(columns=['strategy']) \
.rename(columns={'symbol': 'Stocks',
'quantity': 'Qty.',
'results_strategy_state': 'is_stoploss',
'results_entry_time': 'Entry Time',
'results_exit_time': 'Exit Time',
'results_entry_price': 'Entry Price',
'results_exit_price': 'Exit Price',
'target': 'Target',
'strategy_type': 'Type',
'results_strategy_pl': 'P&L'}) \
.drop(columns=['exchange', 'entry_time', 'entry_price', 'stoploss', 'strategy_end', 'results_strategy_start', 'results_strategy_end'], errors='ignore')
df
Stocks Qty. Type Target strategy_start is_stoploss Entry Time Entry Price Exit Price Exit Time P&L
0 RELIANCE 473 sell 1048.66 01-01-2019 09:25:00 Squareoff 2019-01-01 09:54:00+05:30 1056.25 1059.75 2019-01-01 15:10:00+05:30 -1655.45
1 RELIANCE 473 buy 1072.02 01-01-2019 09:25:00 No Entry 0 1064.39 0.00 0 0.00
2 RELIANCE 472 sell 1048.66 02-01-2019 09:25:00 Target 2019-01-02 09:28:00+05:30 1056.25 1048.66 2019-01-02 12:46:00+05:30 3582.50
3 RELIANCE 472 buy 1072.02 02-01-2019 09:25:00 No Entry 0 1064.39 0.00 0 0.00
4 RELIANCE 480 sell 1032.54 03-01-2019 09:25:00 Target 2019-01-03 11:36:00+05:30 1040.06 1032.54 2019-01-03 14:29:00+05:30 3609.60
... ... ... ... ... ... ... ... ... ... ... ...
2309 RELIANCE 205 buy 2449.02 06-09-2023 09:25:00 No Entry 0 2437.89 0.00 0 0.00
2310 RELIANCE 206 sell 2402.20 07-09-2023 09:25:00 StopLoss 2023-09-07 12:23:00+05:30 2413.27 2425.56 2023-09-07 14:19:00+05:30 -2531.70
2311 RELIANCE 206 buy 2436.67 07-09-2023 09:25:00 StopLoss 2023-09-07 10:15:00+05:30 2425.56 2413.27 2023-09-07 12:23:00+05:30 -2531.75
2312 RELIANCE 205 sell 2414.48 08-09-2023 09:25:00 StopLoss 2023-09-08 10:34:00+05:30 2425.56 2437.89 2023-09-08 14:19:00+05:30 -2527.60
2313 RELIANCE 205 buy 2449.02 08-09-2023 09:25:00 StopLoss 2023-09-08 09:36:00+05:30 2437.89 2425.56 2023-09-08 10:34:00+05:30 -2527.65
2314 rows × 11 columns
df['Type'] = df['Type'].apply(lambda x: x.title())
df['Date'] = df['strategy_start'].astype(str)
df['Entry Time'] = df['Entry Time'].astype(str)
df['Exit Time'] = df['Exit Time'].astype(str)
df['Date'] = df['Date'].str[:-9]
df['Entry Time'] = df['Entry Time'].str[11:-9]
df['Exit Time'] = df['Exit Time'].str[11:-9]
df = df[['Date', 'Stocks', 'Qty.', 'Entry Time', 'Entry Price', 'Target', 'Exit Time', 'Exit Price', 'is_stoploss', 'P&L', 'Type']]
df['Date'] = df['Date'].str.replace('-', '/')
df
Date Stocks Qty. Entry Time Entry Price Target Exit Time Exit Price is_stoploss P&L Type
0 01-01-2019 RELIANCE 473 09:54 1056.25 1048.66 15:10 1059.75 Squareoff -1655.45 Sell
1 01-01-2019 RELIANCE 473 1064.39 1072.02 0.00 No Entry 0.00 Buy
2 02-01-2019 RELIANCE 472 09:28 1056.25 1048.66 12:46 1048.66 Target 3582.50 Sell
3 02-01-2019 RELIANCE 472 1064.39 1072.02 0.00 No Entry 0.00 Buy
4 03-01-2019 RELIANCE 480 11:36 1040.06 1032.54 14:29 1032.54 Target 3609.60 Sell
... ... ... ... ... ... ... ... ... ... ... ...
2309 06-09-2023 RELIANCE 205 2437.89 2449.02 0.00 No Entry 0.00 Buy
2310 07-09-2023 RELIANCE 206 12:23 2413.27 2402.20 14:19 2425.56 StopLoss -2531.70 Sell
2311 07-09-2023 RELIANCE 206 10:15 2425.56 2436.67 12:23 2413.27 StopLoss -2531.75 Buy
2312 08-09-2023 RELIANCE 205 10:34 2425.56 2414.48 14:19 2437.89 StopLoss -2527.60 Sell
2313 08-09-2023 RELIANCE 205 09:36 2437.89 2449.02 10:34 2425.56 StopLoss -2527.65 Buy
2314 rows × 11 columns