NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

After calculate(), each metric is a public property on the Analytics instance — null when its calculator didn’t run:

AccessorCalculatortoArray() keyResult
countsCountscountsNumber of trades, open and closed.
winsWinswinsWinning closed trades and the win ratio: wins over closed trades.
volumeTradingVolumevolumeTraded size aggregates, over every trade.
valueTradingValuevalueNotional value (size × open price) aggregates, over every trade.
commissionCommissionscommissionCommission per trade; a trade without one counts as 0.
unrealizedProfitAndLossUnrealizedGrossProfitAndLoss, UnrealizedNetProfitAndLossprofit_and_loss.unrealizedGross and net P&L of the open trades (at their close price), averaged over them.
realizedProfitAndLossRealizedGrossProfitAndLoss, RealizedNetProfitAndLossprofit_and_loss.realizedGross and net P&L of the closed trades, averaged over them.
profitFactorProfitFactorprofit_factorRealized gross profit divided by realized gross loss (0 without a loss).
cumulativeReturnGrossCumulativeReturn, NetCumulativeReturncumulative_returnCompounded return in percent, gross and net, its geometric mean per trade and the highest / lowest running return.
frequencyTradingFrequencyfrequencyHow often trades are opened, over the span of open times.
durationTradesDurationdurationHow long closed trades stayed open, in seconds.
streaksStreaksstreaksLongest runs of winning and of losing closed trades.
expectancyExpectancyexpectancyExpected P&L of an average closed trade.
riskRewardRatioRiskRewardRatiorisk_reward_ratioAverage win divided by average loss.
winRateByPeriodWinRateByPeriodwin_rate_by_periodWin rate of closed trades, bucketed by the day, week or month they opened.
maxDrawdownMaxDrawdownmax_drawdownLargest peak-to-trough drop of the realized (net) equity curve.
riskAdjustedReturnsRiskAdjustedReturnsrisk_adjusted_returnsSharpe and Sortino ratios of the realized net returns.

The comments in these examples show each value’s string form for the three example trades from Running the engine, at ->scale(2).

Breakdowns

Where it applies, a metric exposes a ->global figure plus ->forPair(), ->forBaseCurrency() and ->forQuoteCurrency() breakdowns, each split into total, buy and sell. Pairs are written BASE/QUOTE, exactly as Trade::pair() returns them:

// Every level splits into total, buy and sell
$analytics->counts->global->total;                  // '3' — every trade
$analytics->counts->global->buy;                    // '2'
$analytics->counts->global->sell;                   // '1'

$analytics->counts->forPair('ETH/USD')->total;      // '2'
$analytics->counts->forBaseCurrency('ETH')->sell;   // '1'
$analytics->counts->forQuoteCurrency('USD')->total; // '3'

Aggregates

Amount-like metrics — volume, value, commission, duration, P&L and cumulative return — hold an aggregate at every level, with the pair responsible for the extremes. Every aggregate also carries a count — the trades it was built from — and its average divides by exactly those: realized figures by the closed trades, unrealized ones by the open trades:

$pnl = $analytics->realizedProfitAndLoss->net->global->total;

$pnl->total;        // '781.00' — the sum
$pnl->count;        // 3 — the trades that fed it: here, the closed trades
$pnl->average;      // '260.33' — the total divided by that count
$pnl->highest;      // '990.00' — the best single trade
$pnl->highestPair;  // 'BTC/USD'
$pnl->lowest;       // '-306.00' — the worst single trade
$pnl->lowestPair;   // 'ETH/USD'

Break-even trades and empty figures

  • A break-even trade (P&L exactly 0) is a closed trade that neither won nor lost: it counts towards the win ratio’s denominator and the expectancy, but not towards the average loss of the risk/reward ratio, and it ends both the winning and the losing streak.
  • Fewer than two trades, or trades that all opened in the same second, leave the frequency at 0 with no unit.
  • A figure with nothing to divide — no loss for the profit factor or risk/reward ratio, no deviation for Sharpe or Sortino — stays 0.

Result shapes

ResultsAccess pathNote
counts, wins, wins->closed, wins->winRatio, streaks->wins, streaks->losses->global, ->forPair(), ->forBaseCurrency(), ->forQuoteCurrency() → ->total, ->buy, ->sellEvery end value is a NumericValueAsString.
volume, value, commission, duration, P&L gross / net, cumulativeReturn gross / net->global, ->forPair(), … → ->total, ->buy, ->sell → ->total, ->count, ->average, ->highest, ->highestPair, ->lowest, ->lowestPaircount is an int — the trades the aggregate was built from; highestPair and lowestPair are plain strings.
profitFactor, frequency, P&L grossProfits / grossLosses / netProfits / netLosses->total, ->forPair(), ->forBaseCurrency(), ->forQuoteCurrency()No buy / sell split.

Iterating breakdowns

The public perPair, perBaseCurrency and perQuoteCurrency arrays hold every breakdown the run produced, keyed by pair or currency:

foreach ($analytics->realizedProfitAndLoss->net->perPair as $pair => $pnl) {
    echo $pair, ': ', $pnl->total->total, PHP_EOL;
}
// BTC/USD: 990.00
// ETH/USD: -209.00

array_keys($analytics->counts->perBaseCurrency);  // ['BTC', 'ETH']
array_keys($analytics->counts->perQuoteCurrency); // ['USD']

forPair() and the other breakdown methods return zero values — never null — for a pair or currency without trades.

Several quote currencies

Global totals add amounts from every quote currency as they are. If you trade against more than one quote currency, read amounts per quote currency with forQuoteCurrency().

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.