Top-level container for a paper trading account.
A Portfolio carries balance state, the set of currently-open
positions, the history of closed positions, the execution-attempt
ledger, and a free-form stats map. It is plain data — the engine
that opens, closes, resolves positions and computes P&L lives in
PaperEx.Engine, which threads an updated Portfolio through each
apply_order/4 / resolve_market/4 call.
This module deliberately does not enforce a P&L invariant
(current_balance == starting_balance + sum(closed.pnl) - capital_in_open).
That arithmetic depends on the chosen P&L model (fixed payout vs
mark-to-market), so it is the engine's and caller's concern, not the
struct's. new/1 only validates field shapes.
Fields
:id— caller-assignable identifier.:name— optional human-readable name. Useful when a consumer maintains multiple portfolios (e.g., "research" vs "live-mirror" per the two-modes design constraint).:starting_balance(required) — initial currency balance. Non-negative number.:current_balance— current free balance, defaulting to:starting_balance.:peak_balance— high-water mark for drawdown computation, defaulting to:starting_balance.:positions— list of currently-openPaperEx.Positionstructs. Defaults to[]. Stored as a flat list; the engine scans it by{market_id, strategy}on each order.:history— list of closedPaperEx.Positionstructs. Defaults to[].:executions— execution-attempt ledger: list ofPaperEx.Executionstructs. Defaults to[]. Held alongside:positionsrather than embedded in them so misses and skips, which never produced a position, are still first-class.:stats— free-form map for caller- or engine-computed summary stats (win rate, total P&L, max drawdown, etc.). Defaults to%{}.:metadata— free-form map for caller-provided context (strategy family, mode, …).:created_at—DateTime.t(), defaults toDateTime.utc_now/0.
Summary
Functions
Builds a Portfolio from a map or keyword list.
Types
@type id() :: term() | nil
@type t() :: %PaperEx.Portfolio{ created_at: DateTime.t(), current_balance: number(), executions: [PaperEx.Execution.t()], history: [PaperEx.Position.t()], id: id(), metadata: map(), name: String.t() | nil, peak_balance: number(), positions: [PaperEx.Position.t()], starting_balance: number(), stats: map() }