Skip to content

Portfolio Manager API Reference

financelib.trading.portfolio_manager.PortfolioManager(initial_cash: float = 1000000, max_position_pct: float = 0.2, max_drawdown_pct: float = 0.15)

Portfolio manager with position tracking and risk management.

Parameters:

Name Type Description Default
initial_cash float

Starting cash balance.

1000000
max_position_pct float

Maximum single position as % of portfolio (0-1).

0.2
max_drawdown_pct float

Maximum drawdown before stopping (0-1).

0.15
Example

pm = PortfolioManager(initial_cash=1000000) pm.open_position("THYAO.IS", 1000, 172.45) pm.update_price("THYAO.IS", 175.00) print(pm.summary())

Source code in financelib/trading/portfolio_manager.py
def __init__(
    self,
    initial_cash: float = 1000000,
    max_position_pct: float = 0.20,
    max_drawdown_pct: float = 0.15,
) -> None:
    self.initial_cash = initial_cash
    self.cash = initial_cash
    self.max_position_pct = max_position_pct
    self.max_drawdown_pct = max_drawdown_pct

    self.positions: Dict[str, Position] = {}
    self.closed_trades: List[Dict[str, Any]] = []
    self.equity_curve: List[float] = [initial_cash]
    self.peak_equity = initial_cash

Attributes

current_drawdown: float property

Current drawdown from peak equity.

total_equity: float property

Total portfolio value (cash + positions).

Functions

close_position(symbol: str, price: float) -> Optional[Dict[str, Any]]

Close an entire position.

Parameters:

Name Type Description Default
symbol str

Security symbol.

required
price float

Exit price.

required

Returns:

Type Description
Optional[Dict[str, Any]]

Trade result dict, or None if no position exists.

Source code in financelib/trading/portfolio_manager.py
def close_position(self, symbol: str, price: float) -> Optional[Dict[str, Any]]:
    """Close an entire position.

    Args:
        symbol: Security symbol.
        price: Exit price.

    Returns:
        Trade result dict, or None if no position exists.
    """
    if symbol not in self.positions:
        return None

    pos = self.positions[symbol]
    revenue = pos.amount * price
    pnl = revenue - pos.cost_basis

    self.cash += revenue
    trade = {
        "symbol": symbol,
        "amount": pos.amount,
        "entry_price": pos.entry_price,
        "exit_price": price,
        "pnl": round(pnl, 2),
        "pnl_pct": round(pnl / pos.cost_basis * 100 if pos.cost_basis else 0, 2),
        "closed_at": datetime.now().isoformat(),
    }
    self.closed_trades.append(trade)
    del self.positions[symbol]

    logger.info(f"Closed position: {symbol} @ {price:.2f} (PnL: {pnl:+.2f})")
    return trade

open_position(symbol: str, amount: float, price: float) -> bool

Open a new position or add to existing.

Parameters:

Name Type Description Default
symbol str

Security symbol.

required
amount float

Number of shares/units.

required
price float

Entry price.

required

Returns:

Type Description
bool

True if position was opened, False if risk limits prevent it.

Source code in financelib/trading/portfolio_manager.py
def open_position(self, symbol: str, amount: float, price: float) -> bool:
    """Open a new position or add to existing.

    Args:
        symbol: Security symbol.
        amount: Number of shares/units.
        price: Entry price.

    Returns:
        True if position was opened, False if risk limits prevent it.
    """
    cost = amount * price

    # Risk check: position size limit
    if self.total_equity > 0:
        position_pct = cost / self.total_equity
        if position_pct > self.max_position_pct:
            logger.warning(
                f"Position size {position_pct:.1%} exceeds limit {self.max_position_pct:.1%}"
            )
            return False

    # Risk check: drawdown limit
    if self.current_drawdown < -self.max_drawdown_pct:
        logger.warning(f"Drawdown {self.current_drawdown:.1%} exceeds limit, trading halted")
        return False

    if cost > self.cash:
        logger.warning(f"Insufficient cash: need {cost:.2f}, have {self.cash:.2f}")
        return False

    if symbol in self.positions:
        pos = self.positions[symbol]
        total_cost = pos.cost_basis + cost
        total_amount = pos.amount + amount
        pos.entry_price = total_cost / total_amount
        pos.amount = total_amount
    else:
        self.positions[symbol] = Position(
            symbol=symbol, amount=amount, entry_price=price, current_price=price
        )

    self.cash -= cost
    logger.info(f"Opened position: {amount} {symbol} @ {price:.2f}")
    return True

record_equity() -> None

Record current equity for the equity curve.

Source code in financelib/trading/portfolio_manager.py
def record_equity(self) -> None:
    """Record current equity for the equity curve."""
    equity = self.total_equity
    self.equity_curve.append(equity)
    if equity > self.peak_equity:
        self.peak_equity = equity

summary() -> Dict[str, Any]

Generate a portfolio summary.

Returns:

Type Description
Dict[str, Any]

Dictionary with portfolio metrics.

Source code in financelib/trading/portfolio_manager.py
def summary(self) -> Dict[str, Any]:
    """Generate a portfolio summary.

    Returns:
        Dictionary with portfolio metrics.
    """
    position_details = []
    for symbol, pos in self.positions.items():
        position_details.append({
            "symbol": symbol,
            "amount": pos.amount,
            "entry_price": round(pos.entry_price, 2),
            "current_price": round(pos.current_price, 2),
            "market_value": round(pos.market_value, 2),
            "unrealized_pnl": round(pos.unrealized_pnl, 2),
            "unrealized_pnl_pct": round(pos.unrealized_pnl_pct, 2),
        })

    realized_pnl = sum(t["pnl"] for t in self.closed_trades)
    unrealized_pnl = sum(p.unrealized_pnl for p in self.positions.values())
    win_trades = [t for t in self.closed_trades if t["pnl"] > 0]
    win_rate = len(win_trades) / len(self.closed_trades) if self.closed_trades else 0

    return {
        "cash": round(self.cash, 2),
        "total_equity": round(self.total_equity, 2),
        "total_pnl": round(self.total_pnl, 2),
        "total_pnl_pct": round(self.total_pnl_pct, 2),
        "realized_pnl": round(realized_pnl, 2),
        "unrealized_pnl": round(unrealized_pnl, 2),
        "current_drawdown": round(self.current_drawdown * 100, 2),
        "num_positions": len(self.positions),
        "num_closed_trades": len(self.closed_trades),
        "win_rate": round(win_rate * 100, 1),
        "positions": position_details,
    }

update_price(symbol: str, price: float) -> None

Update the current price for a position.

Source code in financelib/trading/portfolio_manager.py
def update_price(self, symbol: str, price: float) -> None:
    """Update the current price for a position."""
    if symbol in self.positions:
        self.positions[symbol].current_price = price

financelib.trading.portfolio_manager.Position(symbol: str, amount: float, entry_price: float, current_price: float = 0.0, entry_time: str = (lambda: datetime.now().isoformat())()) dataclass

Represents an open position in a security.