Search code examples
pythonpython-3.xnumpyreturnalgorithmic-trading

Using cumprod() to calculate equity curve in Python


I am having difficulty calculating a compounding return using cumprod(). It starts compounding from the very beginning, but I only want it to start when 'Final_Order' equals buy and stop when 'Final_Order equal sell, and then reset again on the next buy order. Please see the example below.

Sample data below shows the output I expect to see for column 'Backtest'.

 Time  Adj_Price  Final_Order   Backtest
  0        7          nan          1000
  1        6          nan          1000
  2        5          Buy          1000
  3        7          Buy          1400
  4        8          Sell         1600
  5        6          Sell         1600
  6        4          Buy          1600
  7        5          Buy          2000
  8        7          Buy          2800
  9        9          Sell         3600
  10       7          Sell         3600
  11       7          Sell         3600
  12       6          Sell         3600

Below are calculations for 'Backtest'.

  • Time 1 =IF(Final_Order ="Buy(t0)",(6/7)*1000, Else 1000) = 1000
  • Time 2 =IF(Final_Order ="Buy(t1)",(5/6)*1000, Else 1000) = 1000
  • Time 3 =IF(Final_Order ="Buy(t2)",(7/5)*1000, Else 1000) = 1400
  • Time 4 =IF(Final_Order ="Buy(t3)",(8/7)*1400, Else 1400) = 1600
  • Time 5 =IF(Final_Order ="Buy(t4)",(6/8)*1600, Else 1600) = 1600
  • Time 6 =IF(Final_Order ="Buy(t5)",(4/6)*1600, Else 1600) = 1600
  • Time 7 =IF(Final_Order ="Buy(t6)",(5/4)*1600, Else 1600) = 2000
  • Time 8 =IF(Final_Order ="Buy(t7)",(7/5)*2000, Else 2000) = 2800
  • Time 9 =IF(Final_Order ="Buy(t8)",(9/7)*2800, Else 2800) = 3600
  • Time 10 =IF(Final_Order ="Buy(t9)",(7/9)*3600,Else 3600) = 3600

Below is the code I am working with.

data['Backtest'] = np.where(data['Final_Order'] == 'Buy',
                            ((1 + data['Adj 
Close'].pct_change(1)).cumprod())*1000,
                            data['Backtest'].ffill())

Solution

  • Create a list whose initial value is 1000 and has the rate and the Final Order:

    b = [1000]+list(zip(df.Adj_Price.shift(-1)/df.Adj_Price,df.Final_Order))
    
    df['BACK']=list(itertools.accumulate(b,lambda x,y: round(y[0]*x) if y[1]=="Buy" else x))[:-1]
    
    df
    
        Time  Adj_Price Final_Order  Backtest  BACK
    0      0          7         NaN      1000  1000
    1      1          6         NaN      1000  1000
    2      2          5         Buy      1000  1000
    3      3          7         Buy      1400  1400
    4      4          8        Sell      1600  1600
    5      5          6        Sell      1600  1600
    6      6          4         Buy      1600  1600
    7      7          5         Buy      2000  2000
    8      8          7         Buy      2800  2800
    9      9          9        Sell      3600  3600
    10    10          7        Sell      3600  3600
    11    11          7        Sell      3600  3600
    12    12          6        Sell      3600  3600
    

    Comparing the column created to the one already present, They are identical