# The Framework of Strategy Evaluation

Having had the signals, we now know when the algorithm would have placed its buy and sell orders, meaning, that we have an approximate replica of the past where can can control our decisions with no hindsight bias. We have to simulate how the strategy would have done given our conditions. This means that we need to calculate the returns and analyze the performance metrics. This section will try to cover the essentials and provide a framework. We can first start with the simplest measure of all, the profit and loss statement. When we back-test our system, we want to see whether it has made money or lost money. After all, it is a game of wealth. This can be done by calculating the gains and losses, the gross and net return, as well as charting the equity plot which is simply the time series of our balance given a perfect algorithm that initiates buy and sell orders based on the strategy. Before we see that, we have to make sure of the following since we want a framework that fits everywhere:

The above table says that we need to have the indicator or the signal generator at column 4 or 5 (Remember, indexing at Python starts at zero). The buy signal (constant = 1) at the column indexed at 6, and the sell short signal (constant = -1) at the column indexed at 7. This ensures the remainder of the code below works how it should work. The reason for this is that on an OHLC data, we have already the first 4 columns occupied, leaving us 1 or 2 columns to place our Indicators, before having two signal columns. Using the deleter function seen above can help you achieve this order in case the indicators occupy more than 2 columns.

The first step into building the Equity Curve is to calculate the profits and losses from the individual trades we are taking. For simplicity reasons, we can consider buying and selling at closing prices. This means that when we get the signal from the indicator or the pattern on close, we initiate the trade on the close until getting another signal where we exit and initiate the new trade. In real life, we do this mainly on the next open, but generally in FX, there is not a huge difference. The code to be defined for the profit/loss columns is the below:

`def holding(Data, buy, sell, buy_return, sell_return):for i in range(len(Data)):        try:            if Data[i, buy] == 1:                for a in range(i + 1, i + 1000):                                          if Data[a, buy] != 0 or Data[a, sell] != 0:                     Data[a, buy_return] = (Data[a, 3] - Data[i, 3])                        break                                            else:                        continue            elif Data[i, sell] == -1:                       for a in range(i + 1, i + 1000):                                          if Data[a, buy] != 0 or Data[a, sell] != 0:                    Data[a, sell_return] = (Data[i, 3] - Data[a, 3])                        break                                                            else:                        continue                                                 except IndexError:            pass# Using the functionholding(my_data, 6, 7, 8, 9)`

This will give us columns 8 and 9 populated with the gross profit and loss results from the trades taken. Now, we have to transform them into cumulative numbers so that we calculate the Equity Curve. To do that, we use the below indexer function:

`def indexer(Data, expected_cost, lot, investment):    # Charting portfolio evolution      indexer = Data[:, 8:10]        # Creating a combined array for long and short returns    z = np.zeros((len(Data), 1), dtype = float)    indexer = np.append(indexer, z, axis = 1)    # Combining Returns    for i in range(len(indexer)):        try:              if indexer[i, 0] != 0:             indexer[i, 2] = indexer[i, 0] - (expected_cost / lot)          if indexer[i, 1] != 0:             indexer[i, 2] = indexer[i, 1] - (expected_cost / lot)        except IndexError:            pass    # Switching to monetary values    indexer[:, 2] = indexer[:, 2] * lot    # Creating a portfolio balance array    indexer = np.append(indexer, z, axis = 1)    indexer[:, 3] = investment     # Adding returns to the balance        for i in range(len(indexer)):        indexer[i, 3] = indexer[i - 1, 3] + (indexer[i, 2])    indexer = np.array(indexer)    return np.array(indexer)# Using the function for a 0.1 lot strategy on \$10,000 investmentexpected_cost = 0.5 * (lot / 10000) # 0.5 pip spreadinvestment    = 10000                  lot           = 10000equity_curve = indexer(my_data, expected_cost, lot, investment)`

The below code is used to generate the chart. Note that the indexer function nets the returns using the estimated transaction cost, hence, the equity curve that would be charted is theoretically net of fees.

`plt.plot(equity_curve[:, 3], linewidth = 1, label = 'EURUSD)plt.grid()plt.legend()plt.axhline(y = investment, color = 'black’, linewidth = 1)plt.title(’Strategy’, fontsize = 20)`

Now, it is time to start evaluating the performance using other measures.

I will present quickly the main ratios and metrics before presenting a full performance function that outputs them all together. Hence, the below discussions are mainly informational, if you are interested by the code, you can find it at the end.

`Hit ratio       =  42.28 % # Simulated ratio`

The Hit Ratio is extremely easy to use. It is simply the number of winning trades over the number of the trades taken in total. For example, if we have 1359 trades over the course of 5 years and we have been profitable in 711 of them , then our hit ratio (accuracy) is 711/1359 = 52.31%.

The Net Profit is simply the last value in the Equity Curve net of fees minus the initial balance. It is simply the added value on the amount we have invested in the beginning.

`Net profit      =  \$ 1209.4 # Simulated Profit`

The net return measure is your return on your investment or equity. If you started with \$1000 and at the end of the year, your balance shows \$1300, then you would have made a healthy 30%.

`Net Return       =  30.01% # Simulated Profit`

A quick glance on the Average Gain across the trades and the Average Loss can help us manage our risks better. For example, if our average gain is \$1.20 and our average loss is \$4.02, then we know that something is not right as we are risking way too much money for way too little gain.

`Average Gain    =  \$ 56.95 per trade # Simulated Average GainAverage Loss    =  \$ -41.14 per trade # Simulated Average Loss`

