Now, here is a patch of code that analyze that details with various popular trading metrics to get an overview of strength of our strategy.
net_pl = round(df["pl"].sum(), 2)
print("Net P&L:", net_pl)
num_target_hits = len(df[df['is_target']])
total_trades = len(df)
num_target_hits = total_trades-num_target_hits
print(f"Number of total trades: {total_trades}")
print(f"Number of times target is hit: {num_target_hits}")
print(f"Number of times stop loss is hit: {num_target_hits}")
win_ratio = round(num_target_hits / total_trades, 2)
print(f"Win Ratio: {win_ratio}")
avg_pl = round(df['pl'].mean(), 2)
max_pl = round(df['pl'].max(), 2)
min_pl = round(df['pl'].min(), 2)
print(f"Avg PL: {avg_pl}")
print(f"Max PL: {max_pl}")
print(f"Min PL: {min_pl}")
gross_pl = round(df['pl'].sum(), 2)
avg_gain = round(df[df['pl'] > 0]['pl'].mean(), 2)
avg_loss = round(df[df['pl'] < 0]['pl'].mean(), 2)
profit_factor = round(-df[df['pl'] < 0]['pl'].sum() / df[df['pl'] > 0]['pl'].sum(), 2)
expected_payoff = round(df['pl'].mean(), 2)
max_drawdown = round((df['pl'].cumsum().cummax() - df['pl'].cumsum()).max(), 2)
risk_free_rate = 0.02
sharpe_ratio = round((expected_payoff - risk_free_rate) / df['pl'].std(), 2)
print(f"Gross P&L: {gross_pl}")
print(f"Average Gain: {avg_gain}")
print(f"Average Loss: {avg_loss}")
print(f"Profit Factor: {profit_factor}")
print(f"Expected Payoff: {expected_payoff}")
print(f"Maximum Drawdown: {max_drawdown}")
print(f"Sharpe Ratio: {sharpe_ratio}")
# calculate gross profit and loss
gross_profit = round(df[df['pl'] > 0]['pl'].sum(), 2)
gross_loss = round(df[df['pl'] < 0]['pl'].sum(), 2)
print(f"Gross Profit: {gross_profit}")
print(f"Gross Loss: {gross_loss}")
# calculate recovery factor and maximal consecutive profit/loss
recovery_factor = round(abs(gross_profit / gross_loss), 2)
print(f"Recovery Factor: {recovery_factor}")
# calculate max consecutive wins and losses
wins = df['pl'] > 0
losses = df['pl'] < 0
max_wins = wins.groupby((wins != wins.shift()).cumsum()).cumsum().max()
max_losses = losses.groupby((losses != losses.shift()).cumsum()).cumsum().min()
print(f"Maximum consecutive wins: {max_wins}")
print(f"Maximum consecutive losses: {abs(max_losses)}")
max_consecutive_profit = df['pl'].rolling(window=2).sum().max()
max_consecutive_loss = abs(df['pl'].rolling(window=2).sum().min())
print(f"Maximal consecutive profit: {max_consecutive_profit:.2f}")
print(f"Maximal consecutive loss: {max_consecutive_loss:.2f}")
# convert the "Triggered at" and "square_off_time" columns to datetime format
df["Triggered at"] = pd.to_datetime(df["Triggered at"])
df["square_off_time"] = pd.to_datetime(df["square_off_time"])
# calculate the holding time for each trade
df["holding_time"] = df["square_off_time"] - df["Triggered at"]
# calculate the average holding time for all trades
avg_holding_time = df["holding_time"].mean()
print(f"Average holding time: {avg_holding_time}")
# calculate the average holding time for profit trades
profit_trades = df[df["pl"] > 0]
avg_profit_holding_time = profit_trades["holding_time"].mean()
print(f"Average holding time for profit trades: {avg_profit_holding_time}")
# calculate the average holding time for loss trades
loss_trades = df[df["pl"] < 0]
avg_loss_holding_time = loss_trades["holding_time"].mean()
print(f"Average holding time for loss trades: {avg_loss_holding_time}")
The Output shows all the available info you need to know to evaluate a strategy –
Net P&L: 166658.75
Number of total trades: 44
Number of times target is hit: 19
Number of times stop loss is hit: 25
Win Ratio: 0.57
Avg PL: 3787.7
Max PL: 25875.0
Min PL: -15847.5
Gross P&L: 166658.75
Average Gain: 9271.39
Average Loss: -4133.19
Profit Factor: 0.31
Expected Payoff: 3787.7
Maximum Drawdown: 25945.0
Sharpe Ratio: 0.42
Gross Profit: 241056.25
Gross Loss: -74397.5
Recovery Factor: 3.24
Maximum consecutive wins: 5
Maximum consecutive losses: 0
Maximal consecutive profit: 47250.00
Maximal consecutive loss: 19627.50
Average holding time: 0 days 04:44:50.454545454
Average holding time for profit trades: 0 days 05:29:34.615384615
Average holding time for loss trades: 0 days 03:40:13.333333333
matplotlib
library to plot the graph. Here goes the code –
import matplotlib.pyplot as plt
# Set the color of the bars based on positive or negative values
colors = ['g' if pl >= 0 else 'r' for pl in df['pl']]
# Create the bar chart
plt.bar(df.index, df['pl'], color=colors)
# Set the labels and title
plt.xlabel("Trade Number")
plt.ylabel("P&L")
plt.title("P&L Over Time")
# Add a grid
plt.grid()
# Add copyright
plt.text(0.5, 0, "© Copyright 2000-2023, Unofficed Inc",
horizontalalignment='center', verticalalignment='center',
transform=plt.gca().transAxes, fontsize=8, color='gray')
# Show the plot
plt.show()
The graph looks like –
Here goes code snippet of another fancy graph. Despite the fancy tag, the graphs are good! It tells so much thing in so little space –
import matplotlib.pyplot as plt
cum_pl = df["pl"].cumsum()[::-1]
trade_num = range(1, len(df)+1)[::-1]
plt.plot(trade_num, cum_pl)
plt.title('Cumulative P&L Over Trades')
plt.xlabel('Trade Number')
plt.ylabel('Cumulative P&L')
# Add copyright
plt.text(0.5, 0, "© Copyright 2000-2023, Unofficed Inc",
horizontalalignment='center', verticalalignment='center',
transform=plt.gca().transAxes, fontsize=8, color='gray')
plt.show()
The graph looks like –
That concludes this chapter.
In the next chapter, We will simulate the trades in equity segment instead of derivatives.