Skip to content

Quick Start

This guide walks you through the essential qlcore workflows in 5 minutes.

Your First Portfolio

import qlcore as qc
from decimal import Decimal

# Option 1: Use presets for quick setup
portfolio = qc.crypto_portfolio(
    base_currency="USDT",
    initial_balance=10_000
)

# Option 2: Build manually
account = qc.Account(
    base_currency="USDT",
    balances={"USDT": Decimal("10000")}
)
portfolio = qc.Portfolio(account=account)

Recording a Trade

When a trade executes, record it as a Fill:

# Method 1: Fill.create() with keyword arguments (recommended)
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,
)

# Method 2: Fluent builder pattern
fill = (
    qc.FillBuilder()
    .order("order-002")
    .instrument("ETH-USDT-PERP")
    .buy("2.0")
    .at_price("2500")
    .with_fee("2.5")
    .at_time(1701849600000)
    .build()
)

Applying Fills

# Apply single fill
portfolio = portfolio.apply_fill(fill)

# Apply multiple fills
fills = [fill1, fill2, fill3]
for fill in fills:
    portfolio = portfolio.apply_fill(fill)

Checking Positions

# Get a specific position
position = portfolio.positions["BTC-USDT-PERP"]

# Position properties
print(position.instrument_id)  # "BTC-USDT-PERP"
print(position.side)           # PositionSide.LONG
print(position.size)           # Decimal("0.1")
print(position.avg_entry_price)  # Decimal("45000")
print(position.realized_pnl)   # Accumulated realized PnL

# REPL-friendly summary
print(position.summary())

Calculating PnL

# Unrealized PnL at current market price
current_price = Decimal("46000")
upnl = qc.unrealized_pnl(position, current_price=current_price)
print(f"Unrealized PnL: ${upnl}")  # $100

# Mark to market
mtm_position = qc.mark_to_market(position, mark_price=current_price)
print(mtm_position.unrealized_pnl)

# Full PnL breakdown
breakdown = qc.calculate_pnl(position, current_price=current_price)
print(breakdown)

Risk Metrics

# Calculate from a series of returns
returns = [0.02, -0.01, 0.03, -0.02, 0.015]

sharpe = qc.sharpe_ratio(returns, risk_free_rate=0.0)
sortino = qc.sortino_ratio(returns, risk_free_rate=0.0)
mdd = qc.max_drawdown(returns)
var_95 = qc.historical_var(returns, confidence=0.95)

print(f"Sharpe: {sharpe:.2f}")
print(f"Sortino: {sortino:.2f}")
print(f"Max Drawdown: {mdd:.2%}")
print(f"VaR (95%): {var_95:.2%}")

Position Sizing

# Risk-based sizing: risk 2% per trade
size = qc.risk_per_trade(
    equity=10000,
    risk_percent="0.02",      # 2% risk
    entry_price="45000",
    stop_price="44000",       # $1000 stop distance
)
print(f"Position size: {size}")  # 0.2 BTC

# Percent of equity
size = qc.percent_of_equity(
    equity=10000,
    percent="0.1",            # 10% of equity
    price="45000",
)
print(f"Position size: {size}")  # ~0.022 BTC

# Kelly fraction
kelly = qc.kelly_fraction(
    win_rate=0.55,
    avg_win=100,
    avg_loss=80,
)
print(f"Kelly: {kelly:.2%}")

Serialization

# Save portfolio to JSON file
qc.save_to_file(portfolio, "portfolio.json")

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

# Serialize to JSON string
json_str = qc.to_json(position)

# Deserialize from JSON
position = qc.from_json(json_str, qc.SpotPosition)

Using Organized Namespaces

import qlcore as qc

# Access by namespace
qc.positions.PerpetualPosition
qc.risk.sharpe_ratio(returns)
qc.sizing.kelly_fraction(0.55, 100, 80)
qc.margin.calculate_initial_margin("45000", leverage=10)
qc.fees.calculate_fee("4500", rate="0.001")
qc.pnl.calculate_portfolio_pnl(portfolio, prices)
qc.math.round_price_to_tick("45123.456", "0.01")
qc.time.to_unix_ms(datetime.now())
qc.serialization.to_json(position)

Next Steps