Following that, we can calculate two measures:

• The theoretical risk-reward ratio: This is the desired ratio of average gains to average losses. A ratio of 2.0 means we are targeting twice as much as we are risking.
• The realized risk-reward ratio: This is the actual ratio of average gains to average losses. A ratio of 0.75 means we are targeting three quarters of what we are risking.
`Theoretical Risk Reward = 2.00 # Simulated RatioRealized Risk Reward    = 0.75 # Simulated Ratio`

The Profit Factor is a relatively quick and straightforward method to compute the profitability of the strategy. It is calculated as the total gross profit over the total gross loss in absolute values, hence, the interpretation of the profit factor (also referred to as the profitability index in the jargon of corporate finance) is how much profit is generated per \$1 of loss. The formula for the profit factor is:

`Profit factor   =  1.34 # Simulated Profit Factor`

Expectancy is a flexible measure presented by the well-known Laurent Bernut that is composed of the average win/loss and the hit ratio. It provides the expected profit or loss on a dollar figure weighted by the hit ratio. The win rate is what we refer to as the hit ratio in the below formula, and through that, the loss ratio is 1 — hit ratio.

`Expectancy      =  \$ 1.33 per trade # Simulated Expectancy`

Another interesting measure is the number of trades. This is simply to understand the frequency of the trades we have.

`Trades          = 3697 # Simulated Number`

Now, we are ready to have all of the above metrics shown at the same time. After calculating the indexer function, we can use the below performance function to give us the metrics we need:

`def performance(indexer, Data, name):    # Profitability index    indexer = np.delete(indexer, 0, axis = 1)    indexer = np.delete(indexer, 0, axis = 1)    profits = []    losses  = []    np.count_nonzero(Data[:, 7])    np.count_nonzero(Data[:, 8])    for i in range(len(indexer)):        if indexer[i, 0] > 0:            value    = indexer[i, 0]            profits  = np.append(profits, value)        if indexer[i, 0] < 0:            value    = indexer[i, 0]            losses   = np.append(losses, value)    # Hit ratio calculation    hit_ratio = round((len(profits) / (len(profits) + len(losses))) * 100, 2)    realized_risk_reward = round(abs(profits.mean() / losses.mean()), 2)    # Expected and total profits / losses    expected_profits = np.mean(profits)    expected_losses  = np.abs(np.mean(losses))    total_profits    = round(np.sum(profits), 3)    total_losses     = round(np.abs(np.sum(losses)), 3)    # Expectancy    expectancy    = round((expected_profits * (hit_ratio / 100)) \                   - (expected_losses * (1 - (hit_ratio / 100))), 2)    # Largest Win and Largest Loss    largest_win = round(max(profits), 2)    largest_loss = round(min(losses), 2)        # Total Return    indexer = Data[:, 10:12]        # Creating a combined array for long and short returns    z = np.zeros((len(Data), 1), dtype = float)    indexer = np.append(indexer, z, axis = 1)    # Combining Returns    for i in range(len(indexer)):        try:              if indexer[i, 0] != 0:             indexer[i, 2] = indexer[i, 0] - (expected_cost / lot)          if indexer[i, 1] != 0:             indexer[i, 2] = indexer[i, 1] - (expected_cost / lot)        except IndexError:            pass    # Switching to monetary values    indexer[:, 2] = indexer[:, 2] * lot    # Creating a portfolio balance array    indexer = np.append(indexer, z, axis = 1)    indexer[:, 3] = investment     # Adding returns to the balance        for i in range(len(indexer)):        indexer[i, 3] = indexer[i - 1, 3] + (indexer[i, 2])    indexer = np.array(indexer)    total_return = (indexer[-1, 3] / indexer[0, 3]) - 1    total_return = total_return * 100    print('-----------Performance-----------', name)    print('Hit ratio       = ', hit_ratio, '%')    print('Net profit      = ', '\$', round(indexer[-1, 3] - indexer[0, 3], 2))    print('Expectancy      = ', '\$', expectancy, 'per trade')    print('Profit factor   = ' , round(total_profits / total_losses, 2))     print('Total Return    = ', round(total_return, 2), '%')    print('')        print('Average Gain    = ', '\$', round((expected_profits), 2), 'per trade')    print('Average Loss    = ', '\$', round((expected_losses * -1), 2), 'per trade')    print('Largest Gain    = ', '\$', largest_win)    print('Largest Loss    = ', '\$', largest_loss)        print('')    print('Realized RR     = ', realized_risk_reward)    print('Minimum         =', '\$', round(min(indexer[:, 3]), 2))    print('Maximum         =', '\$', round(max(indexer[:, 3]), 2))    print('Trades          =', len(profits) + len(losses))# Using the functionperformance(equity_curve, my_data, 'EURUSD)`

This should give us something like the below:

`-----------Performance----------- EURUSDHit ratio       =  42.28 %Net profit      =  \$ 1209.4Expectancy      =  \$ 0.33 per tradeProfit factor   =  1.01Total Return    =  120.94 %Average Gain    =  \$ 56.95 per tradeAverage Loss    =  \$ -41.14 per tradeLargest Gain    =  \$ 347.5Largest Loss    =  \$ -311.6Realized RR     =  1.38Minimum         = \$ -1957.6Maximum         = \$ 4004.2Trades          = 3697# All of the above are simulated results and do not reflect the presented strategy or indicator`

AI/ML

Trending AI/ML Article Identified & Digested via Granola by Ramsey Elbasheer; a Machine-Driven RSS Bot