extract_trades

Defined in fynance.metrics

extract_trades(positions, returns)[source]

Extract round-trip trades from a position series.

A trade is a maximal run of bars over which sign(positions) is constant and nonzero – flat (0) bars are never part of a trade and always close any open run. Its realized return is the compounded net return of holding the (possibly sized, not just signed) position over the run:

\[ret = \prod_{t=t_{in}}^{t_{out}} \left(1 + positions_t \cdot returns_t\right) - 1\]

positions and returns are assumed already aligned index-for-index (positions[t] earns returns[t]) – the convention BacktestResult stores them in, so extract_trades(result.positions, result.returns) and trades agree exactly (see that class for how the causal shift is folded into returns upstream, in the engine).

Parameters:
positionsnp.ndarray[dtype, ndim=1 or 2]

Position / weight series, shape (T,) for a single asset or (T, n_assets) for a book (column-wise, one independent scan per column).

returnsnp.ndarray[dtype, ndim=1 or 2]

Realized return series aligned with positions. Either the same shape as positions, or, for a 2-D positions, a 1-D array of shape (T,) broadcast to every column (the BacktestResult case: a single net return path shared by every asset’s own position run).

Returns:
np.ndarray[TRADE_DTYPE, ndim=1]

One row per trade, ordered by asset then by time within each asset, with fields:

  • asset (int16) – column index (0 for a 1-D positions).

  • t_in (int64) – first bar of the run.

  • t_out (int64) – last bar of the run, inclusive. A run still open at the last observation is included with its current (unrealized) mark rather than dropped.

  • side (int8) – +1 long, -1 short.

  • ret (float64) – compounded net return over the run.

  • bars (int64) – t_out - t_in + 1.

Raises:
ValueError

If positions or returns is not 1-D or 2-D, or their shapes are incompatible (see above).

See also

trade_summary
fynance.metrics.trading.sign_changes, fynance.metrics.trading.trades_per_year
fynance.backtest.result.BacktestResult.trades

Examples

A long run of two bars, a direct flip to a short run of two bars (no flat gap – two adjacent trades, not one), a flat bar, then an open long trade at the end:

>>> import numpy as np
>>> positions = np.array([1.0, 1.0, -1.0, -1.0, 0.0, 1.0])
>>> returns = np.array([0.5, 0.5, 0.5, -0.5, 0.0, 0.25])
>>> trades = extract_trades(positions, returns)
>>> trades['t_in'], trades['t_out'], trades['side']
(array([0, 2, 5]), array([1, 3, 5]), array([ 1, -1,  1], dtype=int8))
>>> trades['ret']
array([ 1.25, -0.25,  0.25])
>>> trades['bars']
array([2, 2, 1])

A two-asset book: each column is scanned independently and tagged with its own asset index:

>>> pos2 = np.array([[1.0, -1.0], [1.0, -1.0], [0.0, -1.0]])
>>> ret2 = np.array([[0.1, 0.1], [0.1, -0.1], [0.0, 0.2]])
>>> trades2 = extract_trades(pos2, ret2)
>>> trades2['asset']
array([0, 1], dtype=int16)
>>> trades2['t_in'], trades2['t_out']
(array([0, 0]), array([1, 2]))