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

Hand TradingAnalytics::for() a trade source — Trade objects, rows of any shape Trade::fromRow() reads, a mix, or an ordered query — configure the run and call calculate(); or build and run in one call with TradingAnalytics::calculate(). The engine makes a single pass: it runs every active calculator for each trade, then lets each calculator finish its figures. It holds no per-trade history, so memory stays flat however long the history is:

use Illuminate\Support\Facades\DB;
use RoundlyConsulting\TradingAnalytics\Analytics\Counts;
use RoundlyConsulting\TradingAnalytics\Analytics\Streaks;
use RoundlyConsulting\TradingAnalytics\Facades\TradingAnalytics;

// Build, configure, run
$analytics = TradingAnalytics::for($trades)
    ->scale(5)        // decimal places of every amount, default 10 (see Precision)
    ->calculate();    // returns the Analytics instance

// Or build and run in one call, optionally restricted to some metrics
$analytics = TradingAnalytics::calculate(DB::table('trades')->orderBy('close_time')->orderBy('id'));
$analytics = TradingAnalytics::calculate($rows, only: [Counts::class, Streaks::class]);

$analytics->hasBeenCalculated(); // true
$analytics->toArray();           // the full result matrix as a nested array

for() returns the Analytics engine, ready to configure. Its fluent methods:

  • scale(int) — decimal places of every amount in this run (default from config, 10; see Precision). A negative scale throws InvalidScaleException; getScale() reads it back.
  • usingWinRatePeriod(Period) — the bucket for win rate by period (default from config, daily).
  • only([...]) / except([...]) — choose the calculators to run.
  • onEachTrade() / afterTrades() — hooks into the calculation.
  • calculate() — runs the pass and returns the instance; realized trades must arrive in close-time order (see Trade order). hasBeenCalculated() reports whether it has run; toArray() returns [] until it has.

A worked example

The examples throughout these docs use these three fictional trades, in close-time order, at ->scale(2):

use RoundlyConsulting\TradingAnalytics\DataTransferObjects\Trade;
use RoundlyConsulting\TradingAnalytics\Facades\TradingAnalytics;

// Three fictional, closed trades used by the examples in these docs
$trades = Trade::collect([
    ['base_currency' => 'BTC', 'quote_currency' => 'USD', 'open_price' => '40000', 'close_price' => '42000',
     'size' => '0.5', 'direction' => 'buy', 'open_time' => '2024-03-01 09:00', 'commission' => '10',
     'close_time' => '2024-03-01 15:00'],
    ['base_currency' => 'ETH', 'quote_currency' => 'USD', 'open_price' => '3000', 'close_price' => '3150',
     'size' => '2', 'direction' => 'sell', 'open_time' => '2024-03-02 10:00', 'commission' => '6',
     'close_time' => '2024-03-02 12:00'],
    ['base_currency' => 'ETH', 'quote_currency' => 'USD', 'open_price' => '3100', 'close_price' => '3200',
     'size' => '1', 'direction' => 'buy', 'open_time' => '2024-03-04 08:00', 'commission' => '3',
     'close_time' => '2024-03-05 08:00'],
]);

$analytics = TradingAnalytics::for($trades)->scale(2)->calculate();

(string) $analytics->counts->global->total;                          // '3'
(string) $analytics->realizedProfitAndLoss->gross->global->total->total; // '800.00'
(string) $analytics->realizedProfitAndLoss->net->global->total->total;   // '781.00'
(string) $analytics->wins->winRatio->global->total;                  // '0.66'
(string) $analytics->profitFactor->total;                            // '3.66'
(string) $analytics->maxDrawdown->value;                             // '306.00'

Prefer an injected manager or the engine class itself? See The TradingAnalytics facade and Without the facade.

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.