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¶
- Core Concepts — Understand immutability and design philosophy
- Positions Guide — Deep dive into position types
- Cookbook — Common patterns and recipes