Skip to main content

XQDB — Python Bindings

XQDB is independent and not affiliated with or endorsed by KX. kdb+ is a trademark of KX.

A Python interface to kdb+/q powered by Narwhals, with support for multiple dataframe backends (PyArrow, pandas, Polars).

Upgrading to 0.1.4

Breaking change. q timestamp atoms are now naive datetime values with tzinfo is None. Version 0.1.3 returned UTC-aware values.

A q timestamp carries no timezone, and Arrow timestamp[ns] columns were already naive, so in 0.1.3 the same q value compared unequal depending on whether it was read as an atom or from a column, and a value read from a column could not be passed back as a query argument. Both now work.

If you need an aware value, attach the zone yourself. Do not assume UTC unless your q process guarantees it — a process using .z.P stores local wall-clock times, and 0.1.3 labelled those UTC.

# 0.1.3
ts = conn.sync("first exec time from trade")   # datetime(..., tzinfo=timezone.utc)

# 0.1.4
ts = conn.sync("first exec time from trade")   # datetime(...)  naive
ts = ts.replace(tzinfo=timezone.utc)           # only if the data really is UTC

Arguments now accept both shapes: a naive datetime keeps its wall clock, and an aware one normalizes through its UTC offset, including zoneinfo zones across DST boundaries. A tzinfo whose utcoffset() returns None counts as naive, per Python's own definition.

Installation

Requirements: Python ≥ 3.10 and < 3.15, Narwhals ≥ 2.10, PyArrow ≥ 20.0.0

Optional backend packages: pandas, polars

Install the published package:

python -m pip install xqdb

To build from source for development with a Rust toolchain:

python -m pip install -e .

Quick Start

import xqdb
import narwhals as nw

# Basic connection (PyArrow backend by default)
conn = xqdb.Q('localhost', 1800)

# Select an installed Narwhals output backend
conn = xqdb.Q('localhost', 1800, backend='polars')

# Authentication credentials require TLS unless the connection is already protected
conn = xqdb.Q(
    'localhost', 1800, user='user', passwd='password', enable_tls=True
)

# With TLS and retry
conn = xqdb.Q('localhost', 1800, enable_tls=True, retries=3, timeout=30)

Connection Parameters

Parameter Type Default Description
host str Hostname of the q process
port int Port of the q process
backend str "pyarrow" Installed Narwhals output backend; tested with "pyarrow", "pandas", and "polars"
user str "" q username; empty unless explicitly supplied
passwd str "" Password
enable_tls bool False Enable TLS with platform certificate verification
retries int 0 Number of retries with exponential backoff
timeout int 0 Connection timeout in seconds (0 = no timeout)
symbol_encoding str "strict" How q text that is not valid UTF-8 is decoded: "strict" fails the response, "lossy" replaces each invalid byte sequence with U+FFFD

q IPC authentication sends credentials in cleartext when TLS is disabled. Enable TLS for credentialed connections unless another trusted transport already protects the socket.

q stores symbols and strings as raw bytes and never validates them, while Arrow string columns must be valid UTF-8. With the default symbol_encoding="strict", a result carrying a stray Latin-1 or binary byte in a symbol, symbol column, string column, char column, or lambda fails as an XqdbError naming the offending value. Opt in to symbol_encoding="lossy" — at construction or later through conn.symbol_encoding = "lossy" — to decode such text with each maximal invalid sequence replaced by "\ufffd", so every other value in the result survives intact. Valid text decodes identically under both policies, and q error messages always surface with replacement characters rather than being hidden behind a decoding failure.

Narwhals DataFrames and Backend Selection

Results from conn.sync() and conn.receive() return Narwhals DataFrames or Series backed by the selected backend. The default is PyArrow; pandas and Polars are supported when their optional packages are installed. An unavailable or unknown backend raises an error—XQDB does not silently substitute another backend.

To extract the underlying native DataFrame:

result = conn.sync("select from trade")  # Narwhals DataFrame
native_df = nw.to_native(result)  # PyArrow, pandas, or Polars Table/DataFrame

Input Constraints

  • Native or Narwhals eager DataFrames and Series are accepted.
  • Lazy frames are rejected rather than collected implicitly.
  • The Python/native boundary uses the Arrow C Stream interface; it does not serialize frames to Arrow IPC bytes.

Temporal range and precision

Python datetime, time, and timedelta values have microsecond precision. q timestamp or timespan atoms with non-zero sub-microsecond nanoseconds raise ValueError instead of being truncated. q date or datetime atoms outside Python's representable range raise OverflowError instead of being clamped.

A q timestamp carries no timezone, so it maps to a naive datetime. Timestamp atoms and Arrow timestamp[ns] columns therefore share the same timezone semantics, and for any value Python's datetime can represent they compare equal. XQDB does not label q values UTC, because a q process may hold local wall-clock times; apply your own zone when you know it.

Query arguments accept both shapes. A naive datetime is used as the q wall clock unchanged. An aware datetime is resolved to UTC by Python, so fixed offsets, zoneinfo zones, and other tzinfo implementations all normalize to the correct instant, including across DST boundaries.

