Home / Blog / P&L Accounting
Deep Dive Accounting Multi-Chain Tax Reporting

P&L Accounting for AI Agents: Tracking Every Satoshi

By Purple Flea Engineering March 4, 2026 19 min read

Most AI agents can execute trades but cannot tell you if they are profitable. Realized vs unrealized P&L, fee accounting across casino, trading, and wallet operations, FIFO vs LIFO vs HIFO cost basis — all of this requires careful bookkeeping across 8 chains and 6 Purple Flea services. This guide builds a complete Python P&L tracker from scratch.

Table of Contents

  1. P&L Fundamentals
  2. Cost Basis Methods
  3. Fee Accounting
  4. Multi-Chain Aggregation
  5. Python P&L Tracker
  6. Tax Reporting
01 — P&L Fundamentals

P&L Fundamentals for Multi-Service AI Agents

A Purple Flea agent typically interacts with multiple services simultaneously: placing casino bets, running trading positions, receiving escrow payments, and managing wallets across 8 chains. Each of these generates distinct P&L events that must be tracked with different accounting treatment.

Realized vs. Unrealized P&L

Realized P&L

+$4,230
Closed positions and settled events

Exists only when a position is closed: a bet is settled, a trade is exited, an escrow is released. Money is definitively in or out of the portfolio.

Unrealized P&L

+$1,820
Open positions marked to market

Open perp positions, pending casino bets, held tokens. The P&L is calculated at current market price but can reverse before closing.

Total P&L

+$6,050
Realized + Unrealized

Portfolio net change from inception or from a defined reporting period. Includes all fees, funding costs, and network gas expenses.

P&L Event Categories Across Purple Flea Services

Service Event Type P&L Treatment Fees to Track
Casino API Bet settlement Realized (win/loss) House edge (baked in, track expected value)
Trading API (Spot) Buy/Sell execution Realized on close, Unrealized while held Trading fee (0.1-0.3%), gas costs
Trading API (Perp) Position open/close Realized on close, Unrealized while open Funding rate, trading fee, liquidation risk
Wallet API Asset appreciation Unrealized (no taxable event until sale) Bridge fees, gas on transfers
Escrow Payment receipt/release Realized (income or cost) 1% escrow fee, 15% referral income
Faucet USDC claim Income (realized at receipt) None (free)
ℹ️
Casino P&L requires expected-value thinking

Unlike trading, casino P&L is stochastic. An agent playing blackjack with correct basic strategy has a -0.5% house edge. A "realized P&L" of +$500 after 100 hands is partially luck. Track both actual P&L and expected P&L (hands * avg_bet * house_edge) to separate skill from variance.

8 Chains to track
6 Service types
3 Cost basis methods
24/7 Continuous tracking
02 — Cost Basis Methods

Cost Basis Methods: FIFO, LIFO, and HIFO

When you sell an asset, the "cost basis" of that asset determines your realized P&L. If you bought ETH at different prices on different dates, which purchase are you selling? The answer depends on your chosen accounting method — and it significantly affects both reported P&L and tax liability.

FIFO

First In, First Out. The oldest purchased units are treated as sold first.

+ Simple, widely accepted by tax authorities
- In bull markets, sells high-basis old lots, minimizing gains (favorable)
LIFO

Last In, First Out. Most recently purchased units are sold first.

+ Matches recent cost to current price; good for volatile assets
- Not accepted in many crypto jurisdictions; inventory management complexity
HIFO

Highest In, First Out. Highest cost basis units are sold first, minimizing gains.

+ Minimizes realized gains; ideal for tax optimization
- Requires specific lot identification; more complex record-keeping

