---
title: "Reading results — Trading Analytics for Laravel | Roundly"
description: "Every result accessor, and how breakdowns work — global, per pair, per base and quote currency, split into total, buy and sell."
url: https://roundly-consulting.com/open-source/docs/trading-analytics-for-laravel/reading-results
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Trading Analytics for Laravel](https://roundly-consulting.com/open-source/docs/trading-analytics-for-laravel.md)

# Reading results

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

| Accessor | Calculator | toArray() key | Result |
| --- | --- | --- | --- |
| `counts` | Counts | `counts` | Number of trades, open and closed. |
| `wins` | Wins | `wins` | Winning closed trades and the win ratio: wins over closed trades. |
| `volume` | TradingVolume | `volume` | Traded size aggregates, over every trade. |
| `value` | TradingValue | `value` | Notional value (size × open price) aggregates, over every trade. |
| `commission` | Commissions | `commission` | Commission per trade; a trade without one counts as 0. |
| `unrealizedProfitAndLoss` | UnrealizedGrossProfitAndLoss, UnrealizedNetProfitAndLoss | `profit_and_loss.unrealized` | Gross and net P&L of the open trades (at their close price), averaged over them. |
| `realizedProfitAndLoss` | RealizedGrossProfitAndLoss, RealizedNetProfitAndLoss | `profit_and_loss.realized` | Gross and net P&L of the closed trades, averaged over them. |
| `profitFactor` | ProfitFactor | `profit_factor` | Realized gross profit divided by realized gross loss (0 without a loss). |
| `cumulativeReturn` | GrossCumulativeReturn, NetCumulativeReturn | `cumulative_return` | Compounded return in percent, gross and net, its geometric mean per trade and the highest / lowest running return. |
| `frequency` | TradingFrequency | `frequency` | How often trades are opened, over the span of open times. |
| `duration` | TradesDuration | `duration` | How long closed trades stayed open, in seconds. |
| `streaks` | Streaks | `streaks` | Longest runs of winning and of losing closed trades. |
| `expectancy` | Expectancy | `expectancy` | Expected P&L of an average closed trade. |
| `riskRewardRatio` | RiskRewardRatio | `risk_reward_ratio` | Average win divided by average loss. |
| `winRateByPeriod` | WinRateByPeriod | `win_rate_by_period` | Win rate of closed trades, bucketed by the day, week or month they opened. |
| `maxDrawdown` | MaxDrawdown | `max_drawdown` | Largest peak-to-trough drop of the realized (net) equity curve. |
| `riskAdjustedReturns` | RiskAdjustedReturns | `risk_adjusted_returns` | Sharpe 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:

```php
// 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:

```php
$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

| Results | Access path | Note |
| --- | --- | --- |
| counts, wins, wins->closed, wins->winRatio, streaks->wins, streaks->losses | `->global, ->forPair(), ->forBaseCurrency(), ->forQuoteCurrency() → ->total, ->buy, ->sell` | Every 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, ->lowestPair` | count 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:

```php
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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/trading-analytics-for-laravel.md)
