Skip to content

Portfolio Management

The Portfolio is the top-level container that holds your Account (cash balances) and Positions.

Creating a Portfolio

Using Presets

import qlcore as qc

# Crypto trading setup (USDT base)
portfolio = qc.crypto_portfolio(
    base_currency="USDT",
    initial_balance=10_000,
)

# Traditional spot/equity setup (USD base)
portfolio = qc.spot_portfolio(
    base_currency="USD",
    initial_balance=100_000,
)

Manual Construction

from decimal import Decimal

account = qc.Account(
    base_currency="USDT",
    balances={"USDT": Decimal("10000")},
)

portfolio = qc.Portfolio(account=account)

Multi-Currency Account

account = qc.Account(
    base_currency="USD",
    balances={
        "USD": Decimal("50000"),
        "EUR": Decimal("10000"),
        "BTC": Decimal("0.5"),
    },
)

portfolio = qc.Portfolio(account=account)

Applying Fills

When trades execute, apply fills to update positions:

fill = qc.Fill.create(
    order_id="order-001",
    instrument_id="BTC-USDT-PERP",
    side=qc.OrderSide.BUY,
    quantity="0.1",
    price="45000",
    fee="4.5",
    timestamp_ms=1701849600000,
)

# Apply fill (returns new portfolio, original unchanged)
portfolio = portfolio.apply_fill(fill)

Batch Processing

fills = [fill1, fill2, fill3]

# Apply multiple fills
# Apply multiple fills
for fill in fills:
    portfolio = portfolio.apply_fill(fill)

# Or with validation
validated_fills = qc.validate_fill_sequence(fills)
for fill in validated_fills:
    portfolio = portfolio.apply_fill(fill)

Accessing Positions

# Get specific position
btc_position = portfolio.positions.get("BTC-USDT-PERP")

if btc_position:
    print(btc_position.summary())

# Iterate all positions
for instrument_id, position in portfolio.positions.items():
    print(f"{instrument_id}: {position.side} {position.size}")

Portfolio Summary

print(portfolio.summary())

Output:

Portfolio Summary
=================
Account:
  Base Currency: USDT
  Balances: {'USDT': Decimal('9995.50')}

Positions (1):
  BTC-USDT-PERP: LONG 0.1 @ 45000


Portfolio PnL

Calculate aggregate PnL across all positions:

# Current prices for each instrument
prices = {
    "BTC-USDT-PERP": Decimal("46000"),
    "ETH-USDT-PERP": Decimal("2600"),
}

portfolio_pnl = qc.calculate_portfolio_pnl(portfolio, prices)

print(f"Total Realized: {portfolio_pnl.realized_pnl}")
print(f"Total Unrealized: {portfolio_pnl.unrealized_pnl}")
print(f"Total Fees: {portfolio_pnl.total_fees}")
print(f"Net PnL: {portfolio_pnl.net_pnl}")

Portfolio Weights

Calculate allocation weights:

prices = {
    "BTC-USDT-PERP": Decimal("46000"),
    "ETH-USDT-PERP": Decimal("2600"),
}

weights = qc.weights(portfolio, prices)
# Returns dict: {"BTC-USDT-PERP": Decimal("0.64"), "ETH-USDT-PERP": Decimal("0.36")}

Account Operations

Check Balances

usdt_balance = portfolio.account.balances.get("USDT", Decimal(0))
print(f"USDT: {usdt_balance}")

Update Balances

# Account is immutable, create new one
new_account = qc.Account(
    base_currency=portfolio.account.base_currency,
    balances={**portfolio.account.balances, "BTC": Decimal("0.5")},
)

portfolio = qc.Portfolio(account=new_account, positions=portfolio.positions)

Ledger Tracking

Track all balance changes:

ledger = qc.Ledger()

# Record entries
entry = qc.LedgerEntry(
    currency="USDT",
    amount=Decimal("100"),
    entry_type="TRADE_PNL",
    timestamp_ms=1701849600000,
    reference_id="fill-001",
)

ledger = ledger.add_entry(entry)

# Query ledger
usdt_entries = ledger.entries_for_currency("USDT")

Configuration Presets

Use presets for common scenarios:

# Backtesting optimization
config = qc.backtest_config()
# {'cost_basis_method': 'AVERAGE', 'track_lots': False, 'audit_logging': False, ...}

# Live trading safety
config = qc.live_config()
# {'cost_basis_method': 'FIFO', 'track_lots': True, 'audit_logging': True, ...}

Serialization

# Save portfolio
qc.save_to_file(portfolio, "portfolio.json")

# Load portfolio
portfolio = qc.load_from_file("portfolio.json", qc.Portfolio)

# To JSON string
json_str = qc.to_json(portfolio)

# From JSON string
portfolio = qc.from_json(json_str, qc.Portfolio)