Concrete Example: Same Trades, Different Methods

  Purchase history for ETH (all USDC):
  ┌──────────────┬──────────┬────────────┬───────────────┐
  │ Date         │ Quantity │ Price/ETH  │ Total Cost    │
  ├──────────────┼──────────┼────────────┼───────────────┤
  │ Jan 10, 2026 │ 2.0 ETH  │ $3,000     │ $6,000 (Lot A)│
  │ Feb 15, 2026 │ 1.5 ETH  │ $3,500     │ $5,250 (Lot B)│
  │ Mar 01, 2026 │ 1.0 ETH  │ $3,200     │ $3,200 (Lot C)│
  └──────────────┴──────────┴────────────┴───────────────┘

  Sell event: 1.5 ETH at $3,800 on Mar 4, 2026
  Sale proceeds: 1.5 × $3,800 = $5,700

  ┌───────────────────────────────────────────────────────────────┐
  │ Method   │ Lots Used               │ Cost Basis │ Gain/Loss   │
  ├───────────────────────────────────────────────────────────────┤
  │ FIFO     │ 1.5 ETH from Lot A      │ $4,500     │ +$1,200 ✅ │
  │          │ (oldest, $3,000/each)   │            │ (taxable)   │
  ├───────────────────────────────────────────────────────────────┤
  │ LIFO     │ 1.0 ETH Lot C +         │ $4,950     │ +$750       │
  │          │ 0.5 ETH Lot B           │            │ (less tax)  │
  ├───────────────────────────────────────────────────────────────┤
  │ HIFO     │ 1.5 ETH from Lot B      │ $5,250     │ +$450 🏆   │
  │          │ (highest basis, $3,500) │            │ (min tax)   │
  └───────────────────────────────────────────────────────────────┘

  HIFO saves $750 in taxable gains vs FIFO on a single trade.
  At 30% capital gains rate: $225 in tax savings per trade.
      
Figure 1: Same trades, three different reported gains under FIFO/LIFO/HIFO
Consistency is required — you cannot switch methods mid-year

Choose a cost basis method and stick with it for the entire tax year. In the US, crypto is treated as property; switching mid-year requires amendments. Consult a tax professional for your jurisdiction before choosing a method.

03 — Fee Accounting

Fee Accounting: The Hidden P&L Destroyer

Fees are the invisible tax on every agent operation. Across 8 chains and 6 Purple Flea services, fees compound quickly. An agent that does not track fees precisely will dramatically overstate its profitability.

Complete Fee Taxonomy

Fee Type Source Typical Rate Accounting Treatment
Trading Fee Purple Flea Trading API 0.1-0.3% per trade Deduct from proceeds on sell / add to cost on buy
Funding Rate Perpetual positions ±0.01%/8h Realized P&L (positive = income, negative = cost)
Escrow Fee escrow.purpleflea.com 1% of escrow value Transaction cost (reduces net received)
Referral Income Escrow referrals 15% of escrow fees Realized income
Gas (ETH) Ethereum/MATIC/BNB/AVAX $0.10-$20 Cost (adds to basis of asset acquired)
Bridge Fee Stargate/Wormhole/etc 0.05-0.10% Cost of capital movement; reduces net transferred
Casino House Edge Purple Flea Casino API 0.5-5% (game-dependent) Expected value loss; track separately from variance
Gas fees add to cost basis, they are not standalone expenses

In the US and most jurisdictions, gas fees paid to acquire a crypto asset add to the asset's cost basis. The gas paid when buying ETH increases your basis, reducing future capital gains. Gas paid when selling reduces your proceeds. Track this correctly or you will over-pay taxes.

04 — Multi-Chain Aggregation

Multi-Chain P&L Aggregation

Aggregating P&L across 8 chains requires solving the same asset on different chains: ETH on Ethereum and WETH on Polygon are economically equivalent but technically different tokens. A rigorous P&L system must normalize these into a single economic position.

Asset Normalization Rules

  Agent Portfolio Snapshot (normalized to USD):
  ─────────────────────────────────────────────────────────
  ASSET CLASS        CHAIN(S)              VALUE   P&L TODAY
  ─────────────────────────────────────────────────────────
  ETH (all forms)    ETH, MATIC, AVAX      $28,400   +$420
  SOL                SOL                   $12,200   -$180
  BTC                BTC                    $8,100   +$95
  USDC (stables)     ETH, SOL, BNB          $9,500    $0
  BNB                BNB                    $2,300   +$45
  Casino Winnings    (settled, USD eq)        $680   +$680
  Open Perp PnL      ETH/USD Long            +$320   +$320
  ─────────────────────────────────────────────────────────
  TOTAL                                     $61,500  +$1,380
  ─────────────────────────────────────────────────────────
  Fees today:        Gas: -$12.40   Trading: -$8.20   Total: -$20.60
  Net P&L (after fees):                              +$1,359.40
      
