def extract_round_trips(transactions,<EOL>portfolio_value=None):
transactions = _groupby_consecutive(transactions)<EOL>roundtrips = []<EOL>for sym, trans_sym in transactions.groupby('<STR_LIT>'):<EOL><INDENT>trans_sym = trans_sym.sort_index()<EOL>price_stack = deque()<EOL>dt_stack = deque()<EOL>trans_sym['<STR_LIT>'] = trans_sym.price *np.sign(trans_sym.amount)<EOL>trans_sym['<STR_LIT>'] = trans_sym.amount.abs().astype(int)<EOL>for dt, t in trans_sym.iterrows():<EOL><INDENT>if t.price < <NUM_LIT:0>:<EOL><INDENT>warnings.warn('<STR_LIT>'<EOL>'<STR_LIT>')<EOL>continue<EOL><DEDENT>indiv_prices = [t.signed_price] * t.abs_amount<EOL>if (len(price_stack) == <NUM_LIT:0>) or(copysign(<NUM_LIT:1>, price_stack[-<NUM_LIT:1>]) == copysign(<NUM_LIT:1>, t.amount)):<EOL><INDENT>price_stack.extend(indiv_prices)<EOL>dt_stack.extend([dt] * len(indiv_prices))<EOL><DEDENT>else:<EOL><INDENT>pnl = <NUM_LIT:0><EOL>invested = <NUM_LIT:0><EOL>cur_open_dts = []<EOL>for price in indiv_prices:<EOL><INDENT>if len(price_stack) != <NUM_LIT:0> and(copysign(<NUM_LIT:1>, price_stack[-<NUM_LIT:1>]) != copysign(<NUM_LIT:1>, price)):<EOL><INDENT>prev_price = price_stack.popleft()<EOL>prev_dt = dt_stack.popleft()<EOL>pnl += -(price + prev_price)<EOL>cur_open_dts.append(prev_dt)<EOL>invested += abs(prev_price)<EOL><DEDENT>else:<EOL><INDENT>price_stack.append(price)<EOL>dt_stack.append(dt)<EOL><DEDENT><DEDENT>roundtrips.append({'<STR_LIT>': pnl,<EOL>'<STR_LIT>': cur_open_dts[<NUM_LIT:0>],<EOL>'<STR_LIT>': dt,<EOL>'<STR_LIT>': price < <NUM_LIT:0>,<EOL>'<STR_LIT>': pnl / invested,<EOL>'<STR_LIT>': sym,<EOL>})<EOL><DEDENT><DEDENT><DEDENT>roundtrips = pd.DataFrame(roundtrips)<EOL>roundtrips['<STR_LIT>'] = roundtrips['<STR_LIT>'].sub(roundtrips['<STR_LIT>'])<EOL>if portfolio_value is not None:<EOL><INDENT>pv = pd.DataFrame(portfolio_value,<EOL>columns=['<STR_LIT>']).assign(date=portfolio_value.index)<EOL>roundtrips['<STR_LIT:date>'] = roundtrips.close_dt.apply(lambda x:<EOL>x.replace(hour=<NUM_LIT:0>,<EOL>minute=<NUM_LIT:0>,<EOL>second=<NUM_LIT:0>))<EOL>tmp = roundtrips.join(pv, on='<STR_LIT:date>', lsuffix='<STR_LIT:_>')<EOL>roundtrips['<STR_LIT>'] = tmp.pnl / tmp.portfolio_value<EOL>roundtrips = roundtrips.drop('<STR_LIT:date>', axis='<STR_LIT>')<EOL><DEDENT>return roundtrips<EOL>
Group transactions into "round trips". First, transactions are
    grouped by day and directionality. Then, long and short
    transactions are matched to create round-trip round_trips for which
    PnL, duration and returns are computed. Crossings where a position
    changes from long to short and vice-versa are handled correctly.

    Under the hood, we reconstruct the individual shares in a
    portfolio over time and match round_trips in a FIFO-order.

    For example, the following transactions would constitute one round trip:
    index                  amount   price    symbol
    2004-01-09 12:18:01    10       50      'AAPL'
    2004-01-09 15:12:53    10       100      'AAPL'
    2004-01-13 14:41:23    -10      100      'AAPL'
    2004-01-13 15:23:34    -10      200       'AAPL'

    First, the first two and last two round_trips will be merged into a two
    single transactions (computing the price via vwap). Then, during
    the portfolio reconstruction, the two resulting transactions will
    be merged and result in 1 round-trip trade with a PnL of
    (150 * 20) - (75 * 20) = 1500.

    Note, that round trips do not have to close out positions
    completely. For example, we could have removed the last
    transaction in the example above and still generated a round-trip
    over 10 shares with 10 shares left in the portfolio to be matched
    with a later transaction.

    Parameters
    ----------
    transactions : pd.DataFrame
        Prices and amounts of executed round_trips. One row per trade.
        - See full explanation in tears.create_full_tear_sheet

    portfolio_value : pd.Series (optional)
        Portfolio value (all net assets including cash) over time.
        Note that portfolio_value needs to beginning of day, so either
        use .shift() or positions.sum(axis='columns') / (1+returns).

    Returns
    -------
    round_trips : pd.DataFrame
        DataFrame with one row per round trip.  The returns column
        contains returns in respect to the portfolio value while
        rt_returns are the returns in regards to the invested capital
        into that partiulcar round-trip.