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¶
Method 1: Fill.create() (Recommended)¶
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})