Figure 2: Normalized multi-chain portfolio P&L snapshot
05 — Python P&L Tracker

Python P&L Tracker: Full Implementation

The following tracker pulls from all Purple Flea APIs, normalizes positions across chains, applies configurable cost basis methods, and generates daily P&L reports with full fee breakdown.

Python pnl_tracker.py
"""
Multi-chain, multi-service P&L tracker for Purple Flea AI agents.
Supports FIFO, LIFO, HIFO cost basis methods.
Aggregates: Casino, Trading (Spot + Perp), Wallet, Escrow, Faucet.
"""

import asyncio
import csv
import json
import logging
from collections import defaultdict, deque
from dataclasses import dataclass, field, asdict
from decimal import Decimal
from enum import Enum
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timezone
import aiohttp

log = logging.getLogger('pnl_tracker')

PURPLEFLEA_BASE = "https://purpleflea.com"
API_KEY = "your_api_key_here"


# ─── Enums & Constants ───────────────────────────────────────────────────────

class CostBasisMethod(Enum):
    FIFO = "fifo"
    LIFO = "lifo"
    HIFO = "hifo"


class EventType(Enum):
    BUY            = "buy"
    SELL           = "sell"
    CASINO_WIN     = "casino_win"
    CASINO_LOSS    = "casino_loss"
    PERP_OPEN      = "perp_open"
    PERP_CLOSE     = "perp_close"
    FUNDING        = "funding"
    BRIDGE_OUT     = "bridge_out"
    BRIDGE_IN      = "bridge_in"
    FEE            = "fee"
    ESCROW_RECEIVE = "escrow_receive"
    ESCROW_SEND    = "escrow_send"
    FAUCET_CLAIM   = "faucet_claim"
    REFERRAL_EARN  = "referral_earn"


# ─── Data Structures ─────────────────────────────────────────────────────────

@dataclass
class TaxLot:
    """A single acquired unit for cost basis tracking."""
    quantity: Decimal
    cost_per_unit: Decimal
    timestamp: float
    source: str
    lot_id: str


@dataclass
class PnLEvent:
    event_type: EventType
    asset: str
    chain: str
    quantity: Decimal
    price_usd: Decimal
    fee_usd: Decimal
    timestamp: float
    tx_hash: Optional[str] = None
    metadata: Dict = field(default_factory=dict)

    @property
    def gross_usd(self) -> Decimal:
        return self.quantity * self.price_usd

    @property
    def net_usd(self) -> Decimal:
        return self.gross_usd - self.fee_usd


@dataclass
class RealizedGain:
    asset: str
    quantity_sold: Decimal
    sale_price: Decimal
    cost_basis: Decimal
    gain_usd: Decimal
    timestamp: float
    method: str
    lot_id: str


@dataclass
class PnLReport:
    period_start: datetime
    period_end: datetime
    realized_pnl: Decimal
    unrealized_pnl: Decimal
    total_fees: Decimal
    casino_pnl: Decimal
    trading_pnl: Decimal
    funding_pnl: Decimal
    escrow_pnl: Decimal
    faucet_income: Decimal
    net_pnl: Decimal
    gain_events: List[RealizedGain]
    fee_breakdown: Dict[str, Decimal]


# ─── Cost Basis Engine ───────────────────────────────────────────────────────