Whole Series and DataFrame round-trips are always nanosecond-exact on every backend, because they cross the boundary as Arrow rather than as Python objects. Single scalars pulled out of a frame are backend-dependent, and the precision is decided by the backend before XQDB sees the value:

Backend Scalar type Sub-microsecond digits
pyarrow (pandas installed) pandas.Timestamp preserved via .nanosecond
pyarrow (no pandas) — PyArrow refuses to convert and raises
pandas pandas.Timestamp preserved via .nanosecond
polars datetime.datetime truncated by Polars before XQDB sees it

pandas.Timestamp keeps its sub-microsecond digits in .nanosecond rather than .microsecond, and XQDB reads that remainder so the full nanosecond reaches q. Polars materializes a plain datetime, so a nanosecond read out as a Polars scalar is already truncated and XQDB cannot recover it. When nanosecond fidelity matters, pass the frame or Series instead of a scalar.

Reading a sub-microsecond value back as an atom still raises ValueError, because datetime cannot represent it — select it as a one-row table instead.

Connect / Disconnect

# explicitly connect (auto-connects on first query)
conn.connect()

# disconnect (auto-disconnects on IO error)
conn.disconnect()

String Query

conn.sync("select from trade where date=last date")

Functional Query

Supports Python basic data types, Narwhals Series/DataFrame, and dict (with string keys).

from datetime import date, time

import pyarrow as pa

symbols = pa.chunked_array(
    [pa.array(["sym0", "sym1"]).dictionary_encode()]
)
conn.sync(
    ".gw.query",
    "table",
    {
        "date": date(2023, 11, 21),
        "syms": symbols,
        "startTime": time(9),
        "endTime": time(11, 30),
    },
)

Operators and Lambdas

Pass q primitives and arbitrary lambdas as first-class arguments:

from xqdb import XqdbQLambda, XqdbQOperator

conn.sync("{[op;a;b] .[op;(a;b)]}", XqdbQOperator.PLUS, 1, 2)
conn.sync("{[op;a;b] .[op;(a;b)]}", XqdbQLambda("{x+y}"), 1, 2)

# A non-root q context can be supplied explicitly.
scoped = XqdbQLambda("{x+y}", "analytics")

XqdbQOperator(name) accepts supported q primitive names such as "+"; it does not expose wire opcodes. XqdbQLambda(source, context="") preserves its source text, requires a brace-delimited UTF-8 body (optionally prefixed with k)), rejects NUL bytes in both fields, and rejects context values beginning with ".". The context "analytics" represents q namespace .analytics because the wire context omits the leading dot. Lambda source is executable q code: construct it only from trusted input.

Send DataFrame

import pyarrow as pa

frame = pa.table({"sym": ["a", "b"], "price": [10.5, 11.0]})
conn.sync("upsert", "table", frame)

Async Query

conn.asyn("upsert", "table", frame)

Subscribe

import pyarrow as pa

tables = pa.chunked_array(
    [pa.array(["table1", "table2"]).dictionary_encode()]
)
symbols = pa.chunked_array(
    [pa.array(["sym1", "sym2"]).dictionary_encode()]
)
conn.sync(".u.sub", tables, "")
conn.sync(".u.sub", tables, symbols)

while True:
    # returns ("upd", "table", Narwhals DataFrame)
    upd = conn.receive()
    print(upd)

Generate IPC Bytes

Serialize data as kdb+ IPC bytes without a connection.

import pyarrow as pa

from xqdb import serialize_as_ipc_bytes6

frame = pa.table({"sym": ["a", "b"], "price": [10.5, 11.0]})

# without compression
buffer = serialize_as_ipc_bytes6("sync", False, ["upd", "table", frame])

# with compression
buffer = serialize_as_ipc_bytes6("sync", True, ["upd", "table", frame])

msg_type: "async" | "sync" | "response"

Read Binary Table

Read a regular q binary table file directly into a Narwhals DataFrame. Select the native output backend independently of the file format, and pass symbol_encoding to decode text that is not valid UTF-8 the same way Q does.

from xqdb import read_binary6

df = read_binary6("/path/to/table.bin", backend="pandas")
df = read_binary6("/path/to/legacy.bin", symbol_encoding="lossy")

Error Handling

from xqdb import XqdbError, XqdbIOError, XqdbAuthError

try:
    conn.sync("select from trade")
except XqdbAuthError:
    print("Authentication failed")
except XqdbIOError:
    print("Connection error")
except XqdbError:
    print("General xqdb error")

Data Type Mapping

Deserialization (q → Python)

q scalars map to Python scalars; q vectors/tables are returned as Narwhals DataFrames/Series backed by the selected backend.

Atom (scalar to Python)

q type n size Python type Note
boolean 1 1 bool
guid 2 16 str
byte 4 1 int
short 5 2 int
int 6 4 int
long 7 8 int
real 8 4 float
float 9 8 float
char 10 1 str
string 10 1 str
symbol 11 * str
timestamp 12 8 datetime naive; no timezone attached
month 13 4 -
date 14 4 date 0001.01.01 - 9999.12.31
datetime 15 8 datetime naive; no timezone attached
timespan 16 8 timedelta
minute 17 4 time 00:00 - 23:59
second 18 4 time 00:00:00 - 23:59:59
time 19 4 time 00:00:00.000 - 23:59:59.999
primitive 101-103 1 XqdbQOperator supported unary/binary/ternary primitive
lambda 100 * XqdbQLambda source and q context

