Skip to content
markpaper

src/history/stats.ts

v0.3.0 · 1.5 KB

Download file
import { ZERO, add, parseDec, sub, toStr, type Dec } from './decimal.js';
import type { FillLike } from './types.js';

export interface PnlSummary {
  /** Sum of closedPnl. Price PnL only: funding is NOT included in fills. */
  closedPnl: string;
  /** Sum of fees paid in USDC. */
  fees: string;
  /** Sum of funding `usdc` (negative = paid). */
  funding: string;
  /** closedPnl - fees + funding. */
  net: string;
  /** Fills whose fee is in another token (spot); excluded from `fees`. */
  nonUsdcFeeFills: number;
}

/**
 * Net PnL = sum(closedPnl) - sum(fee) + sum(funding.usdc), exact decimal.
 * `closedPnl` of fills never contains funding; ignoring funding misstates the
 * result of positions held across funding payments.
 */
export function summarizePnl(
  fills: readonly FillLike[],
  funding: readonly { delta: { usdc: string } }[] = [],
): PnlSummary {
  let pnl: Dec = ZERO;
  let fees: Dec = ZERO;
  let fund: Dec = ZERO;
  let nonUsdcFeeFills = 0;
  for (const f of fills) {
    const p = parseDec(f.closedPnl);
    if (p) pnl = add(pnl, p);
    const fee = parseDec(f.fee);
    if (!fee) continue;
    if (f.feeToken !== undefined && f.feeToken !== 'USDC') {
      nonUsdcFeeFills += 1;
      continue;
    }
    fees = add(fees, fee);
  }
  for (const e of funding) {
    const u = parseDec(e.delta.usdc);
    if (u) fund = add(fund, u);
  }
  return {
    closedPnl: toStr(pnl),
    fees: toStr(fees),
    funding: toStr(fund),
    net: toStr(add(sub(pnl, fees), fund)),
    nonUsdcFeeFills,
  };
}
All files