Portfolio module for tzutrader - Position and portfolio management
This module provides portfolio tracking, position management, and performance analytics for trading strategies.
Features:
- Cash and position tracking
- Buy/sell order execution with commissions
- Portfolio valuation and metrics
- Performance calculations (returns, Sharpe ratio, drawdown)
- Transaction history
- Risk management helpers
Types
PerformanceMetrics = object totalReturn*: float64 ## Total return percentage annualizedReturn*: float64 ## Annualized return percentage sharpeRatio*: float64 ## Sharpe ratio (risk-adjusted return) maxDrawdown*: float64 ## Maximum drawdown percentage winRate*: float64 ## Percentage of winning trades totalTrades*: int ## Total number of trades winningTrades*: int ## Number of winning trades losingTrades*: int ## Number of losing trades avgWin*: float64 ## Average winning trade avgLoss*: float64 ## Average losing trade profitFactor*: float64 ## Ratio of gross profit to gross loss
- Portfolio performance metrics
Portfolio = ref object initialCash*: float64 ## Starting cash cash*: float64 ## Current available cash positions*: Table[string, PositionInfo] ## Open positions by symbol transactions*: seq[Transaction] ## Transaction history commission*: float64 ## Commission rate (0.001 = 0.1%) minCommission*: float64 ## Minimum commission per trade totalRealizedPnL*: float64 ## Total realized P&L from all closed trades
- Portfolio with cash and position tracking
PositionInfo = object symbol*: string ## Symbol/ticker side*: PositionSide ## Long, Short, or Flat quantity*: float64 ## Number of shares/units entryPrice*: float64 ## Average entry price entryTime*: int64 ## First entry timestamp currentPrice*: float64 ## Latest market price unrealizedPnL*: float64 ## Unrealized profit/loss realizedPnL*: float64 ## Realized profit/loss from partial closes
- Information about an open position
PositionSide = enum Long = "LONG", ## Long position (own the asset) Short = "SHORT", ## Short position (borrowed/sold) Flat = "FLAT" ## No position
- Position direction
Procs
proc `$`(m: PerformanceMetrics): string {....raises: [], tags: [], forbids: [].}
- String representation of PerformanceMetrics
proc `$`(p: Portfolio): string {....raises: [KeyError], tags: [], forbids: [].}
- String representation of Portfolio
proc `$`(pos: PositionInfo): string {....raises: [], tags: [], forbids: [].}
- String representation of Position
proc buy(p: Portfolio; symbol: string; quantity: float64; price: float64; timestamp: int64 = 0): bool {....raises: [KeyError], tags: [TimeEffect], forbids: [].}
-
Execute a buy order (open or add to long position)
Args: symbol: Symbol to buy quantity: Number of shares (must be positive) price: Price per share timestamp: Optional transaction timestamp (uses current time if 0)
Returns: True if order executed successfully, False if insufficient cash
proc calculateCommission(p: Portfolio; quantity: float64; price: float64): float64 {. ...raises: [], tags: [], forbids: [].}
-
Calculate commission for a trade
Args: quantity: Number of shares/units price: Price per share
Returns: Commission amount
proc calculatePerformance(p: Portfolio; currentPrices: Table[string, float64] = initTable[ string, float64](); riskFreeRate: float64 = 0.02): PerformanceMetrics {. ...raises: [KeyError], tags: [], forbids: [].}
-
Calculate comprehensive performance metrics
Args: currentPrices: Current market prices for open positions riskFreeRate: Risk-free rate for Sharpe ratio (default 2%)
Returns: Performance metrics
proc closePosition(p: Portfolio; symbol: string; price: float64; timestamp: int64 = 0): bool {....raises: [KeyError], tags: [TimeEffect], forbids: [].}
-
Close entire position in a symbol
Args: symbol: Symbol to close price: Closing price timestamp: Optional transaction timestamp
Returns: True if position closed, False if no position exists
proc equity(p: Portfolio; currentPrices: Table[string, float64] = initTable[ string, float64]()): float64 {....raises: [KeyError], tags: [], forbids: [].}
-
Calculate total portfolio equity (cash + position values)
Args: currentPrices: Optional table of current prices for positions
Returns: Total equity value
proc getPosition(p: Portfolio; symbol: string): PositionInfo {. ...raises: [KeyError], tags: [TimeEffect], forbids: [].}
- Get position info for a symbol (returns flat position if none exists)
proc hasPosition(p: Portfolio; symbol: string): bool {....raises: [KeyError], tags: [], forbids: [].}
- Check if portfolio has an open position in symbol
proc marketValue(p: Portfolio): float64 {....raises: [], tags: [], forbids: [].}
- Get total market value of all positions (excludes cash)
proc marketValue(pos: PositionInfo): float64 {....raises: [], tags: [], forbids: [].}
- Get current market value of position
proc newPortfolio(initialCash: float64 = 100000.0; commission: float64 = 0.0; minCommission: float64 = 0.0): Portfolio {....raises: [], tags: [], forbids: [].}
-
Create a new portfolio with initial cash
Args: initialCash: Starting capital (default $100,000) commission: Commission rate as decimal (default 0.0, e.g., 0.001 = 0.1%) minCommission: Minimum commission per trade (default $0)
Returns: New Portfolio instance
proc realizedPnL(p: Portfolio): float64 {....raises: [], tags: [], forbids: [].}
- Get total realized P&L from all closed and partially closed positions
proc sell(p: Portfolio; symbol: string; quantity: float64; price: float64; timestamp: int64 = 0): bool {....raises: [KeyError], tags: [TimeEffect], forbids: [].}
-
Execute a sell order (close or reduce long position)
Args: symbol: Symbol to sell quantity: Number of shares (must be positive) price: Price per share timestamp: Optional transaction timestamp (uses current time if 0)
Returns: True if order executed successfully, False if insufficient position
proc totalPnL(p: Portfolio): float64 {....raises: [], tags: [], forbids: [].}
- Get total P&L (realized + unrealized)
proc totalPnL(pos: PositionInfo): float64 {....raises: [], tags: [], forbids: [].}
- Get total P&L (realized + unrealized)
proc unrealizedPnL(p: Portfolio): float64 {....raises: [], tags: [], forbids: [].}
- Get total unrealized P&L across all positions
proc updatePrice(pos: var PositionInfo; currentPrice: float64) {....raises: [], tags: [], forbids: [].}
- Update position with current market price and recalculate P&L
proc updatePrices(p: Portfolio; prices: Table[string, float64]) {. ...raises: [KeyError], tags: [], forbids: [].}
-
Update all position prices with current market prices
Args: prices: Table of symbol -> current price