src/history/stats.ts
v0.3.0 · 1.5 KB
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,
};
}