class CostBasisEngine:
    def __init__(self, method: CostBasisMethod = CostBasisMethod.HIFO):
        self.method = method
        # asset → list of TaxLot (ordered by acquisition time)
        self.lots: Dict[str, List[TaxLot]] = defaultdict(list)
        self.gains: List[RealizedGain] = []

    def acquire(self, asset: str, quantity: Decimal, cost_per_unit: Decimal, ts: float, source: str):
        """Record an asset acquisition (buy, faucet claim, etc)."""
        lot_id = f"{asset}_{int(ts*1000)}"
        lot = TaxLot(
            quantity=quantity,
            cost_per_unit=cost_per_unit,
            timestamp=ts,
            source=source,
            lot_id=lot_id,
        )
        self.lots[asset].append(lot)
        self.lots[asset].sort(key=lambda x: x.timestamp)  # keep chronological
        log.debug(f"Acquired {quantity} {asset} at ${cost_per_unit:.4f} (lot {lot_id})")

    def dispose(
        self, asset: str, quantity: Decimal, sale_price: Decimal, ts: float
    ) -> Tuple[Decimal, List[RealizedGain]]:
        """
        Record disposal (sell, swap out). Returns (realized_gain, lots_used).
        Applies configured cost basis method to select lots.
        """
        available = sum(lot.quantity for lot in self.lots[asset])
        if quantity > available:
            log.warning(f"Disposing {quantity} {asset} but only {available} in lots")
            quantity = available

        # Sort lots by method
        ordered = sorted(
            self.lots[asset],
            key=lambda l: (
                l.timestamp if self.method == CostBasisMethod.FIFO
                else -l.timestamp if self.method == CostBasisMethod.LIFO
                else -l.cost_per_unit  # HIFO: highest cost first
            )
        )

        remaining = quantity
        total_cost_basis = Decimal('0')
        new_gains = []

        for lot in ordered:
            if remaining <= 0:
                break

            use_qty = min(remaining, lot.quantity)
            cost_basis_portion = use_qty * lot.cost_per_unit
            gain = use_qty * sale_price - cost_basis_portion

            gain_event = RealizedGain(
                asset=asset,
                quantity_sold=use_qty,
                sale_price=sale_price,
                cost_basis=lot.cost_per_unit,
                gain_usd=gain,
                timestamp=ts,
                method=self.method.value,
                lot_id=lot.lot_id,
            )
            new_gains.append(gain_event)
            self.gains.append(gain_event)
            total_cost_basis += cost_basis_portion

            lot.quantity -= use_qty
            remaining -= use_qty

        # Remove exhausted lots
        self.lots[asset] = [l for l in self.lots[asset] if l.quantity > 0]

        total_gain = quantity * sale_price - total_cost_basis
        return total_gain, new_gains

    def total_cost_basis(self, asset: str) -> Decimal:
        return sum(l.quantity * l.cost_per_unit for l in self.lots[asset])

    def total_quantity(self, asset: str) -> Decimal:
        return sum(l.quantity for l in self.lots[asset])


# ─── Purple Flea API Data Fetcher ─────────────────────────────────────────────

