I'm preparing a project at my Uni where I have to make a simple pair trading strategy using cointegration between two stocks.

I'm stuck on the equity line calculation. I have prepared opening and closing signals but still have to prepare a plot of equity line but I don't know how to calculate it.

There are some details:

-long position in undervalued stock and short in overvalued

-short selling allowed without boundaries, with the deposit of 100% of the short sell value

-when opening the position, we assume equal shares of short and long positions

-open/close signals are defined by the close price, positions are opened/closed by the next day open price

-transaction fee are 0,1% of position value (at the open and the close)


1 Answer 1


Yes, that can be really sophisticated even using such nice tools as pandas. But the basic idea is to find position enters & exits to derive cashflow.

Here is my code to derive all that stuff from generated signals (in my backtester signals are fractions of 2 stocks in portfolio for each moment). I hope I've found all bugs here, but no warranties. Total_invested variable is used to calculate total number of shares in portfolio using only initial capital.

Actually, this code is part of my backtest engine that I'm gonna open-source in a few months. It also deals with spreads and fixed commissions, so you may skip this part. It will require adaptation to your conditions, but hope that helps!

Also take a look at articles by Mike Halls-Moore. This site helped me a lot!

def get_portfolio(self, signals, prices):
    time_index = prices.index
    end = time_index[-1]
    # Assets prices
    x_prices = prices[self.sym1]
    y_prices = prices[self.sym2]
    # Assets weights in portfolio
    x_weights = signals.map(lambda sig:sig[self.sym1])
    y_weights = signals.map(lambda sig:sig[self.sym2])
    # Total number of invested shares
    total_invested = pd.Series(index = signals.index)
    # Need trade enters to calculate portfolio size
    enter_points = x_weights.diff().fillna(0)!=0
    # Index magic here:
    # We need to delete position exits, we suppose there are separate enters/exits
    enter_points[enter_points[enter_points].index[1::2]] = False
    # capital = w₁⋅total⋅p₁ + w₂⋅total⋅p₂ (cover position)
    total_invested[enter_points] = (self.initial_capital
        + y_weights.abs()[enter_points]*y_prices[enter_points]))
    # Zero for right initial open and final close
    total_invested.iloc[0] = 0; total_invested.iloc[-1] = 0;
    total_invested.fillna(method='ffill', inplace=True)
    # Positions rounded to lot sizes
    x_positions = (total_invested*x_weights//self.lot1*self.lot1).fillna(0)
    y_positions = (total_invested*y_weights//self.lot2*self.lot2).fillna(0)
    long_pos = x_positions.copy(); long_pos[long_pos<0] = 0
    short_pos = x_positions.copy(); short_pos[short_pos>0] = 0
    x_pos_diff = x_positions.diff().fillna(0)
    y_pos_diff = y_positions.diff().fillna(0)

    # Tribute to the market-makers: spread
    # Binary divide because we count twice: enter & exit
    tribute = (abs(x_pos_diff)*self.spread1 + abs(y_pos_diff)*self.spread2)/2
    # Cashflow & Turnover: not the same!
    cashflow = -x_pos_diff*x_prices - y_pos_diff*y_prices
    turnover = abs(x_pos_diff)*x_prices + abs(y_pos_diff)*y_prices
    # Commission from turnover
    commission = turnover * self.commission
    commission[cashflow!=0] += self.fixed_commission
    portfolio = pd.DataFrame(index=time_index, columns=['EQ','holdings',
    # Value of shares
    portfolio['holdings'] = x_positions*x_prices + y_positions*y_prices
    # Value of cash, paid commission on each transaction
    portfolio['cash'] = self.initial_capital + (cashflow
        - commission - tribute).cumsum()
    # Equity is the sum of both
    portfolio['EQ'] = portfolio['cash'] + portfolio['holdings']
    portfolio['turnover'] = turnover

    # Time points of trades
    portfolio['long_trades'] = long_pos.diff().fillna(0)
    portfolio['short_trades'] = short_pos.diff().fillna(0)

    portfolio['x_positions'] = x_positions
    portfolio['y_positions'] = y_positions
    return portfolio

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.