Vector and Table (Arrow-backed Narwhals)

q type PyArrow representation Notes
boolean list bool Native Arrow boolean
byte list uint8 Native Arrow unsigned 8-bit
short list int16 Native Arrow signed 16-bit
int list int32 Native Arrow signed 32-bit
long list int64 Native Arrow signed 64-bit
real list float32 Native Arrow single precision
float list float64 Native Arrow double precision
char/strings string_view Arrow UTF-8 string view
symbol list dictionary-encoded string Preserves q symbol semantics
guid list binary_view Every non-null value is 16 bytes
nested list large_list Child type follows the q list type
timestamp list timestamp[ns] Nanosecond timestamp
date list date32 Days since the Unix epoch
datetime list timestamp[ms] Millisecond timestamp
timespan list duration[ns] Nanosecond duration
minute list time64[ns] Nanosecond time-of-day
second list time64[ns] Nanosecond time-of-day
time list time64[ns] Nanosecond time-of-day
table pyarrow.Table Returned through a Narwhals frame
keyed table pyarrow.Table Key and value columns are combined

Other selected backends receive the equivalent representation that Narwhals can construct from this Arrow stream. Backend-specific dtypes may differ while values and q semantics remain the same.

real/float 0n is mapped to null, not NaN.

short/int/long null and infinity values (0Nh/i/j, 0Wh/i/j, -0Wh/i/j) are mapped to null.

Serialization (Python → q)

Basic Data Type

Python type q type Note
bool boolean
int long
float float
str symbol
bytes string
datetime timestamp naive used as-is; aware resolved to UTC
date date 0001.01.01 - 9999.12.31
timedelta timespan
time time 00:00:00.000 - 23:59:59.999
XqdbQOperator primitive supported primitive name
XqdbQLambda lambda source and q context

Series, DataFrame, and Dictionary

Arrow C Stream dtype q type
boolean boolean list
uint8 byte list
int16 short list
int32 int list
int64 long list
float32 real list
float64 float list
string/string view general list of char vectors
dictionary-encoded string symbol list
16-byte binary values guid list
timestamp timestamp list
date32 date list
duration timespan list
time64 time list
nested numeric list general list of typed q vectors
eager DataFrame table

Dictionary serialization requires str keys.

Resources

License

XQDB is licensed under the BSD-3-Clause permissive open-source license, which permits use in proprietary and commercial applications.

Metadata

Release files for xqdb 0.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xqdb 0.1.7
File Size Uploaded
xqdb-0.1.7.tar.gz 305.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for xqdb 0.1.7
File Interpreter ABI Platform
xqdb-0.1.7-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
xqdb-0.1.7-cp310-abi3-manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64 Details
xqdb-0.1.7-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details

Total release size: 17.2 MB

Release files / xqdb-0.1.7.tar.gz

Download URL xqdb-0.1.7.tar.gz
Size 305.3 kB
Tags Source
SHA-256 checksum
How to use checksums
1b2a4be09bbfef12a0cd4b04d556a6d4c9fc2d9d8470ce55f8bebef837379d77
BLAKE2b-256 checksum
How to use checksums
6e02fc6c867243dc52a0dc2f8f8159d4df4ddf03af16e80fc8fed8932a6edd25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release files / xqdb-0.1.7-cp310-abi3-win_amd64.whl

Download URL xqdb-0.1.7-cp310-abi3-win_amd64.whl
Size 6.2 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
0e5c19b6e21d56800f167d90d5107c174c0b0fd7249b084900110d783289d280
BLAKE2b-256 checksum
How to use checksums
a3ca0a7de5564c3387b9d1c50146bcf048da143362f6e4e183cfb1ac62e320e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release files / xqdb-0.1.7-cp310-abi3-manylinux_2_28_x86_64.whl

Download URL xqdb-0.1.7-cp310-abi3-manylinux_2_28_x86_64.whl
Size 5.8 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
41a1655791ece08f11b86126469c750a92e2c5f5136edd6e11949285275af037
BLAKE2b-256 checksum
How to use checksums
b8066d55fe66955ed63510bd8410bb543a39598896b29db6aacebece7f64172a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release files / xqdb-0.1.7-cp310-abi3-macosx_11_0_arm64.whl

Download URL xqdb-0.1.7-cp310-abi3-macosx_11_0_arm64.whl
Size 5.0 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
59a27fd95138e33136b37b5ead12c556d2ed20e0676513db393d3c16130861e5
BLAKE2b-256 checksum
How to use checksums
d04ab77406ebb6f1374dbe9091b95d888bf92f62990f8084ea713ba2d96ddf90
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.8

4 release files

This release

0.1.7 This release

4 release files

0.1.3

4 release files

0.1.2

4 release files

0.1.1

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page