class PurpleFleasDataFetcher:
    def __init__(self, api_key: str):
        self.headers = {"Authorization": f"Bearer {api_key}"}
        self._session: Optional[aiohttp.ClientSession] = None

    async def __aenter__(self):
        self._session = aiohttp.ClientSession(headers=self.headers)
        return self

    async def __aexit__(self, *args):
        await self._session.close()

    async def get_casino_history(self, since: float) -> List[PnLEvent]:
        async with self._session.get(
            f"{PURPLEFLEA_BASE}/casino-api/history",
            params={"since": int(since)}
        ) as r:
            data = await r.json()

        events = []
        for bet in data["bets"]:
            etype = EventType.CASINO_WIN if bet["outcome"] == "win" else EventType.CASINO_LOSS
            pnl = Decimal(str(bet["net_pnl"]))
            events.append(PnLEvent(
                event_type=etype,
                asset="USDC",
                chain=bet.get("chain", "ETH"),
                quantity=abs(pnl),
                price_usd=Decimal("1.00"),
                fee_usd=Decimal(str(bet.get("fee", 0))),
                timestamp=bet["settled_at"],
                tx_hash=bet.get("tx_hash"),
                metadata={"game": bet["game"], "bet_amount": bet["amount"]},
            ))
        return events

    async def get_trading_history(self, since: float) -> List[PnLEvent]:
        async with self._session.get(
            f"{PURPLEFLEA_BASE}/trading-api/history",
            params={"since": int(since)}
        ) as r:
            data = await r.json()

        events = []
        for trade in data["trades"]:
            etype = EventType.BUY if trade["side"] == "buy" else EventType.SELL
            events.append(PnLEvent(
                event_type=etype,
                asset=trade["base_asset"],
                chain=trade.get("chain", "ETH"),
                quantity=Decimal(str(trade["quantity"])),
                price_usd=Decimal(str(trade["price"])),
                fee_usd=Decimal(str(trade["fee_usd"])),
                timestamp=trade["executed_at"],
                tx_hash=trade.get("tx_hash"),
                metadata={"pair": trade["pair"], "type": trade["type"]},
            ))
        return events

    async def get_wallet_history(self, since: float) -> List[PnLEvent]:
        async with self._session.get(
            f"{PURPLEFLEA_BASE}/wallet-api/history",
            params={"since": int(since)}
        ) as r:
            data = await r.json()

        events = []
        for tx in data["transactions"]:
            if tx["type"] == "bridge_out":
                etype = EventType.BRIDGE_OUT
            elif tx["type"] == "bridge_in":
                etype = EventType.BRIDGE_IN
            else:
                continue  # plain transfers are not P&L events

            events.append(PnLEvent(
                event_type=etype,
                asset=tx["asset"],
                chain=tx["chain"],
                quantity=Decimal(str(tx["amount"])),
                price_usd=Decimal(str(tx.get("price_usd_at_time", 0))),
                fee_usd=Decimal(str(tx.get("fee_usd", 0))),
                timestamp=tx["timestamp"],
                tx_hash=tx.get("tx_hash"),
            ))
        return events

    async def get_escrow_history(self, since: float) -> List[PnLEvent]:
        async with self._session.get(
            f"https://escrow.purpleflea.com/api/history",
            headers=self.headers,
            params={"since": int(since)}
        ) as r:
            data = await r.json()

        events = []
        for e in data["escrows"]:
            etype = EventType.ESCROW_RECEIVE if e["role"] == "recipient" else EventType.ESCROW_SEND
            # Escrow fee: 1% platform fee + 15% of that goes to referrer
            escrow_fee = Decimal(str(e["amount"])) * Decimal("0.01")
            events.append(PnLEvent(
                event_type=etype,
                asset=e["asset"],
                chain=e["chain"],
                quantity=Decimal(str(e["amount"])),
                price_usd=Decimal(str(e.get("price_usd_at_time", 1))),
                fee_usd=escrow_fee,
                timestamp=e["released_at"],
                tx_hash=e.get("tx_hash"),
                metadata={"counterparty": e.get("counterparty")},
            ))
            # Track referral earnings separately if any
            if e.get("referral_earned"):
                events.append(PnLEvent(
                    event_type=EventType.REFERRAL_EARN,
                    asset="USDC",
                    chain=e["chain"],
                    quantity=Decimal(str(e["referral_earned"])),
                    price_usd=Decimal("1.00"),
                    fee_usd=Decimal("0"),
                    timestamp=e["released_at"],
                ))
        return events

    async def get_current_prices(self, assets: List[str]) -> Dict[str, Decimal]:
        async with self._session.get(
            f"{PURPLEFLEA_BASE}/wallet-api/prices",
            params={"assets": ",".join(assets)}
        ) as r:
            data = await r.json()
        return {a: Decimal(str(p)) for a, p in data["prices"].items()}


# ─── Main P&L Tracker ────────────────────────────────────────────────────────

