Skip to content

Events & Fills

Events represent things that happen to positions: trades, funding payments, liquidations, and settlements.

Fill Events

A Fill represents a trade execution.

Creating Fills

Keyword-only arguments prevent positional mistakes:

import qlcore as qc
from decimal import Decimal

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: FillBuilder (Fluent API)

fill = (
    qc.FillBuilder()
    .order("order-001")
    .instrument("BTC-USDT-PERP")
    .buy("0.1")
    .at_price("45000")
    .with_fee("4.5")
    .at_time(1701849600000)
    .build()
)

Builder methods:

Method Description
.order(id) Set order ID
.instrument(id) Set instrument ID
.buy(qty) Set as buy with quantity
.sell(qty) Set as sell with quantity
.at_price(price) Set fill price
.with_fee(fee) Set fee amount
.at_time(ms) Set timestamp in milliseconds

Method 3: From Dictionary

data = {
    "order_id": "order-001",
    "instrument_id": "BTC-USDT-PERP",
    "side": "BUY",
    "quantity": "0.1",
    "price": "45000",
    "fee": "4.5",
    "timestamp_ms": 1701849600000,
}

fill = qc.Fill.from_dict(data)

Method 4: From Exchange Payload

Convert raw exchange data directly:

# Binance-style payload
raw = {
    "id": "12345",
    "symbol": "BTCUSDT",
    "side": "BUY",
    "qty": "0.1",
    "price": "45000",
    "commission": "4.5",
    "time": 1701849600000,
}

fill = qc.Fill.from_exchange_fill("binance", raw, market="perp")

Supported key aliases:

Field Accepted Keys
order_id id, fill_id, trade_id, order_id
instrument_id symbol, instrument, instrument_id
quantity qty, size, quantity
fee fee, commission
timestamp_ms ts, timestamp, time, timestamp_ms

Fill Validation

# Single fill (validated on creation)
try:
    fill = qc.Fill.create(
        order_id="",  # ❌ Empty ID
        ...
    )
except qc.InvalidFillError as e:
    print(f"Invalid fill: {e}")

# Validate sequence
fills = [fill1, fill2, fill3]
validated = qc.validate_fill_sequence(fills)  # Checks order, timestamps

Funding Events

For perpetual positions, track funding payments:

funding = qc.FundingEvent(
    instrument_id="BTC-USDT-PERP",
    funding_rate=Decimal("0.0001"),  # 0.01%
    timestamp_ms=1701878400000,
)

# Calculate funding payment
payment = qc.calculate_funding_payment(
    position_size=Decimal("1.0"),
    mark_price=Decimal("45000"),
    funding_rate=Decimal("0.0001"),
)
print(f"Funding payment: ${payment}")  # $4.50

Liquidation Events

When a position is forcibly closed:

liquidation = qc.LiquidationEvent(
    instrument_id="BTC-USDT-PERP",
    quantity=Decimal("0.1"),
    price=Decimal("40000"),
    timestamp_ms=1701900000000,
)

Calculate liquidation prices:

# Isolated margin
liq_price = qc.calculate_isolated_liquidation_price(
    entry_price=Decimal("45000"),
    position_side=qc.PositionSide.LONG,
    leverage=10,
    maintenance_margin_rate=Decimal("0.005"),
)

# Cross margin
liq_price = qc.calculate_cross_liquidation_price(
    entry_price=Decimal("45000"),
    position_side=qc.PositionSide.LONG,
    position_size=Decimal("1.0"),
    account_balance=Decimal("5000"),
    maintenance_margin_rate=Decimal("0.005"),
)

Settlement Events

For futures at expiration:

settlement = qc.SettlementEvent(
    instrument_id="BTC-USD-MAR25",
    settlement_price=Decimal("47000"),
    timestamp_ms=1711929600000,
)

# Settle a futures position
settled_position = futures_position.settle(
    settlement_price=settlement.settlement_price
)

Fee Events

Track fee charges:

fee_event = qc.FeeEvent(
    instrument_id="BTC-USDT-PERP",
    amount=Decimal("4.5"),
    fee_type="TRADING",
    timestamp_ms=1701849600000,
)

Event Hooks

Subscribe to events with the hook system:

import qlcore as qc

# Subscribe to events
@qc.on("fill")
def handle_fill(fill):
    print(f"Fill received: {fill.instrument_id}")

@qc.on("funding")
def handle_funding(event):
    print(f"Funding: {event.funding_rate}")

# Emit events
qc.emit("fill", fill)
qc.emit("funding", funding_event)

# Unsubscribe
qc.off("fill", handle_fill)

Event Bus

For isolated event handling:

bus = qc.EventBus()

@bus.on("trade")
def on_trade(data):
    print(f"Trade: {data}")

bus.emit("trade", {"price": 45000})

Serialization

# Fill to dict
data = fill.to_dict()

# Fill to JSON
json_str = qc.serialize_fill(fill)
fill = qc.deserialize_fill(json_str)