quantlab.acquisition.wrds.taq.WrdsSession#
- class quantlab.acquisition.wrds.taq.WrdsSession(username)[source]#
Bases:
objectA pool of read-only PostgreSQL connections to WRDS, opened on first use.
Constructing a session makes no network call. A query takes an idle connection from the pool or opens a new one, up to
MAX_CONNECTIONS; when every connection is busy the query waits for one. Sequential use therefore opens exactly one connection, andmax_workersdownload threads open at mostmax_workers. Get the session throughshared()rather than constructing one per batch, so the whole run counts against one pool: a WRDS account may hold only a few connections at once. Once the driver reports an error, or a connect fails, the session is broken for the rest of the run and never reconnects; the next run resumes from the pages on disk.The host, port and database are class constants passed directly to
psycopg2.connect, never read from a config or the environment, so no config file can send the credentials to another server. This code never handles the password: libpq reads it from~/.pgpass(or the file named by$PGPASSFILE), and the session checks that the file has a matching entry before it tries to connect.The methods fall into three groups: pure SQL builders that need no connection (
table_identifier,where_clause,copy_query,count_query), general query helpers used by every WRDS product (schema_usable,fetch_rows,copy_csv), and TAQ-specific methods built on those helpers.- Parameters:
- usernamestr
The WRDS username, normally read from
WRDS_USERNAMEbyshared().
Examples
Needs
WRDS_USERNAMEand a matching~/.pgpassline; the first query opens the first connection:session = WrdsSession.shared() days = session.trading_days(2024) WrdsSession.close_shared()
- COPY_SPOOL_BYTES = 268435456#
Up to this many bytes of one COPY result stay in memory before the buffer moves to a temporary file.
- MAX_CONNECTIONS = 6#
Connections one session opens at most. A WRDS account may hold about seven connections at once; one is left free for another client.
- PGPASS_LINE_HINT = 'wrds-pgdata.wharton.upenn.edu:9737:wrds:$WRDS_USERNAME:<password>'#
The
.pgpassline quoted in error messages.<password>is a placeholder; this code never reads the real password.
- REFUSED_ENV = ('PGHOSTADDR', 'PGSERVICE', 'PGSERVICEFILE')#
libpq environment variables that could redirect the connection away from
HOST:PGHOSTADDRoverrides the address the host name resolves to, and a service file can supply host, port and user. If any is set, the session refuses to connect.PGHOSTis harmless because the host is always passed explicitly.
- assert_entitled(years)[source]#
Check that the account may read the TAQ schema of every year given.
Called before the first data query of a download, so a year outside the subscription stops the run before any data is copied, instead of failing every batch of every day.
- Parameters:
- yearsiterable of int
Calendar years.
- Raises:
- WrdsEntitlementError
Naming every
taqm_YYYYschema the account cannot read.
Examples
Needs a live session:
session.assert_entitled(range(2020, 2025))
- close()[source]#
Close every idle connection and refuse further queries.
A connection still in use by another thread is closed when that thread returns it.
Examples
>>> WrdsSession("someone").close()
Close and forget every shared session.
Examples
>>> WrdsSession.close_shared()
- copy_csv(query)[source]#
Run a
COPY ... TO STDOUTstatement and return the CSV bytes.The statement is passed to
copy_expertunchanged, so no SQL text is assembled outsidepsycopg2.sql. Up toCOPY_SPOOL_BYTESstay in memory before the buffer moves to a temporary file.- Parameters:
- querypsycopg2.sql.Composable
A
COPY (...) TO STDOUTstatement.
- Returns:
- bytes
The CSV output.
Examples
Needs a live session:
raw = session.copy_csv( WrdsSession.copy_query(day, [("AAPL", None)], columns) )
- copy_nbbo_bars_csv(query)[source]#
Run a server-side bar resampling statement and return the CSV bytes.
- Parameters:
- queryquantlab.acquisition.wrds.nbbo_bars_sql.NbboBarsQuery
One day table and one symbol batch; its
statement()is run unchanged.
- Returns:
- bytes
The CSV output, starting with a header line.
Examples
Needs a live session:
raw = session.copy_nbbo_bars_csv(query)
- copy_nbbo_csv(day, pairs, columns)[source]#
Run
copy_queryfor one day and batch and return the CSV bytes.The statement is exactly the one
copy_querybuilds, with no ordering or time condition added.- Parameters:
- daydatetime.date
The trading day.
- pairsiterable of tuple of (str, str or None)
(root, suffix)pairs, as forwhere_clause.- columnsiterable of str
Column names to select, in this order.
- Returns:
- bytes
The CSV output, starting with a header line.
Examples
Needs a live session:
day = date(2024, 1, 24) raw = session.copy_nbbo_csv( day, [("AAPL", None)], session.table_columns(day) )
- classmethod copy_query(day, pairs, columns)[source]#
Build the COPY statement for one day table and one symbol batch.
COPY ... TO STDOUTstreams the result as CSV, which is much faster than fetching rows one by one. The statement has no ORDER BY, GROUP BY or DISTINCT and no time condition. The whole day is collected, and before 2018 the order in which the server stores the rows is the only way to order records that share a timestamp.- Parameters:
- daydatetime.date
The trading day.
- pairsiterable of tuple of (str, str or None)
(root, suffix)pairs, as forwhere_clause.- columnsiterable of str
Column names to select, in this order.
- Returns:
- psycopg2.sql.Composed
The complete statement. The CSV output includes a header line.
Examples
>>> query = WrdsSession.copy_query( ... date(2024, 1, 24), [("AAPL", None)], ("date", "time_m", "best_bid") ... )
which renders as:
COPY (SELECT "date", "time_m", "best_bid" FROM "taqm_2024"."complete_nbbo_20240124" WHERE sym_root = ANY(ARRAY['AAPL']) AND (sym_root, coalesce(sym_suffix, '')) IN (('AAPL', ''))) TO STDOUT WITH (FORMAT csv, HEADER true)
- classmethod count_query(day, pairs)[source]#
Build
SELECT count(*)over the same WHERE ascopy_query.Using the same condition guarantees that a count and the download it checks select the same rows.
- Parameters:
- daydatetime.date
The trading day.
- pairsiterable of tuple of (str, str or None)
(root, suffix)pairs, as forwhere_clause.
- Returns:
- psycopg2.sql.Composed
The count statement.
Examples
>>> query = WrdsSession.count_query(date(2024, 1, 24), [("AAPL", None)])
which renders as:
SELECT count(*) FROM "taqm_2024"."complete_nbbo_20240124" WHERE sym_root = ANY(ARRAY['AAPL']) AND (sym_root, coalesce(sym_suffix, '')) IN (('AAPL', ''))
- count_rows(day, pairs)[source]#
Return how many rows
copy_query(day, pairs, ...)would return.- Parameters:
- daydatetime.date
The trading day.
- pairsiterable of tuple of (str, str or None)
(root, suffix)pairs, as forwhere_clause.
- Returns:
- int
The row count.
Examples
Needs a live session:
session.count_rows(date(2024, 1, 24), [("AAPL", None)])
- fetch_rows(query)[source]#
Execute
queryand return every row.Meant for small catalogue and metadata reads. A data download uses
copy_csvinstead, which streams a full day of records without building a Python tuple per row.- Parameters:
- querypsycopg2.sql.Composable or str
The statement to run.
- Returns:
- list of tuple
All result rows.
Examples
Needs a live session:
from psycopg2 import sql rows = session.fetch_rows(sql.SQL("SELECT 1"))
- has_schema_usage(year)[source]#
Return whether this account may read the TAQ schema
taqm_{year}.- Parameters:
- yearint
The calendar year.
- Returns:
- bool
The answer of
schema_usablefor that schema.
Examples
Needs a live session:
entitled = session.has_schema_usage(2024)
- schema_usable(schema)[source]#
Return whether this account may read
schema.Reading a schema requires the
USAGEprivilege. The question is asked through thepg_namespacecatalogue rather than by schema name, because asking by name raises for a schema that does not exist, and a driver error would break the session. A missing schema returns no row and reads asFalse, the same answer as “not subscribed”.- Parameters:
- schemastr
The schema name, passed as a query parameter.
- Returns:
- bool
True if the account has
USAGEon the schema.
Examples
Needs a live session:
usable = session.schema_usable("crsp_a_stock")
Return the process-wide session for the current
WRDS_USERNAME.The variable is read on every call, so a changed username gets its own session rather than the old one.
- Returns:
- WrdsSession
The same object on every call with the same username.
- Raises:
- RuntimeError
If
WRDS_USERNAMEis unset or empty.
Examples
With
WRDS_USERNAMEset (no connection is opened):session = WrdsSession.shared() session is WrdsSession.shared() # True
- table_columns(day)[source]#
Return the day table’s column names in the server’s column order.
- Parameters:
- daydatetime.date
The trading day.
- Returns:
- tuple of str
Column names, sorted by their position in the table.
Examples
Needs a live session:
columns = session.table_columns(date(2024, 1, 24))
- static table_identifier(day)[source]#
Return the
taqm_{YYYY}.complete_nbbo_{YYYYMMDD}table name for a day.- Parameters:
- daydatetime.date
The trading day.
- Returns:
- psycopg2.sql.Identifier
The schema-qualified table name, safely quoted.
Examples
>>> from datetime import date >>> WrdsSession.table_identifier(date(2024, 1, 24)) Identifier('taqm_2024', 'complete_nbbo_20240124')
- trading_days(year)[source]#
Return every day that has a
complete_nbbotable intaqm_{year}.The days are read from the database catalogue (
information_schema) rather than tested one calendar day at a time, so a missing table cannot be mistaken for a holiday.- Parameters:
- yearint
The calendar year.
- Returns:
- list of datetime.date
The trading days, ascending.
Examples
Needs a live session:
days = session.trading_days(2024)
- static where_clause(pairs)[source]#
Build the WHERE condition selecting exact
(sym_root, sym_suffix)pairs.TAQ splits a symbol into a root and a share-class suffix, so
BRK.Bis rootBRKwith suffixB. The first part of the condition,sym_root = ANY(roots), lets the server skip storage blocks that hold none of the roots; the second part picks the exact share classes. Every value is asql.Literal, never text pasted into the SQL.- Parameters:
- pairsiterable of tuple of (str, str or None)
(root, suffix)pairs; aNoneor empty suffix means the plain root.
- Returns:
- psycopg2.sql.Composed
The condition, without the
WHEREkeyword.
- Raises:
- ValueError
If
pairsis empty. Without asym_rootcondition the query would scan a whole day table, which this class never does.
Examples
>>> clause = WrdsSession.where_clause([("AAPL", None), ("BRK", "B")])
which renders as:
sym_root = ANY(ARRAY['AAPL', 'BRK']) AND (sym_root, coalesce(sym_suffix, '')) IN (('AAPL', ''), ('BRK', 'B'))