class AgentPnLTracker:
    def __init__(self, api_key: str, method: CostBasisMethod = CostBasisMethod.HIFO):
        self.engine  = CostBasisEngine(method)
        self.fetcher = PurpleFleasDataFetcher(api_key)
        self.all_events: List[PnLEvent] = []

    async def ingest_all_events(self, since: float):
        """Pull all P&L events from all Purple Flea services."""
        async with self.fetcher:
            casino_events, trading_events, wallet_events, escrow_events = await asyncio.gather(
                self.fetcher.get_casino_history(since),
                self.fetcher.get_trading_history(since),
                self.fetcher.get_wallet_history(since),
                self.fetcher.get_escrow_history(since),
            )

        all_events = casino_events + trading_events + wallet_events + escrow_events
        # Process in chronological order for correct cost basis assignment
        all_events.sort(key=lambda e: e.timestamp)
        self.all_events = all_events
        log.info(f"Ingested {len(all_events)} events from all services")

    def process_events(self) -> PnLReport:
        """Process all events and compute P&L report."""
        casino_pnl  = Decimal("0")
        trading_pnl = Decimal("0")
        funding_pnl = Decimal("0")
        escrow_pnl  = Decimal("0")
        faucet_inc  = Decimal("0")
        total_fees  = Decimal("0")
        fee_breakdown: Dict[str, Decimal] = defaultdict(Decimal)
        gain_events: List[RealizedGain] = []

        for event in self.all_events:
            total_fees += event.fee_usd
            fee_breakdown[event.event_type.value] += event.fee_usd

            if event.event_type == EventType.BUY:
                self.engine.acquire(
                    event.asset, event.quantity,
                    event.price_usd + (event.fee_usd / event.quantity),  # fee adds to basis
                    event.timestamp, "trade"
                )

            elif event.event_type == EventType.SELL:
                gain, lots = self.engine.dispose(
                    event.asset, event.quantity, event.price_usd - (event.fee_usd / event.quantity),
                    event.timestamp
                )
                trading_pnl += gain
                gain_events.extend(lots)

            elif event.event_type == EventType.CASINO_WIN:
                casino_pnl += event.net_usd

            elif event.event_type == EventType.CASINO_LOSS:
                casino_pnl -= event.net_usd

            elif event.event_type == EventType.FUNDING:
                funding_pnl += event.net_usd

            elif event.event_type in (EventType.ESCROW_RECEIVE, EventType.REFERRAL_EARN):
                escrow_pnl += event.net_usd

            elif event.event_type == EventType.ESCROW_SEND:
                escrow_pnl -= event.fee_usd  # only the fee is a cost here

            elif event.event_type == EventType.FAUCET_CLAIM:
                faucet_inc += event.net_usd
                self.engine.acquire("USDC", event.quantity, Decimal("0"), event.timestamp, "faucet")

        realized = trading_pnl + casino_pnl + funding_pnl + escrow_pnl + faucet_inc
        # Unrealized: would require current prices — computed separately

        period_events = self.all_events
        start = datetime.fromtimestamp(period_events[0].timestamp, tz=timezone.utc) if period_events else datetime.now(tz=timezone.utc)
        end   = datetime.fromtimestamp(period_events[-1].timestamp, tz=timezone.utc) if period_events else datetime.now(tz=timezone.utc)

        return PnLReport(
            period_start=start,
            period_end=end,
            realized_pnl=realized,
            unrealized_pnl=Decimal("0"),  # filled below
            total_fees=total_fees,
            casino_pnl=casino_pnl,
            trading_pnl=trading_pnl,
            funding_pnl=funding_pnl,
            escrow_pnl=escrow_pnl,
            faucet_income=faucet_inc,
            net_pnl=realized - total_fees,
            gain_events=gain_events,
            fee_breakdown=dict(fee_breakdown),
        )

    def export_csv(self, report: PnLReport, path: str):
        """Export gain events to CSV for tax filing."""
        with open(path, "w", newline="") as f:
            writer = csv.DictWriter(f, fieldnames=[
                "date", "asset", "quantity", "sale_price",
                "cost_basis", "gain_usd", "method", "lot_id"
            ])
            writer.writeheader()
            for gain in sorted(report.gain_events, key=lambda g: g.timestamp):
                writer.writerow({
                    "date":       datetime.fromtimestamp(gain.timestamp).strftime("%Y-%m-%d"),
                    "asset":      gain.asset,
                    "quantity":   str(gain.quantity_sold),
                    "sale_price": str(gain.sale_price),
                    "cost_basis": str(gain.cost_basis),
                    "gain_usd":   str(gain.gain_usd),
                    "method":     gain.method,
                    "lot_id":     gain.lot_id,
                })
        log.info(f"Exported {len(report.gain_events)} gain events to {path}")


# ─── Main: Daily Report ──────────────────────────────────────────────────────

async def generate_daily_report(api_key: str, method: CostBasisMethod = CostBasisMethod.HIFO):
    import time
    since_24h = time.time() - 86400

    tracker = AgentPnLTracker(api_key, method)
    await tracker.ingest_all_events(since_24h)
    report = tracker.process_events()

    print(f"\n=== Purple Flea Agent P&L Report ===")
    print(f"Period: {report.period_start:%Y-%m-%d %H:%M} → {report.period_end:%Y-%m-%d %H:%M}")
    print(f"Cost Basis Method: {method.value.upper()}")
    print(f"\nP&L Breakdown:")
    print(f"  Casino:     {'+' if report.casino_pnl >= 0 else ''}${report.casino_pnl:.2f}")
    print(f"  Trading:    {'+' if report.trading_pnl >= 0 else ''}${report.trading_pnl:.2f}")
    print(f"  Funding:    {'+' if report.funding_pnl >= 0 else ''}${report.funding_pnl:.2f}")
    print(f"  Escrow:     +${report.escrow_pnl:.2f}")
    print(f"  Faucet:     +${report.faucet_income:.2f}")
    print(f"  Fees:       -${report.total_fees:.2f}")
    print(f"  ─────────────────────")
    print(f"  Net P&L:    {'+' if report.net_pnl >= 0 else ''}${report.net_pnl:.2f}")

    tracker.export_csv(report, f"pnl_report_{method.value}_{int(time.time())}.csv")
    return report

if __name__ == "__main__":
    asyncio.run(generate_daily_report(API_KEY, CostBasisMethod.HIFO))
06 — Tax Reporting

Tax Reporting for AI Agent Activities

Tax treatment of AI agent activities is still evolving in most jurisdictions, but the following principles apply broadly. Consult a qualified tax professional for jurisdiction-specific guidance.

Activity US Tax Treatment Reporting Form Notes
Token trading gains Capital gains (short/long term) Form 8949 HIFO minimizes gains; 1-year holding for long-term rate
Casino winnings Ordinary income Schedule C or 1040 Net gambling income; losses offset winnings up to win amount
Faucet income Ordinary income at fair market value Schedule 1 Cost basis = FMV at receipt; future gain is additional cap gain
Referral income (Escrow) Ordinary income Schedule C Self-employment income if running an agent business
Gas fees Add to cost basis (on buy) / reduce proceeds (on sell) Form 8949 Do not deduct gas separately — adjust basis instead
Bridge transfers Disposal + acquisition event Form 8949 Each bridge may be a taxable event; same asset, different chain

Year-End P&L Optimization Strategies

📉

Tax Loss Harvesting

Sell positions with unrealized losses before year-end to offset realized gains. With 8 chains and 6 services, there are usually harvesting opportunities somewhere.

🔄

Wash Sale Awareness

US wash sale rules don't formally apply to crypto (yet), but many jurisdictions may adopt them. Avoid buying back the same asset within 30 days of a harvested loss.

📅

Long-Term Holding

Assets held over 1 year qualify for long-term capital gains rates (0-20% vs 10-37% ordinary). Agents can segregate long-term holds from active trading assets.

🏢

Entity Structure

High-volume AI agents may benefit from operating through an LLC or corporation. Trading losses can offset other income; wash sale rules may differ at entity level.

Crypto tax reporting is a legal requirement — automate it from day one

The P&L tracker above generates Form 8949-compatible CSV output. Don't wait until tax season to reconstruct your agent's transaction history. Every Purple Flea API provides complete transaction history via the /history endpoint. Configure your tracker to run daily and archive all events.

Handling Missing Price Data

A common P&L tracking problem is missing historical prices for obscure assets or times when no price feed was available. Best practices:

Start Tracking Your Agent's P&L Today

Every Purple Flea API returns full transaction history with USD prices at execution time. Build your P&L tracker on top of our data and never lose a satoshi